libs-gui/Source/NSPrinter.m
Chad Hardin 989ae0cdd8 Backend printing bundle system changes
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@19705 72102866-910b-0410-8b05-ffd578937521
2004-07-10 10:19:34 +00:00

729 lines
20 KiB
Objective-C

/** <title>NSPrinter</title>
<abstract>Class representing a printer's or printer model's capabilities.</abstract>
Copyright (C) 1996, 1997, 2004 Free Software Foundation, Inc.
Authors: Simon Frankau <sgf@frankau.demon.co.uk>
Date: June 1997
Modified for Printing Backend Support
Author: Chad Hardin <cehardin@mac.com>
Date: June 2004
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
If you are interested in a warranty or support for this source code,
contact Scott Christley <scottc@net-community.com> for more information.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
/* NB:
* There are a few FIXMEs in the functionality left.
* Parsing of the PPDs is somewhat suboptimal.
* (I think it's best to leave optimisation until more of GNUstep is done).
* The *OpenUI, *CloseUI, *OpenGroup and *CloseGroup are not processed.
* (This is not required in the OpenStep standard, but could be useful).
*/
#include "config.h"
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSString.h>
#include <Foundation/NSBundle.h>
#include <Foundation/NSCharacterSet.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSException.h>
#include <Foundation/NSFileManager.h>
#include <Foundation/NSPathUtilities.h>
#include <Foundation/NSScanner.h>
#include <Foundation/NSString.h>
#include <Foundation/NSUtilities.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSMapTable.h>
#include <Foundation/NSSet.h>
#include "AppKit/AppKitExceptions.h"
#include "AppKit/NSGraphics.h"
#include "AppKit/NSPrinter.h"
#include "GNUstepGUI/GSPrinting.h"
@implementation NSPrinter
//
// Class methods
//
+ (void)initialize
{
if (self == [NSPrinter class])
{
// Initial version
[self setVersion:1];
}
}
/** Load the appropriate bundle for the Printer
(eg: GSLPRPrinter, GSCUPSPrinter).
*/
+ (id) allocWithZone: (NSZone*) zone
{
Class principalClass;
principalClass = [[GSPrinting printingBundle] principalClass];
if( principalClass == nil )
return nil;
return [[principalClass printerClass] allocWithZone: zone];
}
//
// Finding an NSPrinter
//
+ (NSPrinter *)printerWithName:(NSString *)name
{
Class principalClass;
principalClass = [[GSPrinting printingBundle] principalClass];
if( principalClass == nil )
return nil;
return [[principalClass printerClass] printerWithName: name];
}
//
// This now different than the OpenStep spec and instead
// follows the more useful implementation Apple choosed. In
// OpenStep, this method would read a PPD and return a NSPrinter
// based upon values from that PPD, regardless if that printer
// was actually avaiable for use or not. On the contrary, Apple's
// implementation looks
// at all avaiable printers and returns one that has the same
// type. The reason for this is because they use CUPS. CUPS
// does not work by maintaining a repository of PPDs. Instead,
// the CUPS server trasnmits PPDs as they are needed, and only
// for actual real printers. Since we cannot know how the backend
// bundles will be handling their PPDs, or if they will even be using
// PPDs for that matter, (a Win32 printing backend, for example),
// I've choosen to go with Apple's implementation. Really, I see
// little use in creating a NSPrinter for a printer that is not
// available for use in the first place, I am open for commments
// on this, of course.
+ (NSPrinter *)printerWithType:(NSString *)type
{
NSEnumerator *printerNamesEnum;
NSString *printerName;
printerNamesEnum = [[self printerNames] objectEnumerator];
while( (printerName = [printerNamesEnum nextObject]) )
{
NSPrinter *printer;
printer = [self printerWithName: printerName];
if( [[printer type] isEqualToString: type] )
{
return printer;
}
}
return nil;
}
+ (NSArray *)printerNames
{
Class principalClass;
principalClass = [[GSPrinting printingBundle] principalClass];
if( principalClass == nil )
return nil;
return [[principalClass printerClass] printerNames];
}
// See note at +(NSPrinter *)printerWithType:(NSString *)type
+ (NSArray *)printerTypes
{
NSMutableSet *printerTypes;
NSEnumerator *printerNamesEnum;
NSString *printerName;
NSPrinter *printer;
printerTypes = [NSMutableSet setWithCapacity:1];
printerNamesEnum = [[self printerNames] objectEnumerator];
while( (printerName = [printerNamesEnum nextObject]) )
{
printer = [self printerWithName: printerName];
[printerTypes addObject: [printer type]];
}
return [printerTypes allObjects];
}
//
// Instance methods
//
//
// Deallocation of instance variables
//
- (void)dealloc
{
RELEASE(_printerHost);
RELEASE(_printerName);
RELEASE(_printerNote);
RELEASE(_printerType);
RELEASE(_PPD);
RELEASE(_PPDOptionTranslation);
RELEASE(_PPDArgumentTranslation);
RELEASE(_PPDOrderDependency);
RELEASE(_PPDUIConstraints);
[super dealloc];
}
//
// Printer Attributes
//
- (NSString *)host
{
return _printerHost;
}
- (NSString *)name
{
return _printerName;
}
- (NSString *)note
{
return _printerNote;
}
- (NSString *)type
{
return _printerType;
}
//
// Retrieving Specific Information
//
- (BOOL)acceptsBinary
{
// FIXME: I'm not sure if acceptsBinary is the same as BCP protocol?
NSString *result;
NSScanner *protocols;
NSCharacterSet *whitespace;
if (_cacheAcceptsBinary != -1)
return _cacheAcceptsBinary;
result = [self stringForKey:@"Protocols" inTable:@"PPD"];
if (!result)
{
_cacheAcceptsBinary = NO;
return NO;
}
protocols = [NSScanner scannerWithString:result];
whitespace = [NSCharacterSet whitespaceCharacterSet];
while(![protocols isAtEnd])
{
[protocols scanUpToCharactersFromSet:whitespace intoString:&result];
if ([result isEqual:@"BCP"])
{
_cacheAcceptsBinary = YES;
return YES;
}
}
_cacheAcceptsBinary = NO;
return NO;
}
- (NSRect)imageRectForPaper:(NSString *)paperName
{
return [self rectForKey:[NSString
stringWithFormat:@"ImageableArea/%@", paperName]
inTable:@"PPD"];
}
- (NSSize)pageSizeForPaper:(NSString *)paperName
{
return [self sizeForKey:[NSString
stringWithFormat:@"PaperDimension/%@", paperName]
inTable:@"PPD"];
}
- (BOOL)isColor
{
return [self booleanForKey:@"ColorDevice" inTable:@"PPD"];
}
- (BOOL)isFontAvailable:(NSString *)fontName
{
return [self isKey:[NSString stringWithFormat:@"Font/%@", fontName]
inTable:@"PPD"];
}
- (int)languageLevel
{
return [self intForKey:@"LanguageLevel" inTable:@"PPD"];
}
- (BOOL)isOutputStackInReverseOrder
{
// FIXME: Is this what is needed? I'm not sure how this is worked out.
NSString *result;
if (_cacheOutputOrder != -1)
return _cacheOutputOrder;
result = [self stringForKey:@"DefaultOutputOrder" inTable:@"PPD"];
if (!result)
{
_cacheOutputOrder = NO;
return NO;
}
if ([result isEqual:@"Reverse"])
{
_cacheOutputOrder = YES;
return YES;
}
_cacheOutputOrder = NO;
return NO;
}
//
// Querying the NSPrinter Tables
//
/*
* Caching of converted values:
* To speed up retrieving non-string values, once they are read and converted
* they are then cached. Since these use the converted value of a string in
* whatever table they are in, this assumes there is only one string
* associated with the key, and so stringListForKey will not be used. The
* second place in the array attached for the key is therefore used to cache
* the information, and generally assumptions are made which are Bad Things,
* but it seems quicker and simpler than other ways and shouldn't go wrong in
* normal use (?).
*/
- (BOOL)booleanForKey:(NSString *)key
inTable:(NSString *)table
{
NSMutableArray *result;
result = (NSMutableArray *)[self stringListForKey:key inTable:table];
if (!result)
return NO;
if ([result count] == 2)
{
// Retrieve cached result
return [(NSNumber *)[result objectAtIndex:1] boolValue];
}
if ([[result objectAtIndex:0] isEqual:@"True"])
{
// Cache result
[result addObject:[NSNumber numberWithBool:YES]];
return YES;
}
// Cache result
[result addObject:[NSNumber numberWithBool:NO]];
return NO;
}
- (NSDictionary *)deviceDescription
{
/* FIXME: This is all rather dodgy - I don't have detailed information.
* I think I'll wait until NSWindow's deviceDescription is
* implemented, and then use that, since I'm not sure as to what sort
* of objects the values should be, and it would be nice to get the
* deviceDescriptions methods to match in the way they work.
*/
NSDictionary *result;
int dpi = [self intForKey:@"DefaultResolution" inTable:@"PPD"];
BOOL color = [self booleanForKey:@"ColorDevice" inTable:@"PPD"];
NSString *colorSpaceName;
int bits = [self intForKey:@"DefaultBitsPerPixel" inTable:@"PPD"];
NSSize paperSize = [self pageSizeForPaper:
[self stringForKey:@"DefaultPageSize"
inTable:@"PPD"]];
// Guess 300 dpi
if (!dpi)
dpi = 300;
// FIXME: Should NSDeviceWhiteColorSpace be NSDeviceBlackColorSpace?
// FIXME #2: Are they calibrated?
// Basically I'm not sure which color spaces should be used...
if (color)
colorSpaceName = NSDeviceCMYKColorSpace;
else
colorSpaceName = NSDeviceWhiteColorSpace;
if (!bits) // Either not found, or 'None'
bits=1;
// If the paper size wasn't found, try Letter
if (!(paperSize.width && paperSize.height))
paperSize = NSMakeSize(612,792);
// Create the dictionary...
result = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:dpi], NSDeviceResolution,
colorSpaceName, NSDeviceColorSpaceName,
[NSNumber numberWithInt:bits], NSDeviceBitsPerSample,
[NSNumber numberWithBool:NO], NSDeviceIsScreen,
[NSNumber numberWithBool:YES], NSDeviceIsPrinter,
[NSValue valueWithSize:paperSize], NSDeviceSize,
nil];
return result;
}
- (float)floatForKey:(NSString *)key
inTable:(NSString *)table
{
NSMutableArray *result;
float number;
result = (NSMutableArray *)[self stringListForKey:key inTable:table];
if (!result)
return 0.0;
if ([result count] == 2)
{
// Retrieve cached result
return [(NSNumber *)[result objectAtIndex:1] floatValue];
}
number = [(NSString *)[result objectAtIndex:0] floatValue];
// Cache result
[result addObject:[NSNumber numberWithFloat:number]];
// And return it
return number;
}
- (int)intForKey:(NSString *)key
inTable:(NSString *)table
{
NSMutableArray *result;
int number;
result = (NSMutableArray *)[self stringListForKey:key inTable:table];
if (!result)
return 0;
if ([result count] == 2)
{
// Retrieve cached result
return [(NSNumber *)[result objectAtIndex:1] intValue];
}
number = [(NSString *)[result objectAtIndex:0] intValue];
// Cache result
[result addObject:[NSNumber numberWithInt:number]];
// And return it
return number;
}
- (NSRect)rectForKey:(NSString *)key
inTable:(NSString *)table
{
NSMutableArray *result;
NSScanner *bits;
float x1, y1, x2, y2;
NSRect rectangle;
result = (NSMutableArray *)[self stringListForKey:key inTable:table];
if (!result)
return NSZeroRect;
if ([result count] == 2)
{
// Retrieve cached result
return [(NSValue *)[result objectAtIndex:1] rectValue];
}
bits = [NSScanner scannerWithString:[result objectAtIndex:0]];
if ([bits scanFloat:&x1] &&
[bits scanFloat:&y1] &&
[bits scanFloat:&x2] &&
[bits scanFloat:&y2])
{
rectangle = NSMakeRect(x1, y1, x2-x1, y2-y1);
// Cache result
[result addObject:[NSValue valueWithRect:rectangle]];
// And return it
return rectangle;
}
return NSZeroRect;
}
- (NSSize)sizeForKey:(NSString *)key
inTable:(NSString *)table
{
NSMutableArray *result;
NSScanner *bits;
float x, y;
NSSize size;
result = (NSMutableArray *)[self stringListForKey:key inTable:table];
if (!result)
return NSZeroSize;
if ([result count] == 2)
{
// Retrieve cached result
return [(NSValue *)[result objectAtIndex:1] sizeValue];
}
bits = [NSScanner scannerWithString:[result objectAtIndex:0]];
if ([bits scanFloat:&x] &&
[bits scanFloat:&y])
{
size = NSMakeSize(x,y);
// Cache result
[result addObject:[NSValue valueWithSize:size]];
// And return it
return size;
}
return NSZeroSize;
}
- (NSString *)stringForKey:(NSString *)key
inTable:(NSString *)table
{
NSMutableDictionary *checkMe = nil;
NSMutableArray *result;
// Select correct table
if ([table isEqual:@"PPD"])
checkMe = _PPD;
else if ([table isEqual:@"PPDOptionTranslation"])
checkMe = _PPDOptionTranslation;
else if ([table isEqual:@"PPDArgumentTranslation"])
checkMe = _PPDArgumentTranslation;
else if ([table isEqual:@"PPDOrderDependency"])
checkMe = _PPDOrderDependency;
else if ([table isEqual:@"PPDUIConstraints"])
checkMe = _PPDUIConstraints;
else
{
[NSException raise:NSGenericException
format:@"Could not find PPD table %@", table];
// NOT REACHED
}
// And check it
result = [checkMe objectForKey:key];
if (!result)
// Not found
return nil;
return [result objectAtIndex:0];
}
- (NSArray *)stringListForKey:(NSString *)key
inTable:(NSString *)table
{
NSMutableDictionary *checkMe = nil;
NSMutableArray *result;
// Select correct Table
if ([table isEqual:@"PPD"])
checkMe = _PPD;
else if ([table isEqual:@"PPDOptionTranslation"])
checkMe = _PPDOptionTranslation;
else if ([table isEqual:@"PPDArgumentTranslation"])
checkMe = _PPDArgumentTranslation;
else if ([table isEqual:@"PPDOrderDependency"])
checkMe = _PPDOrderDependency;
else if ([table isEqual:@"PPDUIConstraints"])
checkMe = _PPDUIConstraints;
else
{
[NSException raise:NSGenericException
format:@"Could not find PPD table %@", table];
// NOT REACHED
}
// And check it
result = [checkMe objectForKey:key];
if (!result)
// Not found
return nil;
if ([[result objectAtIndex:0] isEqual:@""])
{
NSMutableArray *oldResult = result;
result = [NSMutableArray array];
[result addObjectsFromArray:oldResult];
[result removeObjectAtIndex:0];
}
return result;
}
- (NSPrinterTableStatus)statusForTable:(NSString *)table
{
NSMutableDictionary *checkMe;
// Select correct table
if ([table isEqual:@"PPD"])
checkMe = _PPD;
else if ([table isEqual:@"PPDOptionTranslation"])
checkMe = _PPDOptionTranslation;
else if ([table isEqual:@"PPDArgumentTranslation"])
checkMe = _PPDArgumentTranslation;
else if ([table isEqual:@"PPDOrderDependency"])
checkMe = _PPDOrderDependency;
else if ([table isEqual:@"PPDUIConstraints"])
checkMe = _PPDUIConstraints;
else
return NSPrinterTableNotFound;
if (checkMe)
return NSPrinterTableOK;
// Shouldn't happen!
return NSPrinterTableError;
}
- (BOOL)isKey:(NSString *)key
inTable:(NSString *)table
{
NSMutableDictionary *checkMe = nil;
NSMutableArray *result;
// Select correct table
if ([table isEqual:@"PPD"])
checkMe = _PPD;
else if ([table isEqual:@"PPDOptionTranslation"])
checkMe = _PPDOptionTranslation;
else if ([table isEqual:@"PPDArgumentTranslation"])
checkMe = _PPDArgumentTranslation;
else if ([table isEqual:@"PPDOrderDependency"])
checkMe = _PPDOrderDependency;
else if ([table isEqual:@"PPDUIConstraints"])
checkMe = _PPDUIConstraints;
else
{
[NSException raise:NSGenericException
format:@"Could not find PPD table %@", table];
// NOT REACHED
}
// And check it
result = [checkMe objectForKey:key];
if (!result)
// Not found
return NO;
return YES;
}
//
// NSCoding protocol
//
- (void) encodeWithCoder: (NSCoder*)aCoder
{
// [super encodeWithCoder:aCoder];
[aCoder encodeObject: _printerHost];
[aCoder encodeObject: _printerName];
[aCoder encodeObject: _printerNote];
[aCoder encodeObject: _printerType];
[aCoder encodeValueOfObjCType: @encode(int) at: &_cacheAcceptsBinary];
[aCoder encodeValueOfObjCType: @encode(int) at: &_cacheOutputOrder];
[aCoder encodeObject: _PPD];
[aCoder encodeObject: _PPDOptionTranslation];
[aCoder encodeObject: _PPDArgumentTranslation];
[aCoder encodeObject: _PPDOrderDependency];
[aCoder encodeObject: _PPDUIConstraints];
}
- (id) initWithCoder: (NSCoder*)aDecoder
{
// self = [super initWithCoder:aDecoder];
[aDecoder decodeValueOfObjCType: @encode(id) at: &_printerHost];
[aDecoder decodeValueOfObjCType: @encode(id) at: &_printerName];
[aDecoder decodeValueOfObjCType: @encode(id) at: &_printerNote];
[aDecoder decodeValueOfObjCType: @encode(id) at: &_printerType];
[aDecoder decodeValueOfObjCType: @encode(int) at: &_cacheAcceptsBinary];
[aDecoder decodeValueOfObjCType: @encode(int) at: &_cacheOutputOrder];
[aDecoder decodeValueOfObjCType: @encode(id) at: &_PPD];
[aDecoder decodeValueOfObjCType: @encode(id) at: &_PPDOptionTranslation];
[aDecoder decodeValueOfObjCType: @encode(id) at: &_PPDArgumentTranslation];
[aDecoder decodeValueOfObjCType: @encode(id) at: &_PPDOrderDependency];
[aDecoder decodeValueOfObjCType: @encode(id) at: &_PPDUIConstraints];
return self;
}
@end
///
///Private implementation of routines that will be usefull
///for the printing backend bundles that subclass us.
///
@implementation NSPrinter (Private)
//
// Initialisation method used by backend bundles
//
-(id) initWithName:(NSString *)name
withType:(NSString *)type
withHost:(NSString *)host
withNote:(NSString *)note
{
self = [super init];
// Initialise instance variables
ASSIGN(_printerName, name);
ASSIGN(_printerType, type);
ASSIGN(_printerHost, host);
ASSIGN(_printerNote, note);
_cacheAcceptsBinary = _cacheOutputOrder = -1;
_PPD = RETAIN([NSMutableDictionary dictionary]);
_PPDOptionTranslation = RETAIN([NSMutableDictionary dictionary]);
_PPDArgumentTranslation = RETAIN([NSMutableDictionary dictionary]);
_PPDOrderDependency = RETAIN([NSMutableDictionary dictionary]);
_PPDUIConstraints = RETAIN([NSMutableDictionary dictionary]);
return self;
}
//
// Adds the various values to the relevant tables, for the given key
//
- addValue:(NSString *)value
andValueTranslation:(NSString *)valueTranslation
andOptionTranslation:(NSString *)optionTranslation
forKey:(NSString *)key
{
[self addString:value forKey:key inTable:_PPD];
if (valueTranslation)
[self addString:valueTranslation forKey:key
inTable:_PPDArgumentTranslation];
if (optionTranslation)
[self addString:optionTranslation forKey:key
inTable:_PPDOptionTranslation];
return self;
}
//
// Adds the string to the array of strings
//
- addString:(NSString *)string
forKey:(NSString *)key
inTable:(NSMutableDictionary *)table
{
NSMutableArray *array;
array = (NSMutableArray *)[table objectForKey:key];
if (array)
// Add string to existing array
[array addObject:string];
else
// Create the array if it does not exist
[table setObject:[NSMutableArray arrayWithObject:string] forKey:key];
return self;
}
@end