├── .travis.yml ├── SaneNetScanner ├── en.lproj │ ├── InfoPlist.strings │ └── Credits.rtf ├── SaneNetScanner-Prefix.pch ├── Log.h ├── CSSequentialDataProvider.h ├── CSSaneOptionEnumConstraint.h ├── CSSaneOptionRangeConstraint.h ├── CSSaneOptionConstraint.h ├── DeviceMatchingInfo.plist ├── CSSaneOption.h ├── CSSaneOptionConstraint.m ├── SaneNetScanner-Info.plist ├── CSSaneOptionRangeConstraint.m ├── CSSaneNetScanner.h ├── CSSaneOptionEnumConstraint.m ├── ICD_Trampolins.h ├── CSSequentialDataProvider.m ├── main.m ├── ICD_Trampolins.m ├── LoggerCommon.h ├── CSSaneOption.m ├── LoggerClient.h └── CSSaneNetScanner.m ├── sane ├── libsane-net.a └── sane │ ├── sane.h │ └── saneopts.h ├── .gitignore ├── README.md ├── scripts └── publish-sane.rb └── SaneNetScanner.xcodeproj ├── xcshareddata └── xcschemes │ └── SaneNetScanner.xcscheme └── project.pbxproj /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | -------------------------------------------------------------------------------- /SaneNetScanner/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /sane/libsane-net.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kleinweby/SaneNetScanner/HEAD/sane/libsane-net.a -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | build/* 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | *.xcworkspace 12 | !default.xcworkspace 13 | xcuserdata 14 | profile 15 | *.moved-aside 16 | -------------------------------------------------------------------------------- /SaneNetScanner/SaneNetScanner-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SaneNetScanner' target in the 'SaneNetScanner' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | 9 | #import 10 | #include "Log.h" 11 | -------------------------------------------------------------------------------- /SaneNetScanner/Log.h: -------------------------------------------------------------------------------- 1 | // 2 | // Log.h 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 05.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #include "LoggerClient.h" 10 | 11 | #ifdef DEBUG 12 | 13 | #define Log(FMT, ARGS...) LogMessageCompat(FMT, ##ARGS) 14 | 15 | #else 16 | 17 | #define Log(FMT, ARGS...) 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /SaneNetScanner/CSSequentialDataProvider.h: -------------------------------------------------------------------------------- 1 | // 2 | // CSSequentialDataProvider.h 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 07.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CSSequentialDataProvider : NSObject 12 | 13 | + (CGDataProviderRef) createDataProviderWithFileAtURL:(NSURL*)url 14 | andHardOffset:(NSUInteger)hardoffset; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SaneNetScanner/CSSaneOptionEnumConstraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // CSSaneOptionEnumConstraint.h 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 06.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import "CSSaneOptionConstraint.h" 10 | 11 | @interface CSSaneOptionEnumConstraint : CSSaneOptionConstraint 12 | 13 | - (id) initWithDescriptor:(const SANE_Option_Descriptor*)descriptor; 14 | 15 | @property (nonatomic, copy, readonly) NSArray* values; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /SaneNetScanner/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /SaneNetScanner/CSSaneOptionRangeConstraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // CSSaneOptionRangeConstraint.h 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 06.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import "CSSaneOptionConstraint.h" 10 | 11 | @interface CSSaneOptionRangeConstraint : CSSaneOptionConstraint 12 | 13 | - (id) initWithDescriptor:(const SANE_Option_Descriptor*)descriptor; 14 | 15 | @property (nonatomic, copy, readonly) NSNumber* minValue; 16 | @property (nonatomic, copy, readonly) NSNumber* maxValue; 17 | @property (nonatomic, copy, readonly) NSNumber* step; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /SaneNetScanner/CSSaneOptionConstraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // CSSaneOptionConstraint.h 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 06.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import 10 | #include "sane/sane.h" 11 | 12 | typedef enum { 13 | CSSaneRangeConstraint, 14 | CSSaneEnumConstraint 15 | } CSSaneConstraintType; 16 | 17 | @interface CSSaneOptionConstraint : NSObject 18 | 19 | + (id) constraintWithDescriptor:(const SANE_Option_Descriptor*)descriptor; 20 | 21 | @property (nonatomic, readonly) CSSaneConstraintType type; 22 | 23 | - (void) addToDeviceDictionary:(NSMutableDictionary*)dict; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /SaneNetScanner/DeviceMatchingInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BonjourNetwork 6 | 7 | _sane-scanner._tcp. 8 | 9 | 10 | device events 11 | 12 | scan 13 | 14 | device type 15 | scanner 16 | ICABonjourTXTRecordKey 17 | 18 | saned 19 | YES 20 | 21 | 22 | 23 | 24 | Version 25 | 1.0 26 | 27 | 28 | -------------------------------------------------------------------------------- /SaneNetScanner/CSSaneOption.h: -------------------------------------------------------------------------------- 1 | // 2 | // CSSaneOption.h 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 06.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import 10 | #include "sane/sane.h" 11 | #import "CSSaneOptionConstraint.h" 12 | 13 | extern NSString* kSaneScanResolution; 14 | extern NSString* kSaneScanMode; 15 | extern NSString* kSanePreview; 16 | extern NSString* kSaneTopLeftX; 17 | extern NSString* kSaneTopLeftY; 18 | extern NSString* kSaneBottomRightX; 19 | extern NSString* kSaneBottomRightY; 20 | 21 | @interface CSSaneOption : NSObject 22 | 23 | + (NSDictionary*) saneOptionsForHandle:(SANE_Handle)handle; 24 | 25 | @property (nonatomic, copy, readonly) NSString* name; 26 | 27 | // The Value may be an string or an number 28 | // Note: setting it will talk to the device 29 | // getting it may do also so 30 | @property (nonatomic, copy) id value; 31 | 32 | @property (nonatomic, strong, readonly) CSSaneOptionConstraint* constraint; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /SaneNetScanner/CSSaneOptionConstraint.m: -------------------------------------------------------------------------------- 1 | // 2 | // CSSaneOptionConstraint.m 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 06.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import "CSSaneOptionConstraint.h" 10 | #import "CSSaneOptionRangeConstraint.h" 11 | #import "CSSaneOptionEnumConstraint.h" 12 | 13 | @implementation CSSaneOptionConstraint 14 | 15 | + (id) constraintWithDescriptor:(const SANE_Option_Descriptor*)descriptor 16 | { 17 | if (descriptor->constraint_type == SANE_CONSTRAINT_RANGE) 18 | return [[CSSaneOptionRangeConstraint alloc] initWithDescriptor:descriptor]; 19 | else if (descriptor->constraint_type == SANE_CONSTRAINT_STRING_LIST || 20 | descriptor->constraint_type == SANE_CONSTRAINT_WORD_LIST) 21 | return [[CSSaneOptionEnumConstraint alloc] initWithDescriptor:descriptor]; 22 | 23 | return nil; 24 | } 25 | 26 | - (void) addToDeviceDictionary:(NSMutableDictionary*)dict 27 | { 28 | NSAssert(false, @"Not implemented"); 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SaneNetScanner [![Build Status](https://travis-ci.org/chrspeich/SaneNetScanner.png?branch=develop)](https://travis-ci.org/chrspeich/SaneNetScanner]) 2 | ============== 3 | 4 | A Mac OS X Scanner Driver for sane net devices 5 | 6 | Client-Usage 7 | ------------ 8 | 9 | 1. Compile SaneNetScanner 10 | 2. Copy `SaneNetScanner.app` to `/Library/Image Capture/Devices` 11 | 3. Use `Image Capture`, `Preview` or any other compatible application to scan. 12 | 13 | Server-Usage 14 | ------------ 15 | 16 | 1. Install, configure and start saned 17 | 2. Install ruby-bindings for `dnssd` and `dbus` 18 | 3. Run `scripts/publish-sane.rb` 19 | 20 | FAQ 21 | --- 22 | 23 | 1. **Q:** What is the state of the Project? 24 | 25 | **A:** It's working. The OSX-Client/Driver is quite stable and supports most functionally used by the os. The publish service is very rough and sometimes stops working (and saned sometimes, too). Development is currently stalled as all features I want are present. Feel free to send pull requests or contact me. 26 | 2. **Q:** Why do we need `publish-sane.rb`? Saned already announces itself via bonjour! 27 | 28 | **A:** Yeah, saned already anncounes itself, but OSX needs to know about indivudal scanners in order to show it to the user. Therefore the extra script is needed. 29 | -------------------------------------------------------------------------------- /SaneNetScanner/SaneNetScanner-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | de.christian-speich.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 0.2.1 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 5 25 | LSBackgroundOnly 26 | 27 | LSMinimumSystemVersion 28 | ${MACOSX_DEPLOYMENT_TARGET} 29 | NSHumanReadableCopyright 30 | Copyright © 2012 Christian Speich. All rights reserved. 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /scripts/publish-sane.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'dnssd' 4 | require 'dbus' 5 | 6 | class ScannerService 7 | attr_accessor :name, :device 8 | 9 | def initialize(name, device) 10 | @name = name 11 | @device = device 12 | 13 | @dns_service = nil 14 | end 15 | 16 | def announce! 17 | txt = DNSSD::TextRecord.new 'saned' => 'YES', 'ty' => @name, 'name' => @name, 'deviceName' => @device, 'txtvers' => 1 18 | 19 | puts "Announce '#{@name}' at '#{@device}'" 20 | @dns_service = DNSSD.register @name, "_sane-scanner._tcp", nil, 6566, txt 21 | end 22 | 23 | def unannounce! 24 | @dns_serivce.stop 25 | end 26 | end 27 | 28 | class SaneScannerPublisher 29 | attr_accessor :scanners 30 | 31 | def initialize 32 | @scanners = [] 33 | end 34 | 35 | def refresh_scanners 36 | puts 'Refreshing scanners' 37 | output = `scanimage -f "%d\t%v %m%n"` 38 | 39 | @scanners.each do |s| 40 | s.unannounce! 41 | end 42 | 43 | @scanners = [] 44 | 45 | output.each_line do |s| 46 | device, name = s.split(/\t/) 47 | 48 | s = ScannerService.new name.strip, device.strip 49 | s.announce! 50 | 51 | @scanners << s 52 | end 53 | end 54 | end 55 | 56 | publisher = SaneScannerPublisher.new 57 | bus = DBus::SystemBus.instance 58 | 59 | hal_service = bus.service "org.freedesktop.Hal" 60 | manager = hal_service.object "/org/freedesktop/Hal/Manager" 61 | manager.default_iface = 'org.freedesktop.Hal.Manager' 62 | manager.introspect 63 | 64 | manager.on_signal "DeviceAdded" do 65 | publisher.refresh_scanners 66 | end 67 | 68 | manager.on_signal "DeviceRemoved" do 69 | publisher.refresh_scanners 70 | end 71 | 72 | publisher.refresh_scanners 73 | loop = DBus::Main.new 74 | loop << bus 75 | loop.run 76 | 77 | 78 | -------------------------------------------------------------------------------- /SaneNetScanner/CSSaneOptionRangeConstraint.m: -------------------------------------------------------------------------------- 1 | // 2 | // CSSaneOptionRangeConstraint.m 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 06.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import "CSSaneOptionRangeConstraint.h" 10 | 11 | @interface CSSaneOptionRangeConstraint () 12 | 13 | @property (nonatomic, copy) NSNumber* minValue; 14 | @property (nonatomic, copy) NSNumber* maxValue; 15 | @property (nonatomic, copy) NSNumber* step; 16 | 17 | @end 18 | 19 | @implementation CSSaneOptionRangeConstraint 20 | 21 | - (id) initWithDescriptor:(const SANE_Option_Descriptor*)descriptor 22 | { 23 | self = [super init]; 24 | if (self) { 25 | assert(descriptor->constraint_type == SANE_CONSTRAINT_RANGE); 26 | assert(descriptor->type == SANE_TYPE_INT || 27 | descriptor->type == SANE_TYPE_FIXED); 28 | 29 | if (descriptor->type == SANE_TYPE_INT) { 30 | self.minValue = @(descriptor->constraint.range->min); 31 | self.maxValue = @(descriptor->constraint.range->max); 32 | self.step = @(descriptor->constraint.range->quant); 33 | } 34 | else { 35 | self.minValue = @SANE_UNFIX(descriptor->constraint.range->min); 36 | self.maxValue = @SANE_UNFIX(descriptor->constraint.range->max); 37 | self.step = @SANE_UNFIX(descriptor->constraint.range->quant); 38 | } 39 | } 40 | return self; 41 | } 42 | 43 | - (CSSaneConstraintType) type 44 | { 45 | return CSSaneRangeConstraint; 46 | } 47 | 48 | - (NSString *)description { 49 | return [NSString stringWithFormat:@"<%@:%p> (min=%@, max=%@, step=%@)", NSStringFromClass([self class]), self, self.minValue, self.maxValue, self.step]; 50 | } 51 | 52 | - (void) addToDeviceDictionary:(NSMutableDictionary*)dict 53 | { 54 | dict[@"type"] = @"TWON_RANGE"; 55 | dict[@"min"] = self.minValue; 56 | dict[@"max"] = self.maxValue; 57 | dict[@"stepSize"] = self.step; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /SaneNetScanner/CSSaneNetScanner.h: -------------------------------------------------------------------------------- 1 | // 2 | // CSSaneNetScanner.h 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 05.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CSSaneNetScanner : NSObject 12 | 13 | // 14 | // This is used for TCPIP opening 15 | // 16 | // Example: 17 | // { 18 | // ICABonjourDeviceLocationKey = "In the magic ether"; 19 | // ICABonjourServiceNameKey = "Virtual Scanner Bonjour"; 20 | // ICABonjourServiceTypeKey = "_scanner._tcp."; 21 | // ICABonjourTXTRecordKey = { 22 | // mdl = <56697274 75616c20 5363616e 6e6572>; 23 | // mfg = <4170706c 65>; 24 | // note = <496e2074 6865206d 61676963 20657468 6572>; 25 | // scannerAvailable = <31>; 26 | // txtvers = <31>; 27 | // ty = <4170706c 65205669 72747561 6c205363 616e6e65 72>; 28 | // }; 29 | // ICADeviceBrowserDeviceRefKey = 1; 30 | // UUIDString = "1D279211-1D27-9211-1D27-92111D279211"; 31 | // deviceModulePath = "/System/Library/Image Capture/Devices/VirtualScanner.app"; 32 | // deviceModuleVersion = 16809984; 33 | // deviceType = scanner; 34 | // hostGUID = "C933C548-A19F-4084-BF09-528438D6D581"; 35 | // hostName = "Baskarans-MBP-UniMP"; 36 | // ipAddress = "192.168.2.9"; 37 | // "ipAddress_v6" = "fe80::3615:9eff:fe8a:9f9c"; 38 | // ipGUID = ""; 39 | // ipPort = 9500; 40 | // "ipPort_v6" = 9500; 41 | // name = "Apple Virtual Scanner"; 42 | // persistentIDString = "1D279211-1D27-9211-1D27-92111D279211"; 43 | // transportType = "TCP/IP"; 44 | // } 45 | // 46 | - (id) initWithParameters:(NSDictionary*)params; 47 | 48 | @property (nonatomic, strong, readonly) NSString* prettyName; 49 | 50 | @property (nonatomic, assign) ScannerObjectInfo* scannerObjectInfo; 51 | 52 | // ICD Callbacks 53 | - (ICAError) openSession:(ICD_ScannerOpenSessionPB*)params; 54 | - (ICAError) closeSession:(ICD_ScannerCloseSessionPB*)params; 55 | 56 | - (ICAError) addPropertiesToDictitonary:(NSMutableDictionary*)dict; 57 | 58 | - (ICAError) getParameters:(ICD_ScannerGetParametersPB*)params; 59 | - (ICAError) setParameters:(ICD_ScannerSetParametersPB*)params; 60 | 61 | - (ICAError) status:(ICD_ScannerStatusPB*)params; 62 | - (ICAError) start:(ICD_ScannerStartPB*)params; 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /SaneNetScanner/CSSaneOptionEnumConstraint.m: -------------------------------------------------------------------------------- 1 | // 2 | // CSSaneOptionEnumConstraint.m 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 06.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import "CSSaneOptionEnumConstraint.h" 10 | 11 | @interface CSSaneOptionEnumConstraint () 12 | 13 | @property (nonatomic, copy) NSArray* values; 14 | 15 | @end 16 | 17 | @implementation CSSaneOptionEnumConstraint 18 | 19 | - (id) initWithDescriptor:(const SANE_Option_Descriptor*)descriptor 20 | { 21 | self = [super init]; 22 | if (self) { 23 | assert(descriptor->constraint_type == SANE_CONSTRAINT_STRING_LIST || 24 | descriptor->constraint_type == SANE_CONSTRAINT_WORD_LIST); 25 | 26 | if (descriptor->constraint_type == SANE_CONSTRAINT_WORD_LIST) { 27 | SANE_Word length = descriptor->constraint.word_list[0]; 28 | NSMutableArray* values = [NSMutableArray arrayWithCapacity:length]; 29 | 30 | for (SANE_Word i = 1; i < length + 1; i++) { 31 | if (descriptor->type == SANE_TYPE_FIXED) { 32 | [values addObject:@SANE_UNFIX(descriptor->constraint.word_list[i])]; 33 | } 34 | else if (descriptor->type == SANE_TYPE_INT) { 35 | [values addObject:@(descriptor->constraint.word_list[i])]; 36 | } 37 | else { 38 | assert(false); 39 | } 40 | } 41 | 42 | self.values = values; 43 | } 44 | else { 45 | NSMutableArray* values = [NSMutableArray array]; 46 | const char* const * ptr = descriptor->constraint.string_list; 47 | 48 | while (*ptr != NULL) { 49 | const char* const str = *ptr; 50 | 51 | [values addObject:@(str)]; 52 | 53 | ptr++; 54 | } 55 | 56 | self.values = values; 57 | } 58 | } 59 | return self; 60 | } 61 | 62 | - (CSSaneConstraintType) type 63 | { 64 | return CSSaneEnumConstraint; 65 | } 66 | 67 | - (NSString *)description { 68 | return [NSString stringWithFormat:@"<%@:%p> %@", NSStringFromClass([self class]), self, self.values]; 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /SaneNetScanner/ICD_Trampolins.h: -------------------------------------------------------------------------------- 1 | // 2 | // ICD_Trampolins.h 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 05.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #ifdef DEBUG 10 | // This USB scanner is only ussed for debugging, when run inside Xcode 11 | ICAError ICD_ScannerOpenUSBDevice(UInt32 locationID, 12 | ScannerObjectInfo* newDeviceObjectInfo); 13 | #endif 14 | 15 | ICAError ICD_ScannerOpenTCPIPDevice(CFDictionaryRef params, 16 | ScannerObjectInfo* newDeviceObjectInfo); 17 | 18 | ICAError ICD_ScannerCloseDevice(ScannerObjectInfo* deviceObjectInfo); 19 | ICAError ICD_ScannerCleanup(ScannerObjectInfo* objectInfo); 20 | 21 | //ICAError ICD_ScannerPeriodicTask(ScannerObjectInfo* deviceObjectInfo); 22 | 23 | //ICAError ICD_ScannerGetObjectInfo(const ScannerObjectInfo* parentInfo, 24 | // UInt32 index, 25 | // ScannerObjectInfo* newInfo); 26 | 27 | ICAError ICD_ScannerReadFileData(const ScannerObjectInfo* objectInfo, 28 | UInt32 dataType, 29 | Ptr buffer, 30 | UInt32 offset, 31 | UInt32* length); 32 | 33 | //ICAError ICD_ScannerSendMessage(const ScannerObjectInfo* objectInfo, 34 | // ICD_ScannerObjectSendMessagePB* pb, 35 | // ICDCompletion completion); 36 | 37 | ICAError ICD_ScannerAddPropertiesToCFDictionary(ScannerObjectInfo* objectInfo, 38 | CFMutableDictionaryRef dict); 39 | 40 | ICAError ICD_ScannerOpenSession(const ScannerObjectInfo* deviceObjectInfo, 41 | ICD_ScannerOpenSessionPB* pb); 42 | ICAError ICD_ScannerCloseSession(const ScannerObjectInfo* deviceObjectInfo, 43 | ICD_ScannerCloseSessionPB* pb); 44 | 45 | ICAError ICD_ScannerGetParameters(const ScannerObjectInfo* deviceObjectInfo, 46 | ICD_ScannerGetParametersPB* pb); 47 | 48 | ICAError ICD_ScannerSetParameters(const ScannerObjectInfo* deviceObjectInfo, 49 | ICD_ScannerSetParametersPB* pb); 50 | 51 | ICAError ICD_ScannerStatus(const ScannerObjectInfo* deviceObjectInfo, 52 | ICD_ScannerStatusPB* pb); 53 | ICAError ICD_ScannerStart(const ScannerObjectInfo* deviceObjectInfo, 54 | ICD_ScannerStartPB* pb); 55 | -------------------------------------------------------------------------------- /SaneNetScanner/CSSequentialDataProvider.m: -------------------------------------------------------------------------------- 1 | // 2 | // CSSequentialDataProvider.m 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 07.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import "CSSequentialDataProvider.h" 10 | 11 | @interface CSSequentialDataProvider () 12 | 13 | - (size_t) getBytes:(void*)buffer 14 | ofSize:(size_t)size; 15 | - (off_t) skipBytes:(size_t)size; 16 | - (void) rewind; 17 | 18 | @property (nonatomic) FILE* fileHandle; 19 | @property (nonatomic) NSUInteger hardOffset; 20 | 21 | @end 22 | 23 | static size_t __getBytes(void *info, void *buffer, size_t size) 24 | { 25 | CSSequentialDataProvider* provider = (__bridge CSSequentialDataProvider*)info; 26 | 27 | return [provider getBytes:buffer ofSize:size]; 28 | } 29 | 30 | static off_t __skipBytes(void *info, off_t size) 31 | { 32 | CSSequentialDataProvider* provider = (__bridge CSSequentialDataProvider*)info; 33 | 34 | return [provider skipBytes:size]; 35 | } 36 | 37 | static void __rewind(void *info) 38 | { 39 | CSSequentialDataProvider* provider = (__bridge CSSequentialDataProvider*)info; 40 | 41 | [provider rewind]; 42 | } 43 | 44 | static void __releaseProvider(void *info) 45 | { 46 | CSSequentialDataProvider* provider = CFBridgingRelease(info); 47 | #pragma unused(provider) 48 | } 49 | 50 | static CGDataProviderSequentialCallbacks callbacks = {0, __getBytes, __skipBytes, __rewind, __releaseProvider}; 51 | 52 | @implementation CSSequentialDataProvider 53 | 54 | + (CGDataProviderRef) createDataProviderWithFileAtURL:(NSURL*)url 55 | andHardOffset:(NSUInteger)hardoffset 56 | { 57 | CSSequentialDataProvider* callbackObject = [[self alloc] initWithURL:url andHardOffset:hardoffset]; 58 | 59 | return CGDataProviderCreateSequential((void*)CFBridgingRetain(callbackObject), &callbacks); 60 | } 61 | 62 | - (id)initWithURL:(NSURL*)url andHardOffset:(NSUInteger)hardOffset 63 | { 64 | self = [super init]; 65 | if (self) { 66 | self.fileHandle = fopen([[url path] fileSystemRepresentation], "r"); 67 | self.hardOffset = hardOffset; 68 | [self rewind]; 69 | } 70 | return self; 71 | } 72 | 73 | - (size_t) getBytes:(void*)buffer 74 | ofSize:(size_t)size 75 | { 76 | return fread(buffer, 1, size, self.fileHandle); 77 | } 78 | 79 | - (off_t) skipBytes:(size_t)size 80 | { 81 | return fseek(self.fileHandle, size, SEEK_CUR); 82 | } 83 | 84 | - (void) rewind 85 | { 86 | fseek(self.fileHandle, self.hardOffset, SEEK_SET); 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /SaneNetScanner/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 05.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ICD_Trampolins.h" 11 | #import "sane/sane.h" 12 | 13 | void AuthCallBack(SANE_String_Const resource, 14 | SANE_Char *username, 15 | SANE_Char *password) { 16 | username = ""; 17 | password = ""; 18 | } 19 | 20 | int main(int argc, char *argv[]) 21 | { 22 | int status = 0; 23 | SANE_Status saneStatus; 24 | 25 | saneStatus = sane_init(NULL, &AuthCallBack); 26 | 27 | if (saneStatus != SANE_STATUS_GOOD) { 28 | NSLog(@"Sane init failed"); 29 | return -1; 30 | } 31 | 32 | #ifdef DEBUG 33 | gICDScannerCallbackFunctions.f_ICD_ScannerOpenUSBDevice = ICD_ScannerOpenUSBDevice; 34 | #endif 35 | 36 | gICDScannerCallbackFunctions.f_ICD_ScannerOpenFireWireDeviceWithIORegPath = NULL; 37 | 38 | gICDScannerCallbackFunctions.f_ICD_ScannerOpenTCPIPDevice = ICD_ScannerOpenTCPIPDevice; 39 | gICDScannerCallbackFunctions.f_ICD_ScannerCloseDevice = ICD_ScannerCloseDevice; 40 | gICDScannerCallbackFunctions.f_ICD_ScannerPeriodicTask = NULL; 41 | gICDScannerCallbackFunctions.f_ICD_ScannerGetObjectInfo = NULL; 42 | gICDScannerCallbackFunctions.f_ICD_ScannerCleanup = ICD_ScannerCleanup; 43 | gICDScannerCallbackFunctions.f_ICD_ScannerGetPropertyData = NULL; // Unused 44 | gICDScannerCallbackFunctions.f_ICD_ScannerSetPropertyData = NULL; // Unused 45 | gICDScannerCallbackFunctions.f_ICD_ScannerReadFileData = ICD_ScannerReadFileData; 46 | gICDScannerCallbackFunctions.f_ICD_ScannerWriteDataToFile = NULL; // Unused 47 | gICDScannerCallbackFunctions.f_ICD_ScannerWriteFileData = NULL; // Unused 48 | gICDScannerCallbackFunctions.f_ICD_ScannerSendMessage = NULL; 49 | gICDScannerCallbackFunctions.f_ICD_ScannerAddPropertiesToCFDictionary = ICD_ScannerAddPropertiesToCFDictionary; 50 | 51 | gICDScannerCallbackFunctions.f_ICD_ScannerOpenSession = ICD_ScannerOpenSession; 52 | gICDScannerCallbackFunctions.f_ICD_ScannerCloseSession = ICD_ScannerCloseSession; 53 | gICDScannerCallbackFunctions.f_ICD_ScannerInitialize = NULL; // Unused 54 | gICDScannerCallbackFunctions.f_ICD_ScannerGetParameters = ICD_ScannerGetParameters; 55 | gICDScannerCallbackFunctions.f_ICD_ScannerSetParameters = ICD_ScannerSetParameters; 56 | gICDScannerCallbackFunctions.f_ICD_ScannerStatus = ICD_ScannerStatus; 57 | gICDScannerCallbackFunctions.f_ICD_ScannerStart = ICD_ScannerStart; 58 | 59 | status = ICD_ScannerMain(argc, (const char **)argv); 60 | 61 | sane_exit(); 62 | 63 | return status; 64 | } 65 | -------------------------------------------------------------------------------- /SaneNetScanner.xcodeproj/xcshareddata/xcschemes/SaneNetScanner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /SaneNetScanner/ICD_Trampolins.m: -------------------------------------------------------------------------------- 1 | // 2 | // ICD_Trampolins.c 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 05.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #include 10 | #include "ICD_Trampolins.h" 11 | #import "CSSaneNetScanner.h" 12 | 13 | #ifdef DEBUG 14 | 15 | const UInt32 fakeLocation = 0xDEADBEEF; 16 | 17 | // This USB scanner is only ussed for debugging, when run inside Xcode 18 | ICAError ICD_ScannerOpenUSBDevice(UInt32 locationID, 19 | ScannerObjectInfo* newDeviceObjectInfo) 20 | { 21 | ICAError err = paramErr; 22 | 23 | // If the location id is zero we're launched by xcode (debug) 24 | if (locationID == 0) 25 | { 26 | err = ICDConnectUSBDevice(fakeLocation); 27 | } 28 | // This is the fake device we just connected 29 | else if (locationID == fakeLocation) { 30 | return ICD_ScannerOpenTCPIPDevice((__bridge CFDictionaryRef)(@{ 31 | (NSString*)kICABonjourServiceNameKey: @"Mustek 1200 CU", 32 | (NSString*)kICABonjourServiceTypeKey: @"_sane-scanner._tcp", 33 | (NSString*)kICABonjourTXTRecordKey: @{ 34 | @"saned": [@"YES" dataUsingEncoding:NSUTF8StringEncoding], 35 | @"ty": [@"Mustek 1200 CU" dataUsingEncoding:NSUTF8StringEncoding], 36 | @"name": [@"Mustek 1200 CU" dataUsingEncoding:NSUTF8StringEncoding], 37 | @"deviceName": [@"mustek_usb:libusb:001:008" dataUsingEncoding:NSUTF8StringEncoding], 38 | }, 39 | (NSString*)kICADeviceBrowserDeviceRefKey: @1, 40 | @"UUIDString": @"1D279211-1D27-9211-1D27-92111D279211", 41 | @"ipAddress": @"10.0.1.5", 42 | @"ipPort": @9500, 43 | @"name": @"Mustek 1200 CU", 44 | @"transportType": @"TCP/IP" 45 | }), 46 | newDeviceObjectInfo); 47 | } 48 | 49 | return err; 50 | } 51 | 52 | #endif 53 | 54 | ICAError ICD_ScannerOpenTCPIPDevice(CFDictionaryRef params, 55 | ScannerObjectInfo* newDeviceObjectInfo) 56 | { 57 | CSSaneNetScanner* scanner; 58 | 59 | // Create the scanner 60 | scanner = [[CSSaneNetScanner alloc] initWithParameters:(__bridge NSDictionary *)(params)]; 61 | 62 | if (!scanner) 63 | return paramErr; 64 | 65 | // Populate the scanner structure 66 | newDeviceObjectInfo->privateData = (Ptr)CFBridgingRetain(scanner); 67 | scanner.scannerObjectInfo = newDeviceObjectInfo; 68 | newDeviceObjectInfo->flags = 0; 69 | // All scanner clients are based on ImageCaptureCore framework, which dynamically determines 70 | // size of thumbnails. So, we do not need to know the exact size of the thumbnail. Set this 71 | // to 1 if we have a thumbnail. 72 | newDeviceObjectInfo->thumbnailSize = 1; 73 | 74 | newDeviceObjectInfo->dataSize = 0; 75 | 76 | // Obvious, right? 77 | newDeviceObjectInfo->icaObjectInfo.objectType = kICADevice; 78 | newDeviceObjectInfo->icaObjectInfo.objectSubtype = kICADeviceScanner; 79 | 80 | // Set the device name 81 | // TODO: set the device name 82 | strlcpy((char*)newDeviceObjectInfo->name, [scanner.prettyName UTF8String], sizeof(newDeviceObjectInfo->name)); 83 | 84 | // Now set the creation date 85 | NSDateFormatter* df = [[NSDateFormatter alloc] initWithDateFormat:@"%Y:%m:%d %H:%M:%S" allowNaturalLanguage:YES]; 86 | NSDate* d = [NSDate date]; 87 | NSString* ds = [df stringFromDate:d]; 88 | 89 | if (ds) 90 | strlcpy((char*)(newDeviceObjectInfo->creationDate), 91 | [ds UTF8String], 92 | sizeof(newDeviceObjectInfo->creationDate)); 93 | else 94 | strlcpy((char*)(newDeviceObjectInfo->creationDate), 95 | "0000:00:00 00:00:00", 96 | sizeof(newDeviceObjectInfo->creationDate) ); 97 | 98 | return noErr; 99 | } 100 | 101 | ICAError ICD_ScannerCloseDevice(ScannerObjectInfo* deviceObjectInfo) 102 | { 103 | CSSaneNetScanner* scanner = CFBridgingRelease(deviceObjectInfo->privateData); 104 | #pragma unused(scanner) 105 | return noErr; 106 | } 107 | 108 | ICAError ICD_ScannerCleanup(ScannerObjectInfo* objectInfo) 109 | { 110 | return noErr; 111 | } 112 | 113 | ICAError ICD_ScannerReadFileData(const ScannerObjectInfo* objectInfo, 114 | UInt32 dataType, 115 | Ptr buffer, 116 | UInt32 offset, 117 | UInt32* length) 118 | { 119 | assert(false); 120 | } 121 | 122 | ICAError ICD_ScannerAddPropertiesToCFDictionary(ScannerObjectInfo* objectInfo, 123 | CFMutableDictionaryRef dict) 124 | { 125 | CSSaneNetScanner* scanner = (__bridge CSSaneNetScanner*)(void*)(objectInfo->privateData); 126 | 127 | return [scanner addPropertiesToDictitonary:(__bridge NSMutableDictionary*)dict]; 128 | } 129 | 130 | ICAError ICD_ScannerOpenSession(const ScannerObjectInfo* deviceObjectInfo, 131 | ICD_ScannerOpenSessionPB* pb) 132 | { 133 | CSSaneNetScanner* scanner = (__bridge CSSaneNetScanner*)(void*)(deviceObjectInfo->privateData); 134 | 135 | return [scanner openSession:pb]; 136 | } 137 | 138 | ICAError ICD_ScannerCloseSession(const ScannerObjectInfo* deviceObjectInfo, 139 | ICD_ScannerCloseSessionPB* pb) 140 | { 141 | CSSaneNetScanner* scanner = (__bridge CSSaneNetScanner*)(void*)(deviceObjectInfo->privateData); 142 | 143 | return [scanner closeSession:pb]; 144 | } 145 | 146 | ICAError ICD_ScannerGetParameters(const ScannerObjectInfo* deviceObjectInfo, 147 | ICD_ScannerGetParametersPB* pb) 148 | { 149 | CSSaneNetScanner* scanner = (__bridge CSSaneNetScanner*)(void*)(deviceObjectInfo->privateData); 150 | 151 | return [scanner getParameters:pb]; 152 | } 153 | 154 | ICAError ICD_ScannerSetParameters(const ScannerObjectInfo* deviceObjectInfo, 155 | ICD_ScannerSetParametersPB* pb) 156 | { 157 | CSSaneNetScanner* scanner = (__bridge CSSaneNetScanner*)(void*)(deviceObjectInfo->privateData); 158 | 159 | return [scanner setParameters:pb]; 160 | } 161 | 162 | ICAError ICD_ScannerStatus(const ScannerObjectInfo* deviceObjectInfo, 163 | ICD_ScannerStatusPB* pb) 164 | { 165 | CSSaneNetScanner* scanner = (__bridge CSSaneNetScanner*)(void*)(deviceObjectInfo->privateData); 166 | 167 | return [scanner status:pb]; 168 | } 169 | 170 | ICAError ICD_ScannerStart(const ScannerObjectInfo* deviceObjectInfo, 171 | ICD_ScannerStartPB* pb) 172 | { 173 | CSSaneNetScanner* scanner = (__bridge CSSaneNetScanner*)(void*)(deviceObjectInfo->privateData); 174 | 175 | return [scanner start:pb]; 176 | } 177 | -------------------------------------------------------------------------------- /SaneNetScanner/LoggerCommon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LoggerCommon.h 3 | * 4 | * version 1.1 2012-03-31 5 | * 6 | * Definitions common to NSLogger Viewer and NSLoggerClient 7 | * for the binary messages format 8 | * https://github.com/fpillet/NSLogger 9 | * 10 | * BSD license follows (http://www.opensource.org/licenses/bsd-license.php) 11 | * 12 | * Copyright (c) 2010-2012 Florent Pillet All Rights Reserved. 13 | * 14 | * Redistribution and use in source and binary forms, with or without modification, 15 | * are permitted provided that the following conditions are met: 16 | * 17 | * Redistributions of source code must retain the above copyright notice, 18 | * this list of conditions and the following disclaimer. Redistributions in 19 | * binary form must reproduce the above copyright notice, this list of 20 | * conditions and the following disclaimer in the documentation and/or other 21 | * materials provided with the distribution. Neither the name of Florent 22 | * Pillet nor the names of its contributors may be used to endorse or promote 23 | * products derived from this software without specific prior written 24 | * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 25 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT 26 | * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 27 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 30 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 31 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 34 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | * 36 | */ 37 | 38 | /* NSLogger native binary message format: 39 | * Each message is a dictionary encoded in a compact format. All values are stored 40 | * in network order (big endian). A message is made of several "parts", which are 41 | * typed chunks of data, each with a specific purpose (partKey), data type (partType) 42 | * and data size (partSize). 43 | * 44 | * uint32_t totalSize (total size for the whole message) 45 | * uint16_t partCount (number of parts below) 46 | * [repeat partCount times]: 47 | * uint8_t partKey the part key 48 | * uint8_t partType (string, binary, image, int16, int32, int64) 49 | * uint32_t partSize (only for string, binary and image types, others are implicit) 50 | * .. `partSize' data bytes 51 | * 52 | * Complete message is usually made of: 53 | * - a PART_KEY_MESSAGE_TYPE (mandatory) which contains one of the LOGMSG_TYPE_* values 54 | * - a PART_KEY_TIMESTAMP_S (mandatory) which is the timestamp returned by gettimeofday() (seconds from 01.01.1970 00:00) 55 | * - a PART_KEY_TIMESTAMP_MS (optional) complement of the timestamp seconds, in milliseconds 56 | * - a PART_KEY_TIMESTAMP_US (optional) complement of the timestamp seconds and milliseconds, in microseconds 57 | * - a PART_KEY_THREAD_ID (mandatory) the ID of the user thread that produced the log entry 58 | * - a PART_KEY_TAG (optional) a tag that helps categorizing and filtering logs from your application, and shows up in viewer logs 59 | * - a PART_KEY_LEVEL (optional) a log level that helps filtering logs from your application (see as few or as much detail as you need) 60 | * - a PART_KEY_MESSAGE which is the message text, binary data or image 61 | * - a PART_KEY_MESSAGE_SEQ which is the message sequence number (message# sent by client) 62 | * - a PART_KEY_FILENAME (optional) with the filename from which the log was generated 63 | * - a PART_KEY_LINENUMBER (optional) the linenumber in the filename at which the log was generated 64 | * - a PART_KEY_FUNCTIONNAME (optional) the function / method / selector from which the log was generated 65 | * - if logging an image, PART_KEY_IMAGE_WIDTH and PART_KEY_IMAGE_HEIGHT let the desktop know the image size without having to actually decode it 66 | */ 67 | 68 | // Constants for the "part key" field 69 | #define PART_KEY_MESSAGE_TYPE 0 70 | #define PART_KEY_TIMESTAMP_S 1 // "seconds" component of timestamp 71 | #define PART_KEY_TIMESTAMP_MS 2 // milliseconds component of timestamp (optional, mutually exclusive with PART_KEY_TIMESTAMP_US) 72 | #define PART_KEY_TIMESTAMP_US 3 // microseconds component of timestamp (optional, mutually exclusive with PART_KEY_TIMESTAMP_MS) 73 | #define PART_KEY_THREAD_ID 4 74 | #define PART_KEY_TAG 5 75 | #define PART_KEY_LEVEL 6 76 | #define PART_KEY_MESSAGE 7 77 | #define PART_KEY_IMAGE_WIDTH 8 // messages containing an image should also contain a part with the image size 78 | #define PART_KEY_IMAGE_HEIGHT 9 // (this is mainly for the desktop viewer to compute the cell size without having to immediately decode the image) 79 | #define PART_KEY_MESSAGE_SEQ 10 // the sequential number of this message which indicates the order in which messages are generated 80 | #define PART_KEY_FILENAME 11 // when logging, message can contain a file name 81 | #define PART_KEY_LINENUMBER 12 // as well as a line number 82 | #define PART_KEY_FUNCTIONNAME 13 // and a function or method name 83 | 84 | // Constants for parts in LOGMSG_TYPE_CLIENTINFO 85 | #define PART_KEY_CLIENT_NAME 20 86 | #define PART_KEY_CLIENT_VERSION 21 87 | #define PART_KEY_OS_NAME 22 88 | #define PART_KEY_OS_VERSION 23 89 | #define PART_KEY_CLIENT_MODEL 24 // For iPhone, device model (i.e 'iPhone', 'iPad', etc) 90 | #define PART_KEY_UNIQUEID 25 // for remote device identification, part of LOGMSG_TYPE_CLIENTINFO 91 | 92 | // Area starting at which you may define your own constants 93 | #define PART_KEY_USER_DEFINED 100 94 | 95 | // Constants for the "partType" field 96 | #define PART_TYPE_STRING 0 // Strings are stored as UTF-8 data 97 | #define PART_TYPE_BINARY 1 // A block of binary data 98 | #define PART_TYPE_INT16 2 99 | #define PART_TYPE_INT32 3 100 | #define PART_TYPE_INT64 4 101 | #define PART_TYPE_IMAGE 5 // An image, stored in PNG format 102 | 103 | // Data values for the PART_KEY_MESSAGE_TYPE parts 104 | #define LOGMSG_TYPE_LOG 0 // A standard log message 105 | #define LOGMSG_TYPE_BLOCKSTART 1 // The start of a "block" (a group of log entries) 106 | #define LOGMSG_TYPE_BLOCKEND 2 // The end of the last started "block" 107 | #define LOGMSG_TYPE_CLIENTINFO 3 // Information about the client app 108 | #define LOGMSG_TYPE_DISCONNECT 4 // Pseudo-message on the desktop side to identify client disconnects 109 | #define LOGMSG_TYPE_MARK 5 // Pseudo-message that defines a "mark" that users can place in the log flow 110 | 111 | // Default Bonjour service identifiers 112 | #define LOGGER_SERVICE_TYPE_SSL CFSTR("_nslogger-ssl._tcp") 113 | #define LOGGER_SERVICE_TYPE CFSTR("_nslogger._tcp") 114 | -------------------------------------------------------------------------------- /sane/sane/sane.h: -------------------------------------------------------------------------------- 1 | /* sane - Scanner Access Now Easy. 2 | Copyright (C) 1997-1999 David Mosberger-Tang and Andreas Beck 3 | This file is part of the SANE package. 4 | 5 | This file is in the public domain. You may use and modify it as 6 | you see fit, as long as this copyright message is included and 7 | that there is an indication as to what modifications have been 8 | made (if any). 9 | 10 | SANE is distributed in the hope that it will be useful, but WITHOUT 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | FITNESS FOR A PARTICULAR PURPOSE. 13 | 14 | This file declares SANE application interface. See the SANE 15 | standard for a detailed explanation of the interface. */ 16 | #ifndef sane_h 17 | #define sane_h 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | /* 24 | * SANE types and defines 25 | */ 26 | 27 | #define SANE_CURRENT_MAJOR 1 28 | #define SANE_CURRENT_MINOR 0 29 | 30 | #define SANE_VERSION_CODE(major, minor, build) \ 31 | ( (((SANE_Word) (major) & 0xff) << 24) \ 32 | | (((SANE_Word) (minor) & 0xff) << 16) \ 33 | | (((SANE_Word) (build) & 0xffff) << 0)) 34 | 35 | #define SANE_VERSION_MAJOR(code) ((((SANE_Word)(code)) >> 24) & 0xff) 36 | #define SANE_VERSION_MINOR(code) ((((SANE_Word)(code)) >> 16) & 0xff) 37 | #define SANE_VERSION_BUILD(code) ((((SANE_Word)(code)) >> 0) & 0xffff) 38 | 39 | #define SANE_FALSE 0 40 | #define SANE_TRUE 1 41 | 42 | typedef unsigned char SANE_Byte; 43 | typedef int SANE_Word; 44 | typedef SANE_Word SANE_Bool; 45 | typedef SANE_Word SANE_Int; 46 | typedef char SANE_Char; 47 | typedef SANE_Char *SANE_String; 48 | typedef const SANE_Char *SANE_String_Const; 49 | typedef void *SANE_Handle; 50 | typedef SANE_Word SANE_Fixed; 51 | 52 | #define SANE_FIXED_SCALE_SHIFT 16 53 | #define SANE_FIX(v) ((SANE_Word) ((v) * (1 << SANE_FIXED_SCALE_SHIFT))) 54 | #define SANE_UNFIX(v) ((double)(v) / (1 << SANE_FIXED_SCALE_SHIFT)) 55 | 56 | typedef enum 57 | { 58 | SANE_STATUS_GOOD = 0, /* everything A-OK */ 59 | SANE_STATUS_UNSUPPORTED, /* operation is not supported */ 60 | SANE_STATUS_CANCELLED, /* operation was cancelled */ 61 | SANE_STATUS_DEVICE_BUSY, /* device is busy; try again later */ 62 | SANE_STATUS_INVAL, /* data is invalid (includes no dev at open) */ 63 | SANE_STATUS_EOF, /* no more data available (end-of-file) */ 64 | SANE_STATUS_JAMMED, /* document feeder jammed */ 65 | SANE_STATUS_NO_DOCS, /* document feeder out of documents */ 66 | SANE_STATUS_COVER_OPEN, /* scanner cover is open */ 67 | SANE_STATUS_IO_ERROR, /* error during device I/O */ 68 | SANE_STATUS_NO_MEM, /* out of memory */ 69 | SANE_STATUS_ACCESS_DENIED /* access to resource has been denied */ 70 | } 71 | SANE_Status; 72 | 73 | /* following are for later sane version, older frontends wont support */ 74 | #if 0 75 | #define SANE_STATUS_WARMING_UP 12 /* lamp not ready, please retry */ 76 | #define SANE_STATUS_HW_LOCKED 13 /* scanner mechanism locked for transport */ 77 | #endif 78 | 79 | typedef enum 80 | { 81 | SANE_TYPE_BOOL = 0, 82 | SANE_TYPE_INT, 83 | SANE_TYPE_FIXED, 84 | SANE_TYPE_STRING, 85 | SANE_TYPE_BUTTON, 86 | SANE_TYPE_GROUP 87 | } 88 | SANE_Value_Type; 89 | 90 | typedef enum 91 | { 92 | SANE_UNIT_NONE = 0, /* the value is unit-less (e.g., # of scans) */ 93 | SANE_UNIT_PIXEL, /* value is number of pixels */ 94 | SANE_UNIT_BIT, /* value is number of bits */ 95 | SANE_UNIT_MM, /* value is millimeters */ 96 | SANE_UNIT_DPI, /* value is resolution in dots/inch */ 97 | SANE_UNIT_PERCENT, /* value is a percentage */ 98 | SANE_UNIT_MICROSECOND /* value is micro seconds */ 99 | } 100 | SANE_Unit; 101 | 102 | typedef struct 103 | { 104 | SANE_String_Const name; /* unique device name */ 105 | SANE_String_Const vendor; /* device vendor string */ 106 | SANE_String_Const model; /* device model name */ 107 | SANE_String_Const type; /* device type (e.g., "flatbed scanner") */ 108 | } 109 | SANE_Device; 110 | 111 | #define SANE_CAP_SOFT_SELECT (1 << 0) 112 | #define SANE_CAP_HARD_SELECT (1 << 1) 113 | #define SANE_CAP_SOFT_DETECT (1 << 2) 114 | #define SANE_CAP_EMULATED (1 << 3) 115 | #define SANE_CAP_AUTOMATIC (1 << 4) 116 | #define SANE_CAP_INACTIVE (1 << 5) 117 | #define SANE_CAP_ADVANCED (1 << 6) 118 | 119 | #define SANE_OPTION_IS_ACTIVE(cap) (((cap) & SANE_CAP_INACTIVE) == 0) 120 | #define SANE_OPTION_IS_SETTABLE(cap) (((cap) & SANE_CAP_SOFT_SELECT) != 0) 121 | 122 | #define SANE_INFO_INEXACT (1 << 0) 123 | #define SANE_INFO_RELOAD_OPTIONS (1 << 1) 124 | #define SANE_INFO_RELOAD_PARAMS (1 << 2) 125 | 126 | typedef enum 127 | { 128 | SANE_CONSTRAINT_NONE = 0, 129 | SANE_CONSTRAINT_RANGE, 130 | SANE_CONSTRAINT_WORD_LIST, 131 | SANE_CONSTRAINT_STRING_LIST 132 | } 133 | SANE_Constraint_Type; 134 | 135 | typedef struct 136 | { 137 | SANE_Word min; /* minimum (element) value */ 138 | SANE_Word max; /* maximum (element) value */ 139 | SANE_Word quant; /* quantization value (0 if none) */ 140 | } 141 | SANE_Range; 142 | 143 | typedef struct 144 | { 145 | SANE_String_Const name; /* name of this option (command-line name) */ 146 | SANE_String_Const title; /* title of this option (single-line) */ 147 | SANE_String_Const desc; /* description of this option (multi-line) */ 148 | SANE_Value_Type type; /* how are values interpreted? */ 149 | SANE_Unit unit; /* what is the (physical) unit? */ 150 | SANE_Int size; 151 | SANE_Int cap; /* capabilities */ 152 | 153 | SANE_Constraint_Type constraint_type; 154 | union 155 | { 156 | const SANE_String_Const *string_list; /* NULL-terminated list */ 157 | const SANE_Word *word_list; /* first element is list-length */ 158 | const SANE_Range *range; 159 | } 160 | constraint; 161 | } 162 | SANE_Option_Descriptor; 163 | 164 | typedef enum 165 | { 166 | SANE_ACTION_GET_VALUE = 0, 167 | SANE_ACTION_SET_VALUE, 168 | SANE_ACTION_SET_AUTO 169 | } 170 | SANE_Action; 171 | 172 | typedef enum 173 | { 174 | SANE_FRAME_GRAY, /* band covering human visual range */ 175 | SANE_FRAME_RGB, /* pixel-interleaved red/green/blue bands */ 176 | SANE_FRAME_RED, /* red band only */ 177 | SANE_FRAME_GREEN, /* green band only */ 178 | SANE_FRAME_BLUE /* blue band only */ 179 | } 180 | SANE_Frame; 181 | 182 | /* push remaining types down to match existing backends */ 183 | /* these are to be exposed in a later version of SANE */ 184 | /* most front-ends will require updates to understand them */ 185 | #if 0 186 | #define SANE_FRAME_TEXT 0x0A /* backend specific textual data */ 187 | #define SANE_FRAME_JPEG 0x0B /* complete baseline JPEG file */ 188 | #define SANE_FRAME_G31D 0x0C /* CCITT Group 3 1-D Compressed (MH) */ 189 | #define SANE_FRAME_G32D 0x0D /* CCITT Group 3 2-D Compressed (MR) */ 190 | #define SANE_FRAME_G42D 0x0E /* CCITT Group 4 2-D Compressed (MMR) */ 191 | 192 | #define SANE_FRAME_IR 0x0F /* bare infrared channel */ 193 | #define SANE_FRAME_RGBI 0x10 /* red+green+blue+infrared */ 194 | #define SANE_FRAME_GRAYI 0x11 /* gray+infrared */ 195 | #define SANE_FRAME_XML 0x12 /* undefined schema */ 196 | #endif 197 | 198 | typedef struct 199 | { 200 | SANE_Frame format; 201 | SANE_Bool last_frame; 202 | SANE_Int bytes_per_line; 203 | SANE_Int pixels_per_line; 204 | SANE_Int lines; 205 | SANE_Int depth; 206 | } 207 | SANE_Parameters; 208 | 209 | struct SANE_Auth_Data; 210 | 211 | #define SANE_MAX_USERNAME_LEN 128 212 | #define SANE_MAX_PASSWORD_LEN 128 213 | 214 | typedef void (*SANE_Auth_Callback) (SANE_String_Const resource, 215 | SANE_Char *username, 216 | SANE_Char *password); 217 | 218 | extern SANE_Status sane_init (SANE_Int * version_code, 219 | SANE_Auth_Callback authorize); 220 | extern void sane_exit (void); 221 | extern SANE_Status sane_get_devices (const SANE_Device *** device_list, 222 | SANE_Bool local_only); 223 | extern SANE_Status sane_open (SANE_String_Const devicename, 224 | SANE_Handle * handle); 225 | extern void sane_close (SANE_Handle handle); 226 | extern const SANE_Option_Descriptor * 227 | sane_get_option_descriptor (SANE_Handle handle, SANE_Int option); 228 | extern SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, 229 | SANE_Action action, void *value, 230 | SANE_Int * info); 231 | extern SANE_Status sane_get_parameters (SANE_Handle handle, 232 | SANE_Parameters * params); 233 | extern SANE_Status sane_start (SANE_Handle handle); 234 | extern SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data, 235 | SANE_Int max_length, SANE_Int * length); 236 | extern void sane_cancel (SANE_Handle handle); 237 | extern SANE_Status sane_set_io_mode (SANE_Handle handle, 238 | SANE_Bool non_blocking); 239 | extern SANE_Status sane_get_select_fd (SANE_Handle handle, 240 | SANE_Int * fd); 241 | extern SANE_String_Const sane_strstatus (SANE_Status status); 242 | 243 | #ifdef __cplusplus 244 | } 245 | #endif 246 | 247 | 248 | #endif /* sane_h */ 249 | -------------------------------------------------------------------------------- /SaneNetScanner/CSSaneOption.m: -------------------------------------------------------------------------------- 1 | // 2 | // CSSaneOption.m 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 06.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import "CSSaneOptionConstraint.h" 10 | #import "CSSaneOption.h" 11 | #include "sane/saneopts.h" 12 | 13 | NSString* kSaneScanResolution = (NSString*)CFSTR(SANE_NAME_SCAN_RESOLUTION); 14 | NSString* kSaneScanMode = (NSString*)CFSTR(SANE_NAME_SCAN_MODE); 15 | NSString* kSanePreview = (NSString*)CFSTR(SANE_NAME_PREVIEW); 16 | NSString* kSaneTopLeftX = (NSString*)CFSTR(SANE_NAME_SCAN_TL_X); 17 | NSString* kSaneTopLeftY = (NSString*)CFSTR(SANE_NAME_SCAN_TL_Y); 18 | NSString* kSaneBottomRightX = (NSString*)CFSTR(SANE_NAME_SCAN_BR_X); 19 | NSString* kSaneBottomRightY = (NSString*)CFSTR(SANE_NAME_SCAN_BR_Y); 20 | 21 | @interface CSSaneOption () 22 | 23 | @property (nonatomic, copy) NSString* name; 24 | 25 | @property (nonatomic, strong) CSSaneOptionConstraint* constraint; 26 | 27 | @property (nonatomic) SANE_Handle saneHandle; 28 | @property (nonatomic) SANE_Int saneOptionNumber; 29 | 30 | @property (nonatomic) const SANE_Option_Descriptor* descriptor; 31 | 32 | - (void) _fetchValue; 33 | - (void) _setValue; 34 | 35 | @end 36 | 37 | @implementation CSSaneOption 38 | 39 | @synthesize value = _value; 40 | 41 | + (NSDictionary*) saneOptionsForHandle:(SANE_Handle)handle 42 | { 43 | NSParameterAssert(handle != 0); 44 | SANE_Int number; 45 | NSMutableDictionary* options = [NSMutableDictionary dictionary]; 46 | 47 | for (number = 0;; number++) { 48 | const SANE_Option_Descriptor* descriptor; 49 | CSSaneOption* option; 50 | 51 | descriptor = sane_get_option_descriptor(handle, number); 52 | 53 | // Last descriptor 54 | if (descriptor == NULL) 55 | break; 56 | 57 | // Discard group 58 | if (descriptor->type == SANE_TYPE_GROUP) 59 | continue; 60 | 61 | option = [[self alloc] initWithHandle:handle 62 | number:number 63 | andDescriptor:descriptor]; 64 | 65 | NSAssert(option, @"Could not create sane options wrapper"); 66 | NSAssert(options[option.name] == nil, @"Option with that name already exists"); 67 | 68 | options[option.name] = option; 69 | } 70 | 71 | return [options copy]; 72 | } 73 | 74 | - (id)initWithHandle:(SANE_Handle)handle 75 | number:(SANE_Int)number 76 | andDescriptor:(const SANE_Option_Descriptor*)descriptor 77 | { 78 | NSParameterAssert(handle != 0); 79 | NSParameterAssert(descriptor != NULL); 80 | self = [super init]; 81 | if (self) { 82 | self.name = @(descriptor->name); 83 | 84 | self.descriptor = descriptor; 85 | self.saneHandle = handle; 86 | self.saneOptionNumber = number; 87 | 88 | [self _fetchValue]; 89 | 90 | self.constraint = [CSSaneOptionConstraint constraintWithDescriptor:self.descriptor]; 91 | } 92 | return self; 93 | } 94 | 95 | - (void) setValue:(id)value 96 | { 97 | if ([value isEqual:_value]) 98 | return; 99 | 100 | _value = value; 101 | [self _setValue]; 102 | } 103 | 104 | - (void) _fetchValue 105 | { 106 | if (self.descriptor->type == SANE_TYPE_FIXED) { 107 | SANE_Fixed *values; 108 | SANE_Status status; 109 | 110 | values = malloc(self.descriptor->size); 111 | 112 | assert(values); 113 | 114 | status = sane_control_option(self.saneHandle, 115 | self.saneOptionNumber, 116 | SANE_ACTION_GET_VALUE, 117 | values, 0); 118 | 119 | if (status != SANE_STATUS_GOOD) { 120 | Log(@"Failed get %@: %s", self, sane_strstatus(status)); 121 | return; 122 | } 123 | 124 | // Only one value 125 | if (self.descriptor->size == sizeof(SANE_Int)) { 126 | _value = @SANE_UNFIX(values[0]); 127 | } 128 | // Multiple values 129 | else { 130 | NSUInteger numberOfValues = self.descriptor->size/sizeof(SANE_Int); 131 | NSMutableArray* valueArray = [NSMutableArray arrayWithCapacity:numberOfValues]; 132 | 133 | for (NSUInteger i = 0; i < numberOfValues; i++) { 134 | [valueArray addObject: 135 | @SANE_UNFIX(values[i])]; 136 | } 137 | 138 | _value = valueArray; 139 | } 140 | 141 | free(values); 142 | } 143 | else if (self.descriptor->type == SANE_TYPE_INT) { 144 | SANE_Int *values; 145 | SANE_Status status; 146 | 147 | values = malloc(self.descriptor->size); 148 | 149 | assert(values); 150 | 151 | status = sane_control_option(self.saneHandle, 152 | self.saneOptionNumber, 153 | SANE_ACTION_GET_VALUE, 154 | values, 0); 155 | 156 | if (status != SANE_STATUS_GOOD) { 157 | Log(@"Failed get %@: %s", self, sane_strstatus(status)); 158 | return; 159 | } 160 | 161 | // Only one value 162 | if (self.descriptor->size == sizeof(SANE_Int)) { 163 | _value = @(values[0]); 164 | } 165 | // Multiple values 166 | else { 167 | NSUInteger numberOfValues = self.descriptor->size/sizeof(SANE_Int); 168 | NSMutableArray* valueArray = [NSMutableArray arrayWithCapacity:numberOfValues]; 169 | 170 | for (NSUInteger i = 0; i < numberOfValues; i++) { 171 | [valueArray addObject: 172 | @(values[i])]; 173 | } 174 | 175 | _value = valueArray; 176 | } 177 | 178 | free(values); 179 | } 180 | else if (self.descriptor->type == SANE_TYPE_STRING) { 181 | SANE_String value; 182 | SANE_Status status; 183 | 184 | value = malloc(self.descriptor->size); 185 | 186 | assert(value); 187 | 188 | status = sane_control_option(self.saneHandle, 189 | self.saneOptionNumber, 190 | SANE_ACTION_GET_VALUE, 191 | value, 0); 192 | 193 | if (status != SANE_STATUS_GOOD) { 194 | Log(@"Failed get %@: %s", self, sane_strstatus(status)); 195 | return; 196 | } 197 | 198 | _value = @(value); 199 | free(value); 200 | } 201 | else if (self.descriptor->type == SANE_TYPE_BOOL) { 202 | SANE_Bool* values; 203 | SANE_Status status; 204 | 205 | values = malloc(self.descriptor->size); 206 | 207 | assert(values); 208 | 209 | status = sane_control_option(self.saneHandle, 210 | self.saneOptionNumber, 211 | SANE_ACTION_GET_VALUE, 212 | values, 0); 213 | 214 | if (status != SANE_STATUS_GOOD) { 215 | Log(@"Failed get %@: %s", self, sane_strstatus(status)); 216 | return; 217 | } 218 | 219 | // Only one value 220 | if (self.descriptor->size == sizeof(SANE_Bool)) { 221 | _value = @(values[0]); 222 | } 223 | // Multiple values 224 | else { 225 | NSUInteger numberOfValues = self.descriptor->size/sizeof(SANE_Int); 226 | NSMutableArray* valueArray = [NSMutableArray arrayWithCapacity:numberOfValues]; 227 | 228 | for (NSUInteger i = 0; i < numberOfValues; i++) { 229 | [valueArray addObject: 230 | @(values[i])]; 231 | } 232 | 233 | _value = valueArray; 234 | } 235 | 236 | free(values); 237 | } 238 | else { 239 | Log(@"Unsupported type %@", self); 240 | } 241 | } 242 | 243 | - (void) _setValue 244 | { 245 | if (self.descriptor->type == SANE_TYPE_STRING) { 246 | SANE_String str; 247 | SANE_Status status; 248 | NSString* string = self.value; 249 | 250 | if (![string isKindOfClass:[NSString class]]) { 251 | Log(@"%@ requires string but is %@", self, self.value); 252 | return; 253 | } 254 | 255 | str = malloc(self.descriptor->size); 256 | strncpy(str, [string UTF8String], self.descriptor->size); 257 | 258 | Log(@"Set \"%@\" to \"%@\"", self.name, self.value); 259 | status = sane_control_option(self.saneHandle, 260 | self.saneOptionNumber, 261 | SANE_ACTION_SET_VALUE, 262 | str, 263 | 0); 264 | 265 | if (status != SANE_STATUS_GOOD) { 266 | Log(@"Set failed %@: %s", self, sane_strstatus(status)); 267 | return; 268 | } 269 | free(str); 270 | } 271 | else if (self.descriptor->type == SANE_TYPE_FIXED) { 272 | if (self.descriptor->size != sizeof(SANE_Fixed)) { 273 | Log(@"Dont support multi-size fixed type set yet."); 274 | return; 275 | } 276 | 277 | SANE_Fixed value = SANE_FIX([self.value doubleValue]); 278 | SANE_Status status; 279 | 280 | Log(@"Set \"%@\" to \"%@\"", self.name, self.value); 281 | status = sane_control_option(self.saneHandle, 282 | self.saneOptionNumber, 283 | SANE_ACTION_SET_VALUE, 284 | &value, 285 | 0); 286 | 287 | if (status != SANE_STATUS_GOOD) { 288 | Log(@"Set failed %@: %s", self, sane_strstatus(status)); 289 | return; 290 | } 291 | } 292 | else if (self.descriptor->type == SANE_TYPE_INT) { 293 | if (self.descriptor->size != sizeof(SANE_Int)) { 294 | Log(@"Dont support multi-size int type set yet."); 295 | return; 296 | } 297 | 298 | SANE_Int value = [self.value intValue]; 299 | SANE_Status status; 300 | 301 | Log(@"Set \"%@\" to \"%@\"", self.name, self.value); 302 | status = sane_control_option(self.saneHandle, 303 | self.saneOptionNumber, 304 | SANE_ACTION_SET_VALUE, 305 | &value, 306 | 0); 307 | 308 | if (status != SANE_STATUS_GOOD) { 309 | Log(@"Set failed %@: %s", self, sane_strstatus(status)); 310 | return; 311 | } 312 | } 313 | else if (self.descriptor->type == SANE_TYPE_BOOL) { 314 | if (self.descriptor->size != sizeof(SANE_Bool)) { 315 | Log(@"Dont support multi-size bool type set yet."); 316 | return; 317 | } 318 | 319 | SANE_Bool value = [self.value intValue]; 320 | SANE_Status status; 321 | 322 | Log(@"Set \"%@\" to \"%@\"", self.name, value ? @"true" : @"false"); 323 | status = sane_control_option(self.saneHandle, 324 | self.saneOptionNumber, 325 | SANE_ACTION_SET_VALUE, 326 | &value, 327 | 0); 328 | 329 | if (status != SANE_STATUS_GOOD) { 330 | Log(@"Set failed %@: %s", self, sane_strstatus(status)); 331 | return; 332 | } 333 | } 334 | else { 335 | Log(@"Unsuported set type."); 336 | } 337 | } 338 | 339 | - (NSString*)description 340 | { 341 | return [NSString stringWithFormat:@" (name=%@, value=%@, constraint=%@)", self, self.name, self.value, self.constraint]; 342 | } 343 | 344 | @end 345 | -------------------------------------------------------------------------------- /SaneNetScanner/LoggerClient.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LoggerClient.h 3 | * 4 | * version 1.1 2012-03-31 5 | * 6 | * Part of NSLogger (client side) 7 | * https://github.com/fpillet/NSLogger 8 | * 9 | * BSD license follows (http://www.opensource.org/licenses/bsd-license.php) 10 | * 11 | * Copyright (c) 2010-2012 Florent Pillet All Rights Reserved. 12 | * 13 | * Redistribution and use in source and binary forms, with or without modification, 14 | * are permitted provided that the following conditions are met: 15 | * 16 | * Redistributions of source code must retain the above copyright notice, 17 | * this list of conditions and the following disclaimer. Redistributions in 18 | * binary form must reproduce the above copyright notice, this list of 19 | * conditions and the following disclaimer in the documentation and/or other 20 | * materials provided with the distribution. Neither the name of Florent 21 | * Pillet nor the names of its contributors may be used to endorse or promote 22 | * products derived from this software without specific prior written 23 | * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 24 | * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT 25 | * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * 35 | */ 36 | #import 37 | #import 38 | #import 39 | #import 40 | #import 41 | #import 42 | #if !TARGET_OS_IPHONE 43 | #import 44 | #endif 45 | 46 | // This define is here so that user application can test whether NSLogger Client is 47 | // being included in the project, and potentially configure their macros accordingly 48 | #define NSLOGGER_WAS_HERE 1 49 | 50 | // Set this to 0 if you absolutely NOT want any access to Cocoa (Objective-C, NS* calls) 51 | // We need a couple ones to reliably obtain the thread number and device information 52 | // Note that since we need NSAutoreleasePool when using Cocoa in the logger's worker thread, 53 | // we need to put Cocoa in multithreading mode. Also, ALLOW_COCOA_USE allows the client code 54 | // to use NSLog()-style message formatting (less verbose than CFShow()-style) through the 55 | // use of -[NSString stringWithFormat:arguments:] 56 | #define ALLOW_COCOA_USE 1 57 | 58 | /* ----------------------------------------------------------------- 59 | * Logger option flags & default options 60 | * ----------------------------------------------------------------- 61 | */ 62 | enum { 63 | kLoggerOption_LogToConsole = 0x01, 64 | kLoggerOption_BufferLogsUntilConnection = 0x02, 65 | kLoggerOption_BrowseBonjour = 0x04, 66 | kLoggerOption_BrowseOnlyLocalDomain = 0x08, 67 | kLoggerOption_UseSSL = 0x10 68 | }; 69 | 70 | #define LOGGER_DEFAULT_OPTIONS (kLoggerOption_BufferLogsUntilConnection | \ 71 | kLoggerOption_BrowseBonjour | \ 72 | kLoggerOption_BrowseOnlyLocalDomain | \ 73 | kLoggerOption_UseSSL) 74 | 75 | /* ----------------------------------------------------------------- 76 | * Structure defining a Logger 77 | * ----------------------------------------------------------------- 78 | */ 79 | typedef struct 80 | { 81 | CFStringRef bufferFile; // If non-NULL, all buffering is done to the specified file instead of in-memory 82 | CFStringRef host; // Viewer host to connect to (instead of using Bonjour) 83 | UInt32 port; // port on the viewer host 84 | 85 | CFMutableArrayRef bonjourServiceBrowsers; // Active service browsers 86 | CFMutableArrayRef bonjourServices; // Services being tried 87 | CFNetServiceBrowserRef bonjourDomainBrowser; // Domain browser 88 | 89 | CFMutableArrayRef logQueue; // Message queue 90 | pthread_mutex_t logQueueMutex; 91 | pthread_cond_t logQueueEmpty; 92 | 93 | pthread_t workerThread; // The worker thread responsible for Bonjour resolution, connection and logs transmission 94 | CFRunLoopSourceRef messagePushedSource; // A message source that fires on the worker thread when messages are available for send 95 | CFRunLoopSourceRef bufferFileChangedSource; // A message source that fires on the worker thread when the buffer file configuration changes 96 | 97 | CFWriteStreamRef logStream; // The connected stream we're writing to 98 | CFWriteStreamRef bufferWriteStream; // If bufferFile not NULL and we're not connected, points to a stream for writing log data 99 | CFReadStreamRef bufferReadStream; // If bufferFile not NULL, points to a read stream that will be emptied prior to sending the rest of in-memory messages 100 | 101 | SCNetworkReachabilityRef reachability; // The reachability object we use to determine when the target host becomes reachable 102 | CFRunLoopTimerRef checkHostTimer; // A timer to regularly check connection to the defined host, along with reachability for added reliability 103 | 104 | uint8_t *sendBuffer; // data waiting to be sent 105 | NSUInteger sendBufferSize; 106 | NSUInteger sendBufferUsed; // number of bytes of the send buffer currently in use 107 | NSUInteger sendBufferOffset; // offset in sendBuffer to start sending at 108 | 109 | int32_t messageSeq; // sequential message number (added to each message sent) 110 | 111 | // settings 112 | uint32_t options; // Flags, see enum above 113 | CFStringRef bonjourServiceType; // leave NULL to use the default 114 | CFStringRef bonjourServiceName; // leave NULL to use the first one available 115 | 116 | // internal state 117 | BOOL connected; // Set to YES once the write stream declares the connection open 118 | volatile BOOL quit; // Set to YES to terminate the logger worker thread's runloop 119 | BOOL incompleteSendOfFirstItem; // set to YES if we are sending the first item in the queue and it's bigger than what the buffer can hold 120 | } Logger; 121 | 122 | 123 | /* ----------------------------------------------------------------- 124 | * LOGGING FUNCTIONS 125 | * ----------------------------------------------------------------- 126 | */ 127 | 128 | #ifdef __cplusplus 129 | extern "C" { 130 | #endif 131 | 132 | // Functions to set and get the default logger 133 | extern void LoggerSetDefaultLogger(Logger *aLogger); 134 | extern Logger *LoggerGetDefaultLogger(void); 135 | 136 | // Initialize a new logger, set as default logger if this is the first one 137 | // Options default to: 138 | // - logging to console = NO 139 | // - buffer until connection = YES 140 | // - browse Bonjour = YES 141 | // - browse only locally on Bonjour = YES 142 | extern Logger* LoggerInit(void); 143 | 144 | // Set logger options if you don't want the default options (see above) 145 | extern void LoggerSetOptions(Logger *logger, uint32_t options); 146 | 147 | // Set Bonjour logging names, so you can force the logger to use a specific service type 148 | // or direct logs to the machine on your network which publishes a specific name 149 | extern void LoggerSetupBonjour(Logger *logger, CFStringRef bonjourServiceType, CFStringRef bonjourServiceName); 150 | 151 | // Directly set the viewer host (hostname or IP address) and port we want to connect to. If set, LoggerStart() will 152 | // try to connect there first before trying Bonjour 153 | extern void LoggerSetViewerHost(Logger *logger, CFStringRef hostName, UInt32 port); 154 | 155 | 156 | // Configure the logger to use a local file for buffering, instead of memory. 157 | // - If you initially set a buffer file after logging started but while a logger connection 158 | // has not been acquired, the contents of the log queue will be written to the buffer file 159 | // the next time a logging function is called, or when LoggerStop() is called. 160 | // - If you want to change the buffering file after logging started, you should first 161 | // call LoggerStop() the call LoggerSetBufferFile(). Note that all logs stored in the previous 162 | // buffer file WON'T be transferred to the new file in this case. 163 | extern void LoggerSetBufferFile(Logger *logger, CFStringRef absolutePath); 164 | 165 | // Activate the logger, try connecting 166 | extern void LoggerStart(Logger *logger); 167 | 168 | //extern void LoggerConnectToHost(CFDataRef address, int port); 169 | 170 | // Deactivate and free the logger. 171 | extern void LoggerStop(Logger *logger); 172 | 173 | // Pause the current thread until all messages from the logger have been transmitted 174 | // this is useful to use before an assert() aborts your program. If waitForConnection is YES, 175 | // LoggerFlush() will block even if the client is not currently connected to the desktop 176 | // viewer. You should be using NO most of the time, but in some cases it can be useful. 177 | extern void LoggerFlush(Logger *logger, BOOL waitForConnection); 178 | 179 | /* Logging functions. Each function exists in four versions: 180 | * 181 | * - one without a Logger instance (uses default logger) and without filename/line/function (no F suffix) 182 | * - one without a Logger instance but with filename/line/function (F suffix) 183 | * - one with a Logger instance (use a specific Logger) and without filename/line/function (no F suffix) 184 | * - one with a Logger instance (use a specific Logger) and with filename/line/function (F suffix) 185 | * 186 | * The exception being the single LogMessageCompat() function which is designed to be a drop-in replacement for NSLog() 187 | * 188 | */ 189 | 190 | // Log a message, calling format compatible with NSLog 191 | extern void LogMessageCompat(NSString *format, ...); 192 | 193 | // Log a message. domain can be nil if default domain. 194 | extern void LogMessage(NSString *domain, int level, NSString *format, ...) NS_FORMAT_FUNCTION(3,4); 195 | extern void LogMessageF(const char *filename, int lineNumber, const char *functionName, NSString *domain, int level, NSString *format, ...) NS_FORMAT_FUNCTION(6,7); 196 | extern void LogMessageTo(Logger *logger, NSString *domain, int level, NSString *format, ...) NS_FORMAT_FUNCTION(4,5); 197 | extern void LogMessageToF(Logger *logger, const char *filename, int lineNumber, const char *functionName, NSString *domain, int level, NSString *format, ...) NS_FORMAT_FUNCTION(7,8); 198 | 199 | // Log a message. domain can be nil if default domain (versions with va_list format args instead of ...) 200 | extern void LogMessage_va(NSString *domain, int level, NSString *format, va_list args) NS_FORMAT_FUNCTION(3,0); 201 | extern void LogMessageF_va(const char *filename, int lineNumber, const char *functionName, NSString *domain, int level, NSString *format, va_list args) NS_FORMAT_FUNCTION(6,0); 202 | extern void LogMessageTo_va(Logger *logger, NSString *domain, int level, NSString *format, va_list args) NS_FORMAT_FUNCTION(4,0); 203 | extern void LogMessageToF_va(Logger *logger, const char *filename, int lineNumber, const char *functionName, NSString *domain, int level, NSString *format, va_list args) NS_FORMAT_FUNCTION(7,0); 204 | 205 | // Send binary data to remote logger 206 | extern void LogData(NSString *domain, int level, NSData *data); 207 | extern void LogDataF(const char *filename, int lineNumber, const char *functionName, NSString *domain, int level, NSData *data); 208 | extern void LogDataTo(Logger *logger, NSString *domain, int level, NSData *data); 209 | extern void LogDataToF(Logger *logger, const char *filename, int lineNumber, const char *functionName, NSString *domain, int level, NSData *data); 210 | 211 | // Send image data to remote logger 212 | extern void LogImageData(NSString *domain, int level, int width, int height, NSData *data); 213 | extern void LogImageDataF(const char *filename, int lineNumber, const char *functionName, NSString *domain, int level, int width, int height, NSData *data); 214 | extern void LogImageDataTo(Logger *logger, NSString *domain, int level, int width, int height, NSData *data); 215 | extern void LogImageDataToF(Logger *logger, const char *filename, int lineNumber, const char *functionName, NSString *domain, int level, int width, int height, NSData *data); 216 | 217 | // Mark the start of a block. This allows the remote logger to group blocks together 218 | extern void LogStartBlock(NSString *format, ...) NS_FORMAT_FUNCTION(1,2); 219 | extern void LogStartBlockTo(Logger *logger, NSString *format, ...) NS_FORMAT_FUNCTION(2,3); 220 | 221 | // Mark the end of a block 222 | extern void LogEndBlock(void); 223 | extern void LogEndBlockTo(Logger *logger); 224 | 225 | // Log a marker (text can be null) 226 | extern void LogMarker(NSString *text); 227 | extern void LogMarkerTo(Logger *logger, NSString *text); 228 | 229 | #ifdef __cplusplus 230 | }; 231 | #endif 232 | -------------------------------------------------------------------------------- /sane/sane/saneopts.h: -------------------------------------------------------------------------------- 1 | /* sane - Scanner Access Now Easy. 2 | Copyright (C) 1996, 1997 David Mosberger-Tang and Andreas Beck 3 | This file is part of the SANE package. 4 | 5 | SANE is free software; you can redistribute it and/or modify it under 6 | the terms of the GNU General Public License as published by the Free 7 | Software Foundation; either version 2 of the License, or (at your 8 | option) any later version. 9 | 10 | SANE is distributed in the hope that it will be useful, but WITHOUT 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 | for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with sane; see the file COPYING. If not, write to the Free 17 | Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 | 19 | As a special exception, the authors of SANE give permission for 20 | additional uses of the libraries contained in this release of SANE. 21 | 22 | The exception is that, if you link a SANE library with other files 23 | to produce an executable, this does not by itself cause the 24 | resulting executable to be covered by the GNU General Public 25 | License. Your use of that executable is in no way restricted on 26 | account of linking the SANE library code into it. 27 | 28 | This exception does not, however, invalidate any other reasons why 29 | the executable file might be covered by the GNU General Public 30 | License. 31 | 32 | If you submit changes to SANE to the maintainers to be included in 33 | a subsequent release, you agree by submitting the changes that 34 | those changes may be distributed with this exception intact. 35 | 36 | If you write modifications of your own for SANE, it is your choice 37 | whether to permit this exception to apply to your modifications. 38 | If you do not wish that, delete this exception notice. 39 | 40 | This file declares common option names, titles, and descriptions. A 41 | backend is not limited to these options but for the sake of 42 | consistency it's better to use options declared here when appropriate. 43 | */ 44 | 45 | /* This file defines several option NAMEs, TITLEs and DESCs 46 | that are (or should be) used by several backends. 47 | 48 | All well known options should be listed here. But this does 49 | not mean that all options that are listed here are well known options. 50 | To find out if an option is a well known option and how well known 51 | options have to be defined please take a look at the sane standard!!! 52 | */ 53 | #ifndef saneopts_h 54 | #define saneopts_h 55 | 56 | #ifndef SANE_I18N 57 | #define SANE_I18N(text) text 58 | #endif 59 | 60 | /* This _must_ be the first option (index 0): */ 61 | #define SANE_NAME_NUM_OPTIONS "" /* never settable */ 62 | 63 | /* The common option groups */ 64 | #define SANE_NAME_STANDARD "standard" 65 | #define SANE_NAME_GEOMETRY "geometry" 66 | #define SANE_NAME_ENHANCEMENT "enhancement" 67 | #define SANE_NAME_ADVANCED "advanced" 68 | #define SANE_NAME_SENSORS "sensors" 69 | 70 | #define SANE_NAME_PREVIEW "preview" 71 | #define SANE_NAME_GRAY_PREVIEW "preview-in-gray" 72 | #define SANE_NAME_BIT_DEPTH "depth" 73 | #define SANE_NAME_SCAN_MODE "mode" 74 | #define SANE_NAME_SCAN_SPEED "speed" 75 | #define SANE_NAME_SCAN_SOURCE "source" 76 | #define SANE_NAME_BACKTRACK "backtrack" 77 | /* Most user-interfaces will let the user specify the scan area as the 78 | top-left corner and the width/height of the scan area. The reason 79 | the backend interface uses the top-left/bottom-right corner is so 80 | that the scan area values can be properly constraint independent of 81 | any other option value. */ 82 | #define SANE_NAME_SCAN_TL_X "tl-x" 83 | #define SANE_NAME_SCAN_TL_Y "tl-y" 84 | #define SANE_NAME_SCAN_BR_X "br-x" 85 | #define SANE_NAME_SCAN_BR_Y "br-y" 86 | #define SANE_NAME_SCAN_RESOLUTION "resolution" 87 | #define SANE_NAME_SCAN_X_RESOLUTION "x-resolution" 88 | #define SANE_NAME_SCAN_Y_RESOLUTION "y-resolution" 89 | #define SANE_NAME_PAGE_WIDTH "page-width" 90 | #define SANE_NAME_PAGE_HEIGHT "page-height" 91 | #define SANE_NAME_CUSTOM_GAMMA "custom-gamma" 92 | #define SANE_NAME_GAMMA_VECTOR "gamma-table" 93 | #define SANE_NAME_GAMMA_VECTOR_R "red-gamma-table" 94 | #define SANE_NAME_GAMMA_VECTOR_G "green-gamma-table" 95 | #define SANE_NAME_GAMMA_VECTOR_B "blue-gamma-table" 96 | #define SANE_NAME_BRIGHTNESS "brightness" 97 | #define SANE_NAME_CONTRAST "contrast" 98 | #define SANE_NAME_GRAIN_SIZE "grain" 99 | #define SANE_NAME_HALFTONE "halftoning" 100 | #define SANE_NAME_BLACK_LEVEL "black-level" 101 | #define SANE_NAME_WHITE_LEVEL "white-level" 102 | #define SANE_NAME_WHITE_LEVEL_R "white-level-r" 103 | #define SANE_NAME_WHITE_LEVEL_G "white-level-g" 104 | #define SANE_NAME_WHITE_LEVEL_B "white-level-b" 105 | #define SANE_NAME_SHADOW "shadow" 106 | #define SANE_NAME_SHADOW_R "shadow-r" 107 | #define SANE_NAME_SHADOW_G "shadow-g" 108 | #define SANE_NAME_SHADOW_B "shadow-b" 109 | #define SANE_NAME_HIGHLIGHT "highlight" 110 | #define SANE_NAME_HIGHLIGHT_R "highlight-r" 111 | #define SANE_NAME_HIGHLIGHT_G "highlight-g" 112 | #define SANE_NAME_HIGHLIGHT_B "highlight-b" 113 | #define SANE_NAME_HUE "hue" 114 | #define SANE_NAME_SATURATION "saturation" 115 | #define SANE_NAME_FILE "filename" 116 | #define SANE_NAME_HALFTONE_DIMENSION "halftone-size" 117 | #define SANE_NAME_HALFTONE_PATTERN "halftone-pattern" 118 | #define SANE_NAME_RESOLUTION_BIND "resolution-bind" 119 | #define SANE_NAME_NEGATIVE "negative" 120 | #define SANE_NAME_QUALITY_CAL "quality-cal" 121 | #define SANE_NAME_DOR "double-res" 122 | #define SANE_NAME_RGB_BIND "rgb-bind" 123 | #define SANE_NAME_THRESHOLD "threshold" 124 | #define SANE_NAME_ANALOG_GAMMA "analog-gamma" 125 | #define SANE_NAME_ANALOG_GAMMA_R "analog-gamma-r" 126 | #define SANE_NAME_ANALOG_GAMMA_G "analog-gamma-g" 127 | #define SANE_NAME_ANALOG_GAMMA_B "analog-gamma-b" 128 | #define SANE_NAME_ANALOG_GAMMA_BIND "analog-gamma-bind" 129 | #define SANE_NAME_WARMUP "warmup" 130 | #define SANE_NAME_CAL_EXPOS_TIME "cal-exposure-time" 131 | #define SANE_NAME_CAL_EXPOS_TIME_R "cal-exposure-time-r" 132 | #define SANE_NAME_CAL_EXPOS_TIME_G "cal-exposure-time-g" 133 | #define SANE_NAME_CAL_EXPOS_TIME_B "cal-exposure-time-b" 134 | #define SANE_NAME_SCAN_EXPOS_TIME "scan-exposure-time" 135 | #define SANE_NAME_SCAN_EXPOS_TIME_R "scan-exposure-time-r" 136 | #define SANE_NAME_SCAN_EXPOS_TIME_G "scan-exposure-time-g" 137 | #define SANE_NAME_SCAN_EXPOS_TIME_B "scan-exposure-time-b" 138 | #define SANE_NAME_SELECT_EXPOSURE_TIME "select-exposure-time" 139 | #define SANE_NAME_CAL_LAMP_DEN "cal-lamp-density" 140 | #define SANE_NAME_SCAN_LAMP_DEN "scan-lamp-density" 141 | #define SANE_NAME_SELECT_LAMP_DENSITY "select-lamp-density" 142 | #define SANE_NAME_LAMP_OFF_AT_EXIT "lamp-off-at-exit" 143 | 144 | /* well known options from 'SENSORS' group*/ 145 | #define SANE_NAME_SCAN "scan" 146 | #define SANE_NAME_EMAIL "email" 147 | #define SANE_NAME_FAX "fax" 148 | #define SANE_NAME_COPY "copy" 149 | #define SANE_NAME_PDF "pdf" 150 | #define SANE_NAME_CANCEL "cancel" 151 | #define SANE_NAME_PAGE_LOADED "page-loaded" 152 | #define SANE_NAME_COVER_OPEN "cover-open" 153 | 154 | #define SANE_TITLE_NUM_OPTIONS SANE_I18N("Number of options") 155 | 156 | #define SANE_TITLE_STANDARD SANE_I18N("Standard") 157 | #define SANE_TITLE_GEOMETRY SANE_I18N("Geometry") 158 | #define SANE_TITLE_ENHANCEMENT SANE_I18N("Enhancement") 159 | #define SANE_TITLE_ADVANCED SANE_I18N("Advanced") 160 | #define SANE_TITLE_SENSORS SANE_I18N("Sensors") 161 | 162 | #define SANE_TITLE_PREVIEW SANE_I18N("Preview") 163 | #define SANE_TITLE_GRAY_PREVIEW SANE_I18N("Force monochrome preview") 164 | #define SANE_TITLE_BIT_DEPTH SANE_I18N("Bit depth") 165 | #define SANE_TITLE_SCAN_MODE SANE_I18N("Scan mode") 166 | #define SANE_TITLE_SCAN_SPEED SANE_I18N("Scan speed") 167 | #define SANE_TITLE_SCAN_SOURCE SANE_I18N("Scan source") 168 | #define SANE_TITLE_BACKTRACK SANE_I18N("Force backtracking") 169 | #define SANE_TITLE_SCAN_TL_X SANE_I18N("Top-left x") 170 | #define SANE_TITLE_SCAN_TL_Y SANE_I18N("Top-left y") 171 | #define SANE_TITLE_SCAN_BR_X SANE_I18N("Bottom-right x") 172 | #define SANE_TITLE_SCAN_BR_Y SANE_I18N("Bottom-right y") 173 | #define SANE_TITLE_SCAN_RESOLUTION SANE_I18N("Scan resolution") 174 | #define SANE_TITLE_SCAN_X_RESOLUTION SANE_I18N("X-resolution") 175 | #define SANE_TITLE_SCAN_Y_RESOLUTION SANE_I18N("Y-resolution") 176 | #define SANE_TITLE_PAGE_WIDTH SANE_I18N("Page width") 177 | #define SANE_TITLE_PAGE_HEIGHT SANE_I18N("Page height") 178 | #define SANE_TITLE_CUSTOM_GAMMA SANE_I18N("Use custom gamma table") 179 | #define SANE_TITLE_GAMMA_VECTOR SANE_I18N("Image intensity") 180 | #define SANE_TITLE_GAMMA_VECTOR_R SANE_I18N("Red intensity") 181 | #define SANE_TITLE_GAMMA_VECTOR_G SANE_I18N("Green intensity") 182 | #define SANE_TITLE_GAMMA_VECTOR_B SANE_I18N("Blue intensity") 183 | #define SANE_TITLE_BRIGHTNESS SANE_I18N("Brightness") 184 | #define SANE_TITLE_CONTRAST SANE_I18N("Contrast") 185 | #define SANE_TITLE_GRAIN_SIZE SANE_I18N("Grain size") 186 | #define SANE_TITLE_HALFTONE SANE_I18N("Halftoning") 187 | #define SANE_TITLE_BLACK_LEVEL SANE_I18N("Black level") 188 | #define SANE_TITLE_WHITE_LEVEL SANE_I18N("White level") 189 | #define SANE_TITLE_WHITE_LEVEL_R SANE_I18N("White level for red") 190 | #define SANE_TITLE_WHITE_LEVEL_G SANE_I18N("White level for green") 191 | #define SANE_TITLE_WHITE_LEVEL_B SANE_I18N("White level for blue") 192 | #define SANE_TITLE_SHADOW SANE_I18N("Shadow") 193 | #define SANE_TITLE_SHADOW_R SANE_I18N("Shadow for red") 194 | #define SANE_TITLE_SHADOW_G SANE_I18N("Shadow for green") 195 | #define SANE_TITLE_SHADOW_B SANE_I18N("Shadow for blue") 196 | #define SANE_TITLE_HIGHLIGHT SANE_I18N("Highlight") 197 | #define SANE_TITLE_HIGHLIGHT_R SANE_I18N("Highlight for red") 198 | #define SANE_TITLE_HIGHLIGHT_G SANE_I18N("Highlight for green") 199 | #define SANE_TITLE_HIGHLIGHT_B SANE_I18N("Highlight for blue") 200 | #define SANE_TITLE_HUE SANE_I18N("Hue") 201 | #define SANE_TITLE_SATURATION SANE_I18N("Saturation") 202 | #define SANE_TITLE_FILE SANE_I18N("Filename") 203 | #define SANE_TITLE_HALFTONE_DIMENSION SANE_I18N("Halftone pattern size") 204 | #define SANE_TITLE_HALFTONE_PATTERN SANE_I18N("Halftone pattern") 205 | #define SANE_TITLE_RESOLUTION_BIND SANE_I18N("Bind X and Y resolution") 206 | #define SANE_TITLE_NEGATIVE SANE_I18N("Negative") 207 | #define SANE_TITLE_QUALITY_CAL SANE_I18N("Quality calibration") 208 | #define SANE_TITLE_DOR SANE_I18N("Double Optical Resolution") 209 | #define SANE_TITLE_RGB_BIND SANE_I18N("Bind RGB") 210 | #define SANE_TITLE_THRESHOLD SANE_I18N("Threshold") 211 | #define SANE_TITLE_ANALOG_GAMMA SANE_I18N("Analog gamma correction") 212 | #define SANE_TITLE_ANALOG_GAMMA_R SANE_I18N("Analog gamma red") 213 | #define SANE_TITLE_ANALOG_GAMMA_G SANE_I18N("Analog gamma green") 214 | #define SANE_TITLE_ANALOG_GAMMA_B SANE_I18N("Analog gamma blue") 215 | #define SANE_TITLE_ANALOG_GAMMA_BIND SANE_I18N("Bind analog gamma") 216 | #define SANE_TITLE_WARMUP SANE_I18N("Warmup lamp") 217 | #define SANE_TITLE_CAL_EXPOS_TIME SANE_I18N("Cal. exposure-time") 218 | #define SANE_TITLE_CAL_EXPOS_TIME_R SANE_I18N("Cal. exposure-time for red") 219 | #define SANE_TITLE_CAL_EXPOS_TIME_G SANE_I18N("Cal. exposure-time for " \ 220 | "green") 221 | #define SANE_TITLE_CAL_EXPOS_TIME_B SANE_I18N("Cal. exposure-time for blue") 222 | #define SANE_TITLE_SCAN_EXPOS_TIME SANE_I18N("Scan exposure-time") 223 | #define SANE_TITLE_SCAN_EXPOS_TIME_R SANE_I18N("Scan exposure-time for red") 224 | #define SANE_TITLE_SCAN_EXPOS_TIME_G SANE_I18N("Scan exposure-time for " \ 225 | "green") 226 | #define SANE_TITLE_SCAN_EXPOS_TIME_B SANE_I18N("Scan exposure-time for blue") 227 | #define SANE_TITLE_SELECT_EXPOSURE_TIME SANE_I18N("Set exposure-time") 228 | #define SANE_TITLE_CAL_LAMP_DEN SANE_I18N("Cal. lamp density") 229 | #define SANE_TITLE_SCAN_LAMP_DEN SANE_I18N("Scan lamp density") 230 | #define SANE_TITLE_SELECT_LAMP_DENSITY SANE_I18N("Set lamp density") 231 | #define SANE_TITLE_LAMP_OFF_AT_EXIT SANE_I18N("Lamp off at exit") 232 | 233 | /* well known options from 'SENSORS' group*/ 234 | #define SANE_TITLE_SCAN "Scan button" 235 | #define SANE_TITLE_EMAIL "Email button" 236 | #define SANE_TITLE_FAX "Fax button" 237 | #define SANE_TITLE_COPY "Copy button" 238 | #define SANE_TITLE_PDF "PDF button" 239 | #define SANE_TITLE_CANCEL "Cancel button" 240 | #define SANE_TITLE_PAGE_LOADED "Page loaded" 241 | #define SANE_TITLE_COVER_OPEN "Cover open" 242 | 243 | /* Descriptive/help strings for above options: */ 244 | #define SANE_DESC_NUM_OPTIONS \ 245 | SANE_I18N("Read-only option that specifies how many options a specific " \ 246 | "devices supports.") 247 | 248 | #define SANE_DESC_STANDARD SANE_I18N("Source, mode and resolution options") 249 | #define SANE_DESC_GEOMETRY SANE_I18N("Scan area and media size options") 250 | #define SANE_DESC_ENHANCEMENT SANE_I18N("Image modification options") 251 | #define SANE_DESC_ADVANCED SANE_I18N("Hardware specific options") 252 | #define SANE_DESC_SENSORS SANE_I18N("Scanner sensors and buttons") 253 | 254 | #define SANE_DESC_PREVIEW \ 255 | SANE_I18N("Request a preview-quality scan.") 256 | 257 | #define SANE_DESC_GRAY_PREVIEW \ 258 | SANE_I18N("Request that all previews are done in monochrome mode. On a " \ 259 | "three-pass scanner this cuts down the number of passes to one and on a " \ 260 | "one-pass scanner, it reduces the memory requirements and scan-time of the " \ 261 | "preview.") 262 | 263 | #define SANE_DESC_BIT_DEPTH \ 264 | SANE_I18N("Number of bits per sample, typical values are 1 for \"line-art\" " \ 265 | "and 8 for multibit scans.") 266 | 267 | #define SANE_DESC_SCAN_MODE \ 268 | SANE_I18N("Selects the scan mode (e.g., lineart, monochrome, or color).") 269 | 270 | #define SANE_DESC_SCAN_SPEED \ 271 | SANE_I18N("Determines the speed at which the scan proceeds.") 272 | 273 | #define SANE_DESC_SCAN_SOURCE \ 274 | SANE_I18N("Selects the scan source (such as a document-feeder).") 275 | 276 | #define SANE_DESC_BACKTRACK \ 277 | SANE_I18N("Controls whether backtracking is forced.") 278 | 279 | #define SANE_DESC_SCAN_TL_X \ 280 | SANE_I18N("Top-left x position of scan area.") 281 | 282 | #define SANE_DESC_SCAN_TL_Y \ 283 | SANE_I18N("Top-left y position of scan area.") 284 | 285 | #define SANE_DESC_SCAN_BR_X \ 286 | SANE_I18N("Bottom-right x position of scan area.") 287 | 288 | #define SANE_DESC_SCAN_BR_Y \ 289 | SANE_I18N("Bottom-right y position of scan area.") 290 | 291 | #define SANE_DESC_SCAN_RESOLUTION \ 292 | SANE_I18N("Sets the resolution of the scanned image.") 293 | 294 | #define SANE_DESC_SCAN_X_RESOLUTION \ 295 | SANE_I18N("Sets the horizontal resolution of the scanned image.") 296 | 297 | #define SANE_DESC_SCAN_Y_RESOLUTION \ 298 | SANE_I18N("Sets the vertical resolution of the scanned image.") 299 | 300 | #define SANE_DESC_PAGE_WIDTH \ 301 | SANE_I18N("Specifies the width of the media. Required for automatic " \ 302 | "centering of sheet-fed scans.") 303 | 304 | #define SANE_DESC_PAGE_HEIGHT \ 305 | SANE_I18N("Specifies the height of the media.") 306 | 307 | #define SANE_DESC_CUSTOM_GAMMA \ 308 | SANE_I18N("Determines whether a builtin or a custom gamma-table should be " \ 309 | "used.") 310 | 311 | #define SANE_DESC_GAMMA_VECTOR \ 312 | SANE_I18N("Gamma-correction table. In color mode this option equally " \ 313 | "affects the red, green, and blue channels simultaneously (i.e., it is an " \ 314 | "intensity gamma table).") 315 | 316 | #define SANE_DESC_GAMMA_VECTOR_R \ 317 | SANE_I18N("Gamma-correction table for the red band.") 318 | 319 | #define SANE_DESC_GAMMA_VECTOR_G \ 320 | SANE_I18N("Gamma-correction table for the green band.") 321 | 322 | #define SANE_DESC_GAMMA_VECTOR_B \ 323 | SANE_I18N("Gamma-correction table for the blue band.") 324 | 325 | #define SANE_DESC_BRIGHTNESS \ 326 | SANE_I18N("Controls the brightness of the acquired image.") 327 | 328 | #define SANE_DESC_CONTRAST \ 329 | SANE_I18N("Controls the contrast of the acquired image.") 330 | 331 | #define SANE_DESC_GRAIN_SIZE \ 332 | SANE_I18N("Selects the \"graininess\" of the acquired image. Smaller values " \ 333 | "result in sharper images.") 334 | 335 | #define SANE_DESC_HALFTONE \ 336 | SANE_I18N("Selects whether the acquired image should be halftoned (dithered).") 337 | 338 | #define SANE_DESC_BLACK_LEVEL \ 339 | SANE_I18N("Selects what radiance level should be considered \"black\".") 340 | 341 | #define SANE_DESC_WHITE_LEVEL \ 342 | SANE_I18N("Selects what radiance level should be considered \"white\".") 343 | 344 | #define SANE_DESC_WHITE_LEVEL_R \ 345 | SANE_I18N("Selects what red radiance level should be considered \"white\".") 346 | 347 | #define SANE_DESC_WHITE_LEVEL_G \ 348 | SANE_I18N("Selects what green radiance level should be considered \"white\".") 349 | 350 | #define SANE_DESC_WHITE_LEVEL_B \ 351 | SANE_I18N("Selects what blue radiance level should be considered \"white\".") 352 | 353 | #define SANE_DESC_SHADOW \ 354 | SANE_I18N("Selects what radiance level should be considered \"black\".") 355 | #define SANE_DESC_SHADOW_R \ 356 | SANE_I18N("Selects what red radiance level should be considered \"black\".") 357 | #define SANE_DESC_SHADOW_G \ 358 | SANE_I18N("Selects what green radiance level should be considered \"black\".") 359 | #define SANE_DESC_SHADOW_B \ 360 | SANE_I18N("Selects what blue radiance level should be considered \"black\".") 361 | 362 | #define SANE_DESC_HIGHLIGHT \ 363 | SANE_I18N("Selects what radiance level should be considered \"white\".") 364 | #define SANE_DESC_HIGHLIGHT_R \ 365 | SANE_I18N("Selects what red radiance level should be considered \"full red\".") 366 | #define SANE_DESC_HIGHLIGHT_G \ 367 | SANE_I18N("Selects what green radiance level should be considered \"full " \ 368 | "green\".") 369 | #define SANE_DESC_HIGHLIGHT_B \ 370 | SANE_I18N("Selects what blue radiance level should be considered \"full " \ 371 | "blue\".") 372 | 373 | #define SANE_DESC_HUE \ 374 | SANE_I18N("Controls the \"hue\" (blue-level) of the acquired image.") 375 | 376 | #define SANE_DESC_SATURATION \ 377 | SANE_I18N("The saturation level controls the amount of \"blooming\" that " \ 378 | "occurs when acquiring an image with a camera. Larger values cause more " \ 379 | "blooming.") 380 | 381 | #define SANE_DESC_FILE \ 382 | SANE_I18N("The filename of the image to be loaded.") 383 | 384 | #define SANE_DESC_HALFTONE_DIMENSION \ 385 | SANE_I18N("Sets the size of the halftoning (dithering) pattern used when " \ 386 | "scanning halftoned images.") 387 | 388 | #define SANE_DESC_HALFTONE_PATTERN \ 389 | SANE_I18N("Defines the halftoning (dithering) pattern for scanning " \ 390 | "halftoned images.") 391 | 392 | #define SANE_DESC_RESOLUTION_BIND \ 393 | SANE_I18N("Use same values for X and Y resolution") 394 | #define SANE_DESC_NEGATIVE \ 395 | SANE_I18N("Swap black and white") 396 | #define SANE_DESC_QUALITY_CAL \ 397 | SANE_I18N("Do a quality white-calibration") 398 | #define SANE_DESC_DOR \ 399 | SANE_I18N("Use lens that doubles optical resolution") 400 | #define SANE_DESC_RGB_BIND \ 401 | SANE_I18N("In RGB-mode use same values for each color") 402 | #define SANE_DESC_THRESHOLD \ 403 | SANE_I18N("Select minimum-brightness to get a white point") 404 | #define SANE_DESC_ANALOG_GAMMA \ 405 | SANE_I18N("Analog gamma-correction") 406 | #define SANE_DESC_ANALOG_GAMMA_R \ 407 | SANE_I18N("Analog gamma-correction for red") 408 | #define SANE_DESC_ANALOG_GAMMA_G \ 409 | SANE_I18N("Analog gamma-correction for green") 410 | #define SANE_DESC_ANALOG_GAMMA_B \ 411 | SANE_I18N("Analog gamma-correction for blue") 412 | #define SANE_DESC_ANALOG_GAMMA_BIND \ 413 | SANE_I18N("In RGB-mode use same values for each color") 414 | #define SANE_DESC_WARMUP \ 415 | SANE_I18N("Warmup lamp before scanning") 416 | #define SANE_DESC_CAL_EXPOS_TIME \ 417 | SANE_I18N("Define exposure-time for calibration") 418 | #define SANE_DESC_CAL_EXPOS_TIME_R \ 419 | SANE_I18N("Define exposure-time for red calibration") 420 | #define SANE_DESC_CAL_EXPOS_TIME_G \ 421 | SANE_I18N("Define exposure-time for green calibration") 422 | #define SANE_DESC_CAL_EXPOS_TIME_B \ 423 | SANE_I18N("Define exposure-time for blue calibration") 424 | #define SANE_DESC_SCAN_EXPOS_TIME \ 425 | SANE_I18N("Define exposure-time for scan") 426 | #define SANE_DESC_SCAN_EXPOS_TIME_R \ 427 | SANE_I18N("Define exposure-time for red scan") 428 | #define SANE_DESC_SCAN_EXPOS_TIME_G \ 429 | SANE_I18N("Define exposure-time for green scan") 430 | #define SANE_DESC_SCAN_EXPOS_TIME_B \ 431 | SANE_I18N("Define exposure-time for blue scan") 432 | #define SANE_DESC_SELECT_EXPOSURE_TIME \ 433 | SANE_I18N("Enable selection of exposure-time") 434 | #define SANE_DESC_CAL_LAMP_DEN \ 435 | SANE_I18N("Define lamp density for calibration") 436 | #define SANE_DESC_SCAN_LAMP_DEN \ 437 | SANE_I18N("Define lamp density for scan") 438 | #define SANE_DESC_SELECT_LAMP_DENSITY \ 439 | SANE_I18N("Enable selection of lamp density") 440 | #define SANE_DESC_LAMP_OFF_AT_EXIT \ 441 | SANE_I18N("Turn off lamp when program exits") 442 | 443 | /* well known options from 'SENSORS' group*/ 444 | #define SANE_DESC_SCAN SANE_I18N("Scan button") 445 | #define SANE_DESC_EMAIL SANE_I18N("Email button") 446 | #define SANE_DESC_FAX SANE_I18N("Fax button") 447 | #define SANE_DESC_COPY SANE_I18N("Copy button") 448 | #define SANE_DESC_PDF SANE_I18N("PDF button") 449 | #define SANE_DESC_CANCEL SANE_I18N("Cancel button") 450 | #define SANE_DESC_PAGE_LOADED SANE_I18N("Page loaded") 451 | #define SANE_DESC_COVER_OPEN SANE_I18N("Cover open") 452 | 453 | /* Typical values for stringlists (to keep the backends consistent) */ 454 | #define SANE_VALUE_SCAN_MODE_COLOR SANE_I18N("Color") 455 | #define SANE_VALUE_SCAN_MODE_COLOR_LINEART SANE_I18N("Color Lineart") 456 | #define SANE_VALUE_SCAN_MODE_COLOR_HALFTONE SANE_I18N("Color Halftone") 457 | #define SANE_VALUE_SCAN_MODE_GRAY SANE_I18N("Gray") 458 | #define SANE_VALUE_SCAN_MODE_HALFTONE SANE_I18N("Halftone") 459 | #define SANE_VALUE_SCAN_MODE_LINEART SANE_I18N("Lineart") 460 | 461 | #endif /* saneopts_h */ 462 | -------------------------------------------------------------------------------- /SaneNetScanner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | AA02A6E015D159BE008B329A /* CSSequentialDataProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = AA02A6DF15D159BE008B329A /* CSSequentialDataProvider.m */; }; 11 | AA45B42715CED4D9004A8357 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA45B42615CED4D9004A8357 /* Cocoa.framework */; }; 12 | AA45B43115CED4D9004A8357 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = AA45B42F15CED4D9004A8357 /* InfoPlist.strings */; }; 13 | AA45B43315CED4D9004A8357 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AA45B43215CED4D9004A8357 /* main.m */; }; 14 | AA45B43715CED4D9004A8357 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = AA45B43515CED4D9004A8357 /* Credits.rtf */; }; 15 | AA45B44715CED518004A8357 /* libsane-net.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AA45B44615CED518004A8357 /* libsane-net.a */; }; 16 | AA45B44D15CEEB7B004A8357 /* CSSaneNetScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = AA45B44C15CEEB7B004A8357 /* CSSaneNetScanner.m */; }; 17 | AA45B45015CEED1E004A8357 /* ICD_Trampolins.m in Sources */ = {isa = PBXBuildFile; fileRef = AA45B44F15CEED1E004A8357 /* ICD_Trampolins.m */; }; 18 | AA45B45315CEF2D2004A8357 /* ICADevices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA45B45115CEF2CA004A8357 /* ICADevices.framework */; }; 19 | AA45B45815CEF529004A8357 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA45B45615CEF517004A8357 /* Carbon.framework */; }; 20 | AA45B46815CF0451004A8357 /* LoggerClient.m in Sources */ = {isa = PBXBuildFile; fileRef = AA45B46615CF0451004A8357 /* LoggerClient.m */; }; 21 | AA45B46B15CF04F1004A8357 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA45B46A15CF04F1004A8357 /* SystemConfiguration.framework */; }; 22 | AA7A28AE15D16ECE000522A7 /* DeviceMatchingInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = AA7A28AD15D16ECE000522A7 /* DeviceMatchingInfo.plist */; }; 23 | AAC97EC915D0137600B9E327 /* CSSaneOption.m in Sources */ = {isa = PBXBuildFile; fileRef = AAC97EC815D0137600B9E327 /* CSSaneOption.m */; }; 24 | AAC97ECC15D0289700B9E327 /* CSSaneOptionConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = AAC97ECB15D0289700B9E327 /* CSSaneOptionConstraint.m */; }; 25 | AAC97ECF15D0297200B9E327 /* CSSaneOptionRangeConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = AAC97ECE15D0297200B9E327 /* CSSaneOptionRangeConstraint.m */; }; 26 | AAC97ED215D0370500B9E327 /* CSSaneOptionEnumConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = AAC97ED115D0370500B9E327 /* CSSaneOptionEnumConstraint.m */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | AA02A6DE15D159BE008B329A /* CSSequentialDataProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSequentialDataProvider.h; sourceTree = ""; }; 31 | AA02A6DF15D159BE008B329A /* CSSequentialDataProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSequentialDataProvider.m; sourceTree = ""; }; 32 | AA45B42215CED4D9004A8357 /* SaneNetScanner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SaneNetScanner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | AA45B42615CED4D9004A8357 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 34 | AA45B42915CED4D9004A8357 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 35 | AA45B42A15CED4D9004A8357 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 36 | AA45B42B15CED4D9004A8357 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 37 | AA45B42E15CED4D9004A8357 /* SaneNetScanner-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SaneNetScanner-Info.plist"; sourceTree = ""; }; 38 | AA45B43015CED4D9004A8357 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 39 | AA45B43215CED4D9004A8357 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 40 | AA45B43415CED4D9004A8357 /* SaneNetScanner-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SaneNetScanner-Prefix.pch"; sourceTree = ""; }; 41 | AA45B43615CED4D9004A8357 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; 42 | AA45B44615CED518004A8357 /* libsane-net.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libsane-net.a"; sourceTree = ""; }; 43 | AA45B44915CED525004A8357 /* sane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sane.h; sourceTree = ""; }; 44 | AA45B44A15CED525004A8357 /* saneopts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = saneopts.h; sourceTree = ""; }; 45 | AA45B44B15CEEB7B004A8357 /* CSSaneNetScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSaneNetScanner.h; sourceTree = ""; }; 46 | AA45B44C15CEEB7B004A8357 /* CSSaneNetScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSaneNetScanner.m; sourceTree = ""; }; 47 | AA45B44E15CEED11004A8357 /* ICD_Trampolins.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ICD_Trampolins.h; sourceTree = ""; }; 48 | AA45B44F15CEED1E004A8357 /* ICD_Trampolins.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ICD_Trampolins.m; sourceTree = ""; }; 49 | AA45B45115CEF2CA004A8357 /* ICADevices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ICADevices.framework; path = System/Library/Frameworks/ICADevices.framework; sourceTree = SDKROOT; }; 50 | AA45B45615CEF517004A8357 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; 51 | AA45B46515CF0451004A8357 /* LoggerClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoggerClient.h; sourceTree = ""; }; 52 | AA45B46615CF0451004A8357 /* LoggerClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoggerClient.m; sourceTree = ""; }; 53 | AA45B46715CF0451004A8357 /* LoggerCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoggerCommon.h; sourceTree = ""; }; 54 | AA45B46915CF0466004A8357 /* Log.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Log.h; sourceTree = ""; }; 55 | AA45B46A15CF04F1004A8357 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 56 | AA7A28AD15D16ECE000522A7 /* DeviceMatchingInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = DeviceMatchingInfo.plist; sourceTree = ""; }; 57 | AAC97EC715D0137600B9E327 /* CSSaneOption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSaneOption.h; sourceTree = ""; }; 58 | AAC97EC815D0137600B9E327 /* CSSaneOption.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSaneOption.m; sourceTree = ""; }; 59 | AAC97ECA15D0289700B9E327 /* CSSaneOptionConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSaneOptionConstraint.h; sourceTree = ""; }; 60 | AAC97ECB15D0289700B9E327 /* CSSaneOptionConstraint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSaneOptionConstraint.m; sourceTree = ""; }; 61 | AAC97ECD15D0297200B9E327 /* CSSaneOptionRangeConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSaneOptionRangeConstraint.h; sourceTree = ""; }; 62 | AAC97ECE15D0297200B9E327 /* CSSaneOptionRangeConstraint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSaneOptionRangeConstraint.m; sourceTree = ""; }; 63 | AAC97ED015D0370500B9E327 /* CSSaneOptionEnumConstraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSaneOptionEnumConstraint.h; sourceTree = ""; }; 64 | AAC97ED115D0370500B9E327 /* CSSaneOptionEnumConstraint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSaneOptionEnumConstraint.m; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | AA45B41F15CED4D9004A8357 /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | AA45B46B15CF04F1004A8357 /* SystemConfiguration.framework in Frameworks */, 73 | AA45B45815CEF529004A8357 /* Carbon.framework in Frameworks */, 74 | AA45B45315CEF2D2004A8357 /* ICADevices.framework in Frameworks */, 75 | AA45B42715CED4D9004A8357 /* Cocoa.framework in Frameworks */, 76 | AA45B44715CED518004A8357 /* libsane-net.a in Frameworks */, 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | /* End PBXFrameworksBuildPhase section */ 81 | 82 | /* Begin PBXGroup section */ 83 | AA45B41715CED4D9004A8357 = { 84 | isa = PBXGroup; 85 | children = ( 86 | AA45B46A15CF04F1004A8357 /* SystemConfiguration.framework */, 87 | AA45B42C15CED4D9004A8357 /* SaneNetScanner */, 88 | AA45B44315CED4E9004A8357 /* sane */, 89 | AA45B42515CED4D9004A8357 /* Frameworks */, 90 | AA45B42315CED4D9004A8357 /* Products */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | AA45B42315CED4D9004A8357 /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | AA45B42215CED4D9004A8357 /* SaneNetScanner.app */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | AA45B42515CED4D9004A8357 /* Frameworks */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | AA45B45615CEF517004A8357 /* Carbon.framework */, 106 | AA45B45115CEF2CA004A8357 /* ICADevices.framework */, 107 | AA45B42615CED4D9004A8357 /* Cocoa.framework */, 108 | AA45B42815CED4D9004A8357 /* Other Frameworks */, 109 | ); 110 | name = Frameworks; 111 | sourceTree = ""; 112 | }; 113 | AA45B42815CED4D9004A8357 /* Other Frameworks */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | AA45B42915CED4D9004A8357 /* AppKit.framework */, 117 | AA45B42A15CED4D9004A8357 /* CoreData.framework */, 118 | AA45B42B15CED4D9004A8357 /* Foundation.framework */, 119 | ); 120 | name = "Other Frameworks"; 121 | sourceTree = ""; 122 | }; 123 | AA45B42C15CED4D9004A8357 /* SaneNetScanner */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | AAC97EC515D0136400B9E327 /* Sane Wrapper */, 127 | AA45B46415CF0446004A8357 /* Logging */, 128 | AA45B42D15CED4D9004A8357 /* Supporting Files */, 129 | AA45B44B15CEEB7B004A8357 /* CSSaneNetScanner.h */, 130 | AA45B44C15CEEB7B004A8357 /* CSSaneNetScanner.m */, 131 | AA02A6DE15D159BE008B329A /* CSSequentialDataProvider.h */, 132 | AA02A6DF15D159BE008B329A /* CSSequentialDataProvider.m */, 133 | ); 134 | path = SaneNetScanner; 135 | sourceTree = ""; 136 | }; 137 | AA45B42D15CED4D9004A8357 /* Supporting Files */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | AA7A28AD15D16ECE000522A7 /* DeviceMatchingInfo.plist */, 141 | AA45B42E15CED4D9004A8357 /* SaneNetScanner-Info.plist */, 142 | AA45B42F15CED4D9004A8357 /* InfoPlist.strings */, 143 | AA45B43215CED4D9004A8357 /* main.m */, 144 | AA45B43415CED4D9004A8357 /* SaneNetScanner-Prefix.pch */, 145 | AA45B43515CED4D9004A8357 /* Credits.rtf */, 146 | AA45B44E15CEED11004A8357 /* ICD_Trampolins.h */, 147 | AA45B44F15CEED1E004A8357 /* ICD_Trampolins.m */, 148 | ); 149 | name = "Supporting Files"; 150 | sourceTree = ""; 151 | }; 152 | AA45B44315CED4E9004A8357 /* sane */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | AA45B44815CED525004A8357 /* sane */, 156 | AA45B44615CED518004A8357 /* libsane-net.a */, 157 | ); 158 | path = sane; 159 | sourceTree = ""; 160 | }; 161 | AA45B44815CED525004A8357 /* sane */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | AA45B44915CED525004A8357 /* sane.h */, 165 | AA45B44A15CED525004A8357 /* saneopts.h */, 166 | ); 167 | path = sane; 168 | sourceTree = ""; 169 | }; 170 | AA45B46415CF0446004A8357 /* Logging */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | AA45B46515CF0451004A8357 /* LoggerClient.h */, 174 | AA45B46615CF0451004A8357 /* LoggerClient.m */, 175 | AA45B46715CF0451004A8357 /* LoggerCommon.h */, 176 | AA45B46915CF0466004A8357 /* Log.h */, 177 | ); 178 | name = Logging; 179 | sourceTree = ""; 180 | }; 181 | AAC97EC515D0136400B9E327 /* Sane Wrapper */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | AAC97EC715D0137600B9E327 /* CSSaneOption.h */, 185 | AAC97EC815D0137600B9E327 /* CSSaneOption.m */, 186 | AAC97ECA15D0289700B9E327 /* CSSaneOptionConstraint.h */, 187 | AAC97ECB15D0289700B9E327 /* CSSaneOptionConstraint.m */, 188 | AAC97ECD15D0297200B9E327 /* CSSaneOptionRangeConstraint.h */, 189 | AAC97ECE15D0297200B9E327 /* CSSaneOptionRangeConstraint.m */, 190 | AAC97ED015D0370500B9E327 /* CSSaneOptionEnumConstraint.h */, 191 | AAC97ED115D0370500B9E327 /* CSSaneOptionEnumConstraint.m */, 192 | ); 193 | name = "Sane Wrapper"; 194 | sourceTree = ""; 195 | }; 196 | /* End PBXGroup section */ 197 | 198 | /* Begin PBXNativeTarget section */ 199 | AA45B42115CED4D9004A8357 /* SaneNetScanner */ = { 200 | isa = PBXNativeTarget; 201 | buildConfigurationList = AA45B44015CED4D9004A8357 /* Build configuration list for PBXNativeTarget "SaneNetScanner" */; 202 | buildPhases = ( 203 | AA45B41E15CED4D9004A8357 /* Sources */, 204 | AA45B41F15CED4D9004A8357 /* Frameworks */, 205 | AA45B42015CED4D9004A8357 /* Resources */, 206 | ); 207 | buildRules = ( 208 | ); 209 | dependencies = ( 210 | ); 211 | name = SaneNetScanner; 212 | productName = SaneNetScanner; 213 | productReference = AA45B42215CED4D9004A8357 /* SaneNetScanner.app */; 214 | productType = "com.apple.product-type.application"; 215 | }; 216 | /* End PBXNativeTarget section */ 217 | 218 | /* Begin PBXProject section */ 219 | AA45B41915CED4D9004A8357 /* Project object */ = { 220 | isa = PBXProject; 221 | attributes = { 222 | CLASSPREFIX = CS; 223 | LastUpgradeCheck = 0500; 224 | ORGANIZATIONNAME = "Christian Speich"; 225 | }; 226 | buildConfigurationList = AA45B41C15CED4D9004A8357 /* Build configuration list for PBXProject "SaneNetScanner" */; 227 | compatibilityVersion = "Xcode 3.2"; 228 | developmentRegion = English; 229 | hasScannedForEncodings = 0; 230 | knownRegions = ( 231 | en, 232 | ); 233 | mainGroup = AA45B41715CED4D9004A8357; 234 | productRefGroup = AA45B42315CED4D9004A8357 /* Products */; 235 | projectDirPath = ""; 236 | projectRoot = ""; 237 | targets = ( 238 | AA45B42115CED4D9004A8357 /* SaneNetScanner */, 239 | ); 240 | }; 241 | /* End PBXProject section */ 242 | 243 | /* Begin PBXResourcesBuildPhase section */ 244 | AA45B42015CED4D9004A8357 /* Resources */ = { 245 | isa = PBXResourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | AA45B43115CED4D9004A8357 /* InfoPlist.strings in Resources */, 249 | AA45B43715CED4D9004A8357 /* Credits.rtf in Resources */, 250 | AA7A28AE15D16ECE000522A7 /* DeviceMatchingInfo.plist in Resources */, 251 | ); 252 | runOnlyForDeploymentPostprocessing = 0; 253 | }; 254 | /* End PBXResourcesBuildPhase section */ 255 | 256 | /* Begin PBXSourcesBuildPhase section */ 257 | AA45B41E15CED4D9004A8357 /* Sources */ = { 258 | isa = PBXSourcesBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | AA45B43315CED4D9004A8357 /* main.m in Sources */, 262 | AA45B44D15CEEB7B004A8357 /* CSSaneNetScanner.m in Sources */, 263 | AA45B45015CEED1E004A8357 /* ICD_Trampolins.m in Sources */, 264 | AA45B46815CF0451004A8357 /* LoggerClient.m in Sources */, 265 | AAC97EC915D0137600B9E327 /* CSSaneOption.m in Sources */, 266 | AAC97ECC15D0289700B9E327 /* CSSaneOptionConstraint.m in Sources */, 267 | AAC97ECF15D0297200B9E327 /* CSSaneOptionRangeConstraint.m in Sources */, 268 | AAC97ED215D0370500B9E327 /* CSSaneOptionEnumConstraint.m in Sources */, 269 | AA02A6E015D159BE008B329A /* CSSequentialDataProvider.m in Sources */, 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | }; 273 | /* End PBXSourcesBuildPhase section */ 274 | 275 | /* Begin PBXVariantGroup section */ 276 | AA45B42F15CED4D9004A8357 /* InfoPlist.strings */ = { 277 | isa = PBXVariantGroup; 278 | children = ( 279 | AA45B43015CED4D9004A8357 /* en */, 280 | ); 281 | name = InfoPlist.strings; 282 | sourceTree = ""; 283 | }; 284 | AA45B43515CED4D9004A8357 /* Credits.rtf */ = { 285 | isa = PBXVariantGroup; 286 | children = ( 287 | AA45B43615CED4D9004A8357 /* en */, 288 | ); 289 | name = Credits.rtf; 290 | sourceTree = ""; 291 | }; 292 | /* End PBXVariantGroup section */ 293 | 294 | /* Begin XCBuildConfiguration section */ 295 | AA45B43E15CED4D9004A8357 /* Debug */ = { 296 | isa = XCBuildConfiguration; 297 | buildSettings = { 298 | ALWAYS_SEARCH_USER_PATHS = NO; 299 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 300 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 301 | CLANG_ENABLE_OBJC_ARC = YES; 302 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 303 | COPY_PHASE_STRIP = NO; 304 | GCC_C_LANGUAGE_STANDARD = gnu99; 305 | GCC_DYNAMIC_NO_PIC = NO; 306 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 307 | GCC_OPTIMIZATION_LEVEL = 0; 308 | GCC_PREPROCESSOR_DEFINITIONS = ( 309 | "DEBUG=1", 310 | "$(inherited)", 311 | ); 312 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 313 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 314 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 315 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 316 | GCC_WARN_UNUSED_VARIABLE = YES; 317 | MACOSX_DEPLOYMENT_TARGET = 10.8; 318 | ONLY_ACTIVE_ARCH = YES; 319 | SDKROOT = macosx; 320 | }; 321 | name = Debug; 322 | }; 323 | AA45B43F15CED4D9004A8357 /* Release */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 328 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 329 | CLANG_ENABLE_OBJC_ARC = YES; 330 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 331 | COPY_PHASE_STRIP = YES; 332 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 333 | GCC_C_LANGUAGE_STANDARD = gnu99; 334 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 335 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 336 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 337 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 338 | GCC_WARN_UNUSED_VARIABLE = YES; 339 | MACOSX_DEPLOYMENT_TARGET = 10.8; 340 | SDKROOT = macosx; 341 | }; 342 | name = Release; 343 | }; 344 | AA45B44115CED4D9004A8357 /* Debug */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | COMBINE_HIDPI_IMAGES = YES; 348 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 349 | GCC_PREFIX_HEADER = "SaneNetScanner/SaneNetScanner-Prefix.pch"; 350 | HEADER_SEARCH_PATHS = "\"$(SRCROOT)/sane\""; 351 | INFOPLIST_FILE = "SaneNetScanner/SaneNetScanner-Info.plist"; 352 | INSTALL_PATH = "/Library/Image Capture/Devices"; 353 | LIBRARY_SEARCH_PATHS = ( 354 | "$(inherited)", 355 | "\"$(SRCROOT)/sane\"", 356 | ); 357 | PRODUCT_NAME = "$(TARGET_NAME)"; 358 | WRAPPER_EXTENSION = app; 359 | }; 360 | name = Debug; 361 | }; 362 | AA45B44215CED4D9004A8357 /* Release */ = { 363 | isa = XCBuildConfiguration; 364 | buildSettings = { 365 | COMBINE_HIDPI_IMAGES = YES; 366 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 367 | GCC_PREFIX_HEADER = "SaneNetScanner/SaneNetScanner-Prefix.pch"; 368 | HEADER_SEARCH_PATHS = "\"$(SRCROOT)/sane\""; 369 | INFOPLIST_FILE = "SaneNetScanner/SaneNetScanner-Info.plist"; 370 | INSTALL_PATH = "/Library/Image Capture/Devices"; 371 | LIBRARY_SEARCH_PATHS = ( 372 | "$(inherited)", 373 | "\"$(SRCROOT)/sane\"", 374 | ); 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | WRAPPER_EXTENSION = app; 377 | }; 378 | name = Release; 379 | }; 380 | /* End XCBuildConfiguration section */ 381 | 382 | /* Begin XCConfigurationList section */ 383 | AA45B41C15CED4D9004A8357 /* Build configuration list for PBXProject "SaneNetScanner" */ = { 384 | isa = XCConfigurationList; 385 | buildConfigurations = ( 386 | AA45B43E15CED4D9004A8357 /* Debug */, 387 | AA45B43F15CED4D9004A8357 /* Release */, 388 | ); 389 | defaultConfigurationIsVisible = 0; 390 | defaultConfigurationName = Release; 391 | }; 392 | AA45B44015CED4D9004A8357 /* Build configuration list for PBXNativeTarget "SaneNetScanner" */ = { 393 | isa = XCConfigurationList; 394 | buildConfigurations = ( 395 | AA45B44115CED4D9004A8357 /* Debug */, 396 | AA45B44215CED4D9004A8357 /* Release */, 397 | ); 398 | defaultConfigurationIsVisible = 0; 399 | defaultConfigurationName = Release; 400 | }; 401 | /* End XCConfigurationList section */ 402 | }; 403 | rootObject = AA45B41915CED4D9004A8357 /* Project object */; 404 | } 405 | -------------------------------------------------------------------------------- /SaneNetScanner/CSSaneNetScanner.m: -------------------------------------------------------------------------------- 1 | // 2 | // CSSaneNetScanner.m 3 | // SaneNetScanner 4 | // 5 | // Created by Christian Speich on 05.08.12. 6 | // Copyright (c) 2012 Christian Speich. All rights reserved. 7 | // 8 | 9 | #import "CSSaneNetScanner.h" 10 | 11 | #import "CSSequentialDataProvider.h" 12 | 13 | #import "CSSaneOption.h" 14 | #import "CSSaneOptionRangeConstraint.h" 15 | 16 | #include "sane/sane.h" 17 | 18 | typedef enum { 19 | ProgressNotificationsNone, 20 | ProgressNotificationsWithData, 21 | ProgressNotificationsWithoutData 22 | } ProgressNotifications; 23 | 24 | @interface CSSaneNetScanner () 25 | 26 | @property (nonatomic, strong) NSString* prettyName; 27 | 28 | @property (nonatomic, strong) NSString* deviceName; 29 | @property (nonatomic, strong) NSArray* saneAdresses; 30 | 31 | @property (nonatomic, strong) NSMutableDictionary* deviceProperties; 32 | @property (nonatomic, strong) NSDictionary* saneOptions; 33 | 34 | @property (nonatomic) BOOL open; 35 | 36 | @property (nonatomic) SANE_Handle saneHandle; 37 | 38 | @property (nonatomic) ProgressNotifications progressNotifications; 39 | @property (nonatomic) BOOL produceFinalScan; 40 | 41 | @property (nonatomic) NSString* colorSyncMode; 42 | 43 | @property (nonatomic, assign) CGColorSpaceRef colorSpace; 44 | 45 | @property (nonatomic) NSURL* rawFileURL; 46 | @property (nonatomic) NSURL* documentURL; 47 | @property (nonatomic) NSString* documentType; 48 | 49 | - (UInt32) numberOfComponents; 50 | 51 | @end 52 | 53 | @interface CSSaneNetScanner (Progress) 54 | 55 | - (void) showWarmUpMessage; 56 | - (void) doneWarmUpMessage; 57 | - (void) pageDoneMessage; 58 | - (void) scanDoneMessage; 59 | 60 | - (void) sendTransactionCanceledMessage; 61 | 62 | @end 63 | 64 | @interface CSSaneNetScanner (ICARawFile) 65 | 66 | - (void) createColorSpaceWithSaneParameters:(SANE_Parameters*)parameters; 67 | - (void) writeHeaderToFile:(NSFileHandle*)handle 68 | withSaneParameters:(SANE_Parameters*)parameters; 69 | 70 | - (void) resaveRawFileAt:(NSURL*)url 71 | asType:(NSString*)type 72 | toURL:(NSURL*)url 73 | saneParameters:(SANE_Parameters*)parameters; 74 | 75 | @end 76 | 77 | @implementation CSSaneNetScanner 78 | 79 | - (id) initWithParameters:(NSDictionary*)params; 80 | { 81 | self = [super init]; 82 | if (self) { 83 | Log(@"Params %@", params); 84 | 85 | self.open = NO; 86 | self.saneHandle = 0; 87 | self.prettyName = params[(NSString*)kICABonjourServiceNameKey]; 88 | self.deviceName = [[NSString alloc] initWithData:params[(NSString*)kICABonjourTXTRecordKey][@"deviceName"] 89 | encoding:NSUTF8StringEncoding]; 90 | self.saneAdresses = @[]; 91 | if (params[@"ipAddress"]) 92 | self.saneAdresses = [self.saneAdresses arrayByAddingObject:params[@"ipAddress"]]; 93 | if (params[@"ipAddress_v6"]) 94 | self.saneAdresses = [self.saneAdresses arrayByAddingObject: 95 | [NSString stringWithFormat:@"[%@]", params[@"ipAddress_v6"]]]; 96 | } 97 | return self; 98 | } 99 | 100 | - (void)dealloc 101 | { 102 | if (self.open || self.saneHandle != 0) { 103 | Log(@"Deallocating but sane handle is still open"); 104 | sane_close(self.saneHandle); 105 | self.saneHandle = 0; 106 | } 107 | } 108 | 109 | - (ICAError) openSession:(ICD_ScannerOpenSessionPB*)params 110 | { 111 | Log(@"Open session"); 112 | if (self.open) 113 | return kICAInvalidSessionErr; 114 | 115 | SANE_Handle handle; 116 | SANE_Status status; 117 | 118 | for (NSString* address in self.saneAdresses) { 119 | NSString* fullName = [NSString stringWithFormat:@"%@:%@", address, self.deviceName]; 120 | 121 | Log(@"Try open to %@", fullName); 122 | status = sane_open([fullName UTF8String], &handle); 123 | 124 | // If open succeeded we can quit tring 125 | if (status == SANE_STATUS_GOOD) 126 | break; 127 | else { 128 | Log(@"Failed width %s", sane_strstatus(status)); 129 | } 130 | } 131 | 132 | if (status == SANE_STATUS_GOOD) { 133 | self.open = YES; 134 | self.saneHandle = handle; 135 | 136 | return noErr; 137 | } 138 | else { 139 | return kICADeviceInternalErr; 140 | } 141 | } 142 | 143 | - (ICAError) closeSession:(ICD_ScannerCloseSessionPB*)params 144 | { 145 | Log(@"Close session"); 146 | 147 | if (!self.open) 148 | return kICAInvalidSessionErr; 149 | 150 | sane_close(self.saneHandle); 151 | self.saneHandle = 0; 152 | self.open = NO; 153 | 154 | return noErr; 155 | } 156 | 157 | - (ICAError) addPropertiesToDictitonary:(NSMutableDictionary*)dict 158 | { 159 | // Add kICAUserAssignedDeviceNameKey. Since this key is a simple NSString, 160 | // the value may be of any length. This key supercedes any name already 161 | // provided in the device information before, which is limited to 32 characters. 162 | dict[(NSString*)kICAUserAssignedDeviceNameKey] = self.prettyName; 163 | 164 | // Add key indicating that the module supports using the ICA Raw File 165 | // as a backing store for image io 166 | dict[@"supportsICARawFileFormat"] = @1; 167 | 168 | Log(@"addPropertiesToDictitonary:%@", dict); 169 | 170 | return noErr; 171 | } 172 | 173 | - (ICAError) getParameters:(ICD_ScannerGetParametersPB*)params 174 | { 175 | Log(@"Get params"); 176 | NSMutableDictionary* dict = (__bridge NSMutableDictionary*)(params->theDict); 177 | 178 | if (!dict) 179 | return paramErr; 180 | 181 | NSMutableDictionary* deviceDict = [@{ 182 | @"functionalUnits": @{ 183 | @"availableFunctionalUnitTypes" : @[ @0 ] 184 | }, 185 | @"selectedFunctionalUnitType": @0, 186 | 187 | @"ICAP_SUPPORTEDSIZES": @{ @"current": @1, @"default": @1, @"type": @"TWON_ENUMERATION", @"value": @[ @1, @2, @3, @4, @5, @10, @0 ]}, 188 | 189 | @"ICAP_UNITS": @{ @"current": @0, @"default": @0, @"type": @"TWON_ENUMERATION", @"value": @[ @0, @1, @5 ] }, 190 | 191 | } mutableCopy]; 192 | 193 | self.saneOptions = [CSSaneOption saneOptionsForHandle:self.saneHandle]; 194 | 195 | // Export the resolution 196 | if (self.saneOptions[kSaneScanResolution]) { 197 | NSMutableDictionary* d = [NSMutableDictionary dictionary]; 198 | CSSaneOption* option = self.saneOptions[kSaneScanResolution]; 199 | 200 | [option.constraint addToDeviceDictionary:d]; 201 | d[@"current"] = option.value; 202 | d[@"default"] = option.value; 203 | 204 | deviceDict[@"ICAP_XRESOLUTION"] = d; 205 | deviceDict[@"ICAP_YRESOLUTION"] = d; 206 | } 207 | else { 208 | Log(@"WARN: scanner does not support resolutions!?"); 209 | } 210 | 211 | // Export the physical width 212 | for (NSString* name in @[ kSaneTopLeftX, kSaneBottomRightX ]) { 213 | if (self.saneOptions[name]) { 214 | // Convert to inch (will be reported as mm) 215 | CSSaneOption* option = self.saneOptions[name]; 216 | CSSaneOptionRangeConstraint* constraint = (CSSaneOptionRangeConstraint*)option.constraint; 217 | double width = ([constraint.maxValue doubleValue] - [constraint.minValue doubleValue])/25.4; 218 | 219 | // If already exists look if the new width is smaller and update if so 220 | if (deviceDict[@"ICAP_PHYSICALWIDTH"]) { 221 | if ([deviceDict[@"ICAP_PHYSICALWIDTH"][@"value"] doubleValue] > width) { 222 | deviceDict[@"ICAP_PHYSICALWIDTH"] = @{ 223 | @"type": @"TWON_ONEVALUE", 224 | @"value": @(width) 225 | }; 226 | } 227 | } 228 | // Not present yes, so set 229 | else { 230 | deviceDict[@"ICAP_PHYSICALWIDTH"] = @{ 231 | @"type": @"TWON_ONEVALUE", 232 | @"value": @(width) 233 | }; 234 | } 235 | } 236 | } 237 | 238 | // Export the physical height 239 | for (NSString* name in @[ kSaneTopLeftY, kSaneBottomRightY ]) { 240 | if (self.saneOptions[name]) { 241 | // Convert to inch (will be reported as mm) 242 | CSSaneOption* option = self.saneOptions[name]; 243 | CSSaneOptionRangeConstraint* constraint = (CSSaneOptionRangeConstraint*)option.constraint; 244 | double height = ([constraint.maxValue doubleValue] - [constraint.minValue doubleValue])/25.4; 245 | 246 | // If already exists look if the new width is smaller and update if so 247 | if (deviceDict[@"ICAP_PHYSICALHEIGHT"]) { 248 | if ([deviceDict[@"ICAP_PHYSICALHEIGHT"][@"value"] doubleValue] > height) { 249 | deviceDict[@"ICAP_PHYSICALHEIGHT"] = @{ 250 | @"type": @"TWON_ONEVALUE", 251 | @"value": @(height) 252 | }; 253 | } 254 | } 255 | // Not present yes, so set 256 | else { 257 | deviceDict[@"ICAP_PHYSICALHEIGHT"] = @{ 258 | @"type": @"TWON_ONEVALUE", 259 | @"value": @(height) 260 | }; 261 | } 262 | } 263 | } 264 | 265 | // The bitdepth was not an option from the device 266 | // now we have to infer from the scan mode. 267 | if (deviceDict[@"ICAP_BITDEPTH"] == nil) { 268 | deviceDict[@"ICAP_BITDEPTH"] = @{ 269 | @"current": @1, 270 | @"default": @1, 271 | @"type": 272 | @"TWON_ENUMERATION", 273 | @"value": @[ @1, @8 ] 274 | }; 275 | } 276 | 277 | dict[@"device"] = deviceDict; 278 | self.deviceProperties = deviceDict; 279 | 280 | Log(@"Updated parameters %@", dict); 281 | 282 | return noErr; 283 | } 284 | 285 | - (ICAError) setParameters:(ICD_ScannerSetParametersPB*)params 286 | { 287 | Log(@"Set params: %@", params->theDict); 288 | NSDictionary* dict = ((__bridge NSDictionary *)(params->theDict))[@"userScanArea"]; 289 | 290 | 291 | { 292 | NSString* documentPath = dict[@"document folder"]; 293 | documentPath = [documentPath stringByAppendingPathComponent:dict[@"document name"]]; 294 | documentPath = [documentPath stringByAppendingPathExtension:dict[@"document extension"]]; 295 | 296 | if (documentPath) { 297 | self.documentURL = [NSURL fileURLWithPath:documentPath]; 298 | self.documentType = dict[@"document format"]; 299 | } 300 | 301 | // RAW is not requested, so we need a temporary raw file 302 | if (![self.documentType isEqualToString:@"com.apple.ica.raw"] && !self.rawFileURL) { 303 | self.rawFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingFormat:@"scan-raw-%@.ica", [[NSUUID UUID] UUIDString]]]; 304 | } 305 | } 306 | 307 | int unit = [dict[@"ICAP_UNITS"][@"value"] intValue]; 308 | 309 | if (dict[@"ColorSyncMode"]) { 310 | CSSaneOption* option = self.saneOptions[kSaneScanMode]; 311 | 312 | NSString* syncMode = dict[@"ColorSyncMode"]; 313 | if ([syncMode isEqualToString:@"scanner.reflective.RGB.positive"]) { 314 | option.value = @"Color"; 315 | } 316 | else if ([syncMode isEqualToString:@"scanner.reflective.Gray.positive"]) { 317 | option.value = @"Gray"; 318 | } 319 | else { 320 | Log(@"Unkown colorsyncmode %@", syncMode); 321 | } 322 | } 323 | 324 | // X and Y resolution are always equal 325 | if (dict[@"ICAP_XRESOLUTION"] || dict[@"ICAP_XRESOLUTION"]) { 326 | CSSaneOption* option = self.saneOptions[kSaneScanResolution]; 327 | 328 | if (unit == 1 /* Centimeter */) { 329 | // Convert dpcm to dpi 330 | // 1 dpcm = 2,54 dpi 331 | option.value = @([dict[@"ICAP_XRESOLUTION"][@"value"] doubleValue] / 2.54); 332 | } 333 | else if (unit == 0 /* Inches */) { 334 | // Great nothing to to here =) 335 | option.value = dict[@"ICAP_XRESOLUTION"][@"value"]; 336 | } 337 | else { 338 | Log(@"Unsupported unit"); 339 | } 340 | } 341 | 342 | if (dict[@"scan mode"]) { 343 | CSSaneOption* option = self.saneOptions[kSanePreview]; 344 | 345 | if ([dict[@"scan mode"] isEqualToString:@"overview"]) { 346 | option.value = @1; 347 | } 348 | else { 349 | option.value = @0; 350 | } 351 | } 352 | 353 | if (dict[@"offsetX"]) { 354 | CSSaneOption* option = self.saneOptions[kSaneTopLeftX]; 355 | 356 | if (unit == 1 /* Centimeter */) { 357 | // Convert cm to mm 358 | option.value = @([dict[@"offsetX"] doubleValue] * 10); 359 | } 360 | else if (unit == 0 /* Inches */) { 361 | // Convert inches to mm 362 | option.value = @([dict[@"offsetX"] doubleValue] * 25.4); 363 | } 364 | } 365 | 366 | if (dict[@"offsetY"]) { 367 | CSSaneOption* option = self.saneOptions[kSaneTopLeftY]; 368 | 369 | if (unit == 1 /* Centimeter */) { 370 | // Convert cm to mm 371 | option.value = @([dict[@"offsetY"] doubleValue] * 10); 372 | } 373 | else if (unit == 0 /* Inches */) { 374 | // Convert inches to mm 375 | option.value = @([dict[@"offsetY"] doubleValue] * 25.4); 376 | } 377 | } 378 | 379 | if (dict[@"width"]) { 380 | CSSaneOption* option = self.saneOptions[kSaneBottomRightX]; 381 | 382 | double value = [dict[@"offsetX"] doubleValue] + [dict[@"width"] doubleValue]; 383 | if (unit == 1 /* Centimeter */) { 384 | // Convert cm to mm 385 | option.value = @(value * 10); 386 | } 387 | else if (unit == 0 /* Inches */) { 388 | // Convert inches to mm 389 | option.value = @(value * 25.4); 390 | } 391 | } 392 | 393 | if (dict[@"height"]) { 394 | CSSaneOption* option = self.saneOptions[kSaneBottomRightY]; 395 | 396 | double value = [dict[@"offsetY"] doubleValue] + [dict[@"height"] doubleValue]; 397 | if (unit == 1 /* Centimeter */) { 398 | // Convert cm to mm 399 | option.value = @(value * 10); 400 | } 401 | else if (unit == 0 /* Inches */) { 402 | // Convert inches to mm 403 | option.value = @(value * 25.4); 404 | } 405 | } 406 | 407 | if ([dict[@"progressNotificationWithData"] boolValue]) { 408 | self.progressNotifications = ProgressNotificationsWithData; 409 | } 410 | else if ([dict[@"progressNotificationNoData"] boolValue]) { 411 | self.progressNotifications = ProgressNotificationsWithoutData; 412 | } 413 | else { 414 | self.progressNotifications = ProgressNotificationsNone; 415 | } 416 | 417 | if ([dict[@"scan mode"] isEqualToString:@"overview"]) 418 | self.produceFinalScan = NO; 419 | else 420 | self.produceFinalScan = YES; 421 | 422 | self.colorSyncMode = dict[@"ColorSyncMode"]; 423 | 424 | return noErr; 425 | } 426 | 427 | - (ICAError) status:(ICD_ScannerStatusPB*)params 428 | { 429 | Log( @"status"); 430 | 431 | return paramErr; 432 | } 433 | 434 | - (ICAError) start:(ICD_ScannerStartPB*)params 435 | { 436 | Log(@"Start"); 437 | SANE_Status status; 438 | SANE_Parameters parameters; 439 | 440 | [self showWarmUpMessage]; 441 | Log(@"sane_start"); 442 | status = sane_start(self.saneHandle); 443 | 444 | if (status != SANE_STATUS_GOOD) { 445 | Log(@"sane_start failed: %s", sane_strstatus(status)); 446 | return kICADeviceInternalErr; 447 | } 448 | 449 | Log(@"sane_get_parameters"); 450 | status = sane_get_parameters(self.saneHandle, ¶meters); 451 | 452 | if (status != SANE_STATUS_GOOD) { 453 | Log(@"sane_get_parameters failed: %s", sane_strstatus(status)); 454 | sane_cancel(self.saneHandle); 455 | return kICADeviceInternalErr; 456 | } 457 | Log(@"sane_get_parameters: last_frame=%u, bytes_per_line=%u, pixels_per_line=%u, lines=%u, depth=%u", parameters.last_frame, parameters.bytes_per_line, parameters.pixels_per_line, parameters.lines, parameters.depth); 458 | 459 | [self doneWarmUpMessage]; 460 | 461 | Log(@"Prepare raw file"); 462 | NSFileHandle* rawFileHandle; 463 | 464 | if (![self.documentType isEqualToString:@"com.apple.ica.raw"]) { 465 | [[NSFileManager defaultManager] createFileAtPath:[self.rawFileURL path] 466 | contents:nil 467 | attributes:nil]; 468 | rawFileHandle = [NSFileHandle fileHandleForWritingAtPath:[self.rawFileURL path]]; 469 | } 470 | else { 471 | [[NSFileManager defaultManager] createFileAtPath:[self.documentURL path] 472 | contents:nil 473 | attributes:nil]; 474 | rawFileHandle = [NSFileHandle fileHandleForWritingAtPath:[self.documentURL path]]; 475 | } 476 | 477 | [self createColorSpaceWithSaneParameters:¶meters]; 478 | 479 | // Write header 480 | [self writeHeaderToFile:rawFileHandle 481 | withSaneParameters:¶meters]; 482 | 483 | 484 | Log(@"Prepare buffers"); 485 | int bufferSize; 486 | int bufferdRows; 487 | NSMutableData* buffer; 488 | 489 | // Choose buffer size 490 | // 491 | // Use a buffer size around 50KiB. 492 | // the size will be aligned to row boundries 493 | bufferdRows = MIN(500*1025 / parameters.bytes_per_line, parameters.lines); 494 | bufferSize = bufferdRows * parameters.bytes_per_line; 495 | 496 | buffer = [NSMutableData dataWithLength:bufferSize]; 497 | 498 | Log(@"Choose to buffer %u rows (%u in size)", bufferdRows, bufferSize); 499 | 500 | Log(@"Begin reading"); 501 | int row = 0; 502 | 503 | do { 504 | // Fill the buffer 505 | unsigned char* b = [buffer mutableBytes]; 506 | int filled = 0; 507 | 508 | do { 509 | SANE_Int readBytes; 510 | status = sane_read(self.saneHandle, 511 | &b[filled], 512 | bufferSize - filled, 513 | &readBytes); 514 | 515 | if (status == SANE_STATUS_EOF) 516 | break; 517 | else if (status != SANE_STATUS_GOOD) { 518 | NSLog(@"Read error"); 519 | return kICADeviceInternalErr; 520 | } 521 | 522 | filled += readBytes; 523 | } while (filled < bufferSize); 524 | // Shrink the buffer if not fully filled 525 | // (may happen for the last block) 526 | [buffer setLength:filled]; 527 | 528 | // Means we have to save the data somewhere 529 | if (self.produceFinalScan) { 530 | [rawFileHandle writeData:buffer]; 531 | } 532 | 533 | // Notify the image capture kit that we made progress 534 | if (self.progressNotifications != ProgressNotificationsNone) { 535 | ICASendNotificationPB notePB = {}; 536 | NSMutableDictionary* d = [@{ 537 | (id)kICANotificationICAObjectKey: @(self.scannerObjectInfo->icaObject), 538 | (id)kICANotificationTypeKey: (id)kICANotificationTypeScanProgressStatus 539 | } mutableCopy]; 540 | 541 | notePB.notificationDictionary = (__bridge CFMutableDictionaryRef)d; 542 | 543 | // Add image with data 544 | if (self.progressNotifications == ProgressNotificationsWithData) { 545 | ICDAddImageInfoToNotificationDictionary(notePB.notificationDictionary, 546 | parameters.pixels_per_line, 547 | parameters.lines, 548 | parameters.bytes_per_line, 549 | row, 550 | bufferdRows, 551 | (UInt32)[buffer length], 552 | (void*)[buffer bytes]); 553 | } 554 | // Add image info without data 555 | else { 556 | ICDAddImageInfoToNotificationDictionary(notePB.notificationDictionary, 557 | parameters.pixels_per_line, 558 | parameters.lines, 559 | parameters.bytes_per_line, 560 | row, 561 | bufferdRows, 562 | 0, 563 | NULL); 564 | } 565 | 566 | // Send the progress and check if the user 567 | // canceled the scan 568 | if (ICDSendNotificationAndWaitForReply(¬ePB) == noErr) 569 | { 570 | if (notePB.replyCode == userCanceledErr) { 571 | Log(@"User canceled. Clean up..."); 572 | sane_cancel(self.saneHandle); 573 | 574 | [self sendTransactionCanceledMessage]; 575 | return noErr; 576 | } 577 | } 578 | } 579 | Log(@"Read line %i", row); 580 | row+=bufferdRows; 581 | } while (status == SANE_STATUS_GOOD); 582 | 583 | // We now need to read the raw file and produce a formatted version 584 | if (self.produceFinalScan) { 585 | if (![self.documentType isEqualToString:@"com.apple.ica.raw"]) { 586 | [self resaveRawFileAt:self.rawFileURL 587 | asType:self.documentType 588 | toURL:self.documentURL 589 | saneParameters:¶meters]; 590 | } 591 | } 592 | 593 | sane_cancel(self.saneHandle); 594 | 595 | Log(@"Done..."); 596 | [self pageDoneMessage]; 597 | [self scanDoneMessage]; 598 | 599 | return noErr; 600 | } 601 | 602 | - (UInt32) numberOfComponents 603 | { 604 | NSString* scanMode = [self.saneOptions[kSaneScanMode] value]; 605 | UInt32 numberOfComponents = 0; 606 | 607 | if ([scanMode isEqualToString:@"Color"]) 608 | numberOfComponents = 3; 609 | else if ([scanMode isEqualToString:@"Gray"] || [scanMode isEqualToString:@"Lineart"]) 610 | numberOfComponents = 1; 611 | 612 | return numberOfComponents; 613 | } 614 | 615 | @end 616 | 617 | @implementation CSSaneNetScanner (Progress) 618 | 619 | - (void) showWarmUpMessage 620 | { 621 | ICASendNotificationPB notePB = {}; 622 | NSMutableDictionary* dict = [@{ 623 | (id)kICANotificationICAObjectKey: @(self.scannerObjectInfo->icaObject), 624 | (id)kICANotificationTypeKey: (id)kICANotificationTypeDeviceStatusInfo, 625 | (id)kICANotificationSubTypeKey: (id)kICANotificationSubTypeWarmUpStarted 626 | } mutableCopy]; 627 | notePB.notificationDictionary = (__bridge CFMutableDictionaryRef)dict; 628 | 629 | ICDSendNotification( ¬ePB ); 630 | } 631 | 632 | - (void) doneWarmUpMessage 633 | { 634 | ICASendNotificationPB notePB = {}; 635 | NSMutableDictionary* dict = [@{ 636 | (id)kICANotificationICAObjectKey: @(self.scannerObjectInfo->icaObject), 637 | (id)kICANotificationTypeKey: (id)kICANotificationTypeDeviceStatusInfo, 638 | (id)kICANotificationSubTypeKey: (id)kICANotificationSubTypeWarmUpDone 639 | } mutableCopy]; 640 | notePB.notificationDictionary = (__bridge CFMutableDictionaryRef)dict; 641 | 642 | ICDSendNotification(¬ePB); 643 | } 644 | 645 | - (void) pageDoneMessage 646 | { 647 | ICASendNotificationPB notePB = {}; 648 | NSMutableDictionary* dict = [@{ 649 | (id)kICANotificationICAObjectKey: @(self.scannerObjectInfo->icaObject), 650 | (id)kICANotificationTypeKey: (id)kICANotificationTypeScannerPageDone, 651 | } mutableCopy]; 652 | notePB.notificationDictionary = (__bridge CFMutableDictionaryRef)dict; 653 | 654 | if (self.documentURL) 655 | ((__bridge NSMutableDictionary*)notePB.notificationDictionary)[(id)kICANotificationScannerDocumentNameKey] = [self.documentURL path]; 656 | 657 | 658 | ICDSendNotification( ¬ePB ); 659 | } 660 | 661 | - (void) scanDoneMessage 662 | { 663 | ICASendNotificationPB notePB = {}; 664 | NSMutableDictionary* dict = [@{ 665 | (id)kICANotificationICAObjectKey: @(self.scannerObjectInfo->icaObject), 666 | (id)kICANotificationTypeKey: (id)kICANotificationTypeScannerScanDone 667 | } mutableCopy]; 668 | notePB.notificationDictionary = (__bridge CFMutableDictionaryRef)dict; 669 | 670 | ICDSendNotification( ¬ePB ); 671 | } 672 | 673 | - (void) sendTransactionCanceledMessage 674 | { 675 | ICASendNotificationPB notePB = {}; 676 | NSMutableDictionary* dict = [@{ 677 | (id)kICANotificationICAObjectKey: @(self.scannerObjectInfo->icaObject), 678 | (id)kICANotificationTypeKey: (id)kICANotificationTypeTransactionCanceled 679 | } mutableCopy]; 680 | notePB.notificationDictionary = (__bridge CFMutableDictionaryRef)dict; 681 | 682 | ICDSendNotification( ¬ePB ); 683 | } 684 | 685 | @end 686 | 687 | @implementation CSSaneNetScanner (ICARawFile) 688 | 689 | - (void) createColorSpaceWithSaneParameters:(SANE_Parameters*)parameters 690 | { 691 | NSString* profilePath = [NSTemporaryDirectory() stringByAppendingFormat:@"vs-%d",getpid()]; 692 | 693 | self.colorSpace = ICDCreateColorSpace([self numberOfComponents] * parameters->depth, 694 | [self numberOfComponents], 695 | self.scannerObjectInfo->icaObject, 696 | (__bridge CFStringRef)(self.colorSyncMode), 697 | NULL, 698 | (char*)[profilePath fileSystemRepresentation]); 699 | } 700 | 701 | - (void) writeHeaderToFile:(NSFileHandle*)handle 702 | withSaneParameters:(SANE_Parameters*)parameters 703 | { 704 | ICARawFileHeader h; 705 | 706 | h.imageDataOffset = sizeof(ICARawFileHeader); 707 | h.version = 1; 708 | h.imageWidth = parameters->pixels_per_line; 709 | h.imageHeight = parameters->lines; 710 | h.bytesPerRow = parameters->bytes_per_line; 711 | h.bitsPerComponent = parameters->depth; 712 | h.bitsPerPixel = [self numberOfComponents] * parameters->depth; 713 | h.numberOfComponents = [self numberOfComponents]; 714 | h.cgColorSpaceModel = CGColorSpaceGetModel(self.colorSpace); 715 | h.bitmapInfo = kCGImageAlphaNone; 716 | h.dpi = 75; 717 | h.orientation = 1; 718 | strlcpy(h.colorSyncModeStr, [self.colorSyncMode UTF8String], sizeof(h.colorSyncModeStr)); 719 | 720 | [handle writeData:[NSData dataWithBytesNoCopy:&h 721 | length:sizeof(ICARawFileHeader) 722 | freeWhenDone:NO]]; 723 | } 724 | 725 | - (void) resaveRawFileAt:(NSURL*)url 726 | asType:(NSString*)type 727 | toURL:(NSURL*)destUrl 728 | saneParameters:(SANE_Parameters*)parameters 729 | { 730 | CGImageDestinationRef dest = CGImageDestinationCreateWithURL((__bridge CFURLRef)destUrl, (__bridge CFStringRef)type, 1, nil); 731 | CGDataProviderRef provider = [CSSequentialDataProvider createDataProviderWithFileAtURL:url 732 | andHardOffset:sizeof(ICARawFileHeader)]; 733 | 734 | CGImageRef image = CGImageCreate(parameters->pixels_per_line, 735 | parameters->lines, 736 | parameters->depth, 737 | [self numberOfComponents] * parameters->depth, 738 | parameters->bytes_per_line, 739 | self.colorSpace, 740 | kCGImageAlphaNone, 741 | provider, 742 | NULL, 743 | NO, kCGRenderingIntentDefault); 744 | 745 | CGImageDestinationAddImage(dest, image, nil); 746 | 747 | 748 | CGImageDestinationFinalize(dest); 749 | } 750 | 751 | @end 752 | --------------------------------------------------------------------------------