mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-24 02:51:22 +00:00
functionality change in non-GC mode, in GC mode it invokes a quick GC pass to try to delete the short-lived objects. Also deleted some [pool release] lines just before exit() or return-from-main statements. These cause objects to be swapped in and destructors to be run to no benefit (the OS will reclaim this memory without requiring stuff to be swapped in when the process exits). git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@33146 72102866-910b-0410-8b05-ffd578937521
1534 lines
40 KiB
Objective-C
1534 lines
40 KiB
Objective-C
/** <title>NSPrinter</title>
|
|
|
|
<abstract>Class representing a printer'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: July 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 Lesser 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
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; see the file COPYING.LIB.
|
|
If not, see <http://www.gnu.org/licenses/> or write to the
|
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#import <Foundation/NSAutoreleasePool.h>
|
|
#import <Foundation/NSArray.h>
|
|
#import <Foundation/NSDebug.h>
|
|
#import <Foundation/NSDictionary.h>
|
|
#import <Foundation/NSString.h>
|
|
#import <Foundation/NSBundle.h>
|
|
#import <Foundation/NSCharacterSet.h>
|
|
#import <Foundation/NSDictionary.h>
|
|
#import <Foundation/NSEnumerator.h>
|
|
#import <Foundation/NSException.h>
|
|
#import <Foundation/NSFileManager.h>
|
|
#import <Foundation/NSPathUtilities.h>
|
|
#import <Foundation/NSScanner.h>
|
|
#import <Foundation/NSSet.h>
|
|
#import <Foundation/NSString.h>
|
|
#import <Foundation/NSValue.h>
|
|
#import <Foundation/NSMapTable.h>
|
|
#import <Foundation/NSSet.h>
|
|
#import "AppKit/AppKitExceptions.h"
|
|
#import "AppKit/NSGraphics.h"
|
|
#import "AppKit/NSPrinter.h"
|
|
#import "GNUstepGUI/GSPrinting.h"
|
|
|
|
//
|
|
// Class variables:
|
|
//
|
|
|
|
//
|
|
// Class variables used during scanning:
|
|
//
|
|
|
|
// Character sets used in scanning.
|
|
static NSCharacterSet* newlineSet = nil;
|
|
static NSCharacterSet* keyEndSet = nil;
|
|
static NSCharacterSet* optKeyEndSet = nil;
|
|
static NSCharacterSet* valueEndSet = nil;
|
|
|
|
//Class variable to cache NSPrinters, without this they
|
|
//are created (and PPDs are parsed) ALL the time
|
|
static NSMutableDictionary* printerCache;
|
|
|
|
|
|
//
|
|
// Private methods used for PPD Parsing
|
|
//
|
|
@interface NSPrinter (PPDParsingPrivate)
|
|
|
|
-(void) loadPPDAtPath: (NSString*) PPDstring
|
|
symbolValues: (NSMutableDictionary*) ppdSymbolValues
|
|
inclusionSet: (NSMutableSet*) includeSet;
|
|
|
|
-(void) addPPDKeyword: (NSString*) mainKeyword
|
|
withScanner: (NSScanner*) PPDdata
|
|
withPPDPath: (NSString*) ppdPath;
|
|
|
|
-(void) addPPDUIConstraint: (NSScanner*) constraint
|
|
withPPDPath: (NSString*) ppdPath;
|
|
|
|
-(void) addPPDOrderDependency: (NSScanner*) dependency
|
|
withPPDPath: (NSString*) ppdPath;
|
|
|
|
-(id) addString: (NSString*) string
|
|
forKey: (NSString*) key
|
|
inTable: (NSString*) table;
|
|
|
|
-(void) addValue: (NSString*) value
|
|
andValueTranslation: (NSString*) valueTranslation
|
|
andOptionTranslation: (NSString*) optionTranslation
|
|
forKey: (NSString*) key;
|
|
|
|
-(NSString*) interpretQuotedValue: (NSString*) qString;
|
|
|
|
-(int) gethex: (unichar) character;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSPrinter
|
|
|
|
//
|
|
// Class methods
|
|
//
|
|
+(void) initialize
|
|
{
|
|
if (self == [NSPrinter class])
|
|
{
|
|
// Initial version
|
|
[self setVersion:1];
|
|
}
|
|
printerCache = RETAIN([NSMutableDictionary dictionary]);
|
|
}
|
|
|
|
/** 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
|
|
{
|
|
NSEnumerator *keyEnum;
|
|
NSString *key;
|
|
NSPrinter *printer;
|
|
|
|
//First, the cache has to be managed.
|
|
//Take into account any deleted printers.
|
|
keyEnum = [[printerCache allKeys] objectEnumerator];
|
|
while ((key = [keyEnum nextObject]))
|
|
{
|
|
NSEnumerator *namesEnum;
|
|
NSString *validName;
|
|
BOOL stillValid = NO;
|
|
|
|
namesEnum = [[self printerNames] objectEnumerator];
|
|
while ((validName = [namesEnum nextObject]))
|
|
{
|
|
if ([validName isEqualToString: key])
|
|
{
|
|
stillValid = YES;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (stillValid == NO)
|
|
{
|
|
[printerCache removeObjectForKey: key];
|
|
}
|
|
}
|
|
|
|
printer = [printerCache objectForKey: name];
|
|
|
|
if (printer)
|
|
{
|
|
return printer;
|
|
}
|
|
else
|
|
{
|
|
Class principalClass;
|
|
|
|
principalClass = [[GSPrinting printingBundle] principalClass];
|
|
|
|
if (principalClass == nil)
|
|
return nil;
|
|
|
|
printer = [[principalClass printerClass] printerWithName: name];
|
|
|
|
if (printer)
|
|
{
|
|
[printerCache setObject: printer
|
|
forKey: name];
|
|
}
|
|
return printer;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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
|
|
//
|
|
|
|
|
|
//
|
|
// 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;
|
|
|
|
result = [self stringForKey: @"Protocols"
|
|
inTable: @"PPD"];
|
|
if (!result)
|
|
return NO;
|
|
|
|
protocols = [NSScanner scannerWithString: result];
|
|
|
|
while (![protocols isAtEnd])
|
|
{
|
|
[protocols scanUpToCharactersFromSet: [NSCharacterSet whitespaceCharacterSet]
|
|
intoString: &result];
|
|
|
|
if ([result isEqual:@"BCP"])
|
|
return YES;
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
-(NSRect) imageRectForPaper: (NSString*) paperName
|
|
{
|
|
NSString *key;
|
|
|
|
key = [NSString stringWithFormat: @"ImageableArea/%@", paperName];
|
|
|
|
return [self rectForKey: key
|
|
inTable: @"PPD"];
|
|
}
|
|
|
|
-(NSSize) pageSizeForPaper: (NSString*) paperName
|
|
{
|
|
NSString *key;
|
|
|
|
key = [NSString stringWithFormat: @"PaperDimension/%@", paperName];
|
|
|
|
return [self sizeForKey: key
|
|
inTable: @"PPD"];
|
|
}
|
|
|
|
-(BOOL) isColor
|
|
{
|
|
return [self booleanForKey: @"ColorDevice"
|
|
inTable: @"PPD"];
|
|
}
|
|
|
|
-(BOOL) isFontAvailable: (NSString*) fontName
|
|
{
|
|
NSString *key;
|
|
|
|
key = [NSString stringWithFormat: @"Font/%@", fontName];
|
|
return [self isKey: key
|
|
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;
|
|
|
|
result = [self stringForKey: @"DefaultOutputOrder"
|
|
inTable: @"PPD"];
|
|
|
|
if (!result)
|
|
return NO;
|
|
|
|
if ([result caseInsensitiveCompare: @"REVERSE"] == NSOrderedSame)
|
|
return YES;
|
|
else
|
|
return NO;
|
|
}
|
|
|
|
//
|
|
// Querying the NSPrinter Tables
|
|
//
|
|
-(BOOL) booleanForKey: (NSString*) key
|
|
inTable: (NSString*) table
|
|
{
|
|
NSString *result;
|
|
result = [self stringForKey: key
|
|
inTable: table];
|
|
|
|
if (!result) //raise exception?
|
|
return NO;
|
|
|
|
if ([result caseInsensitiveCompare: @"TRUE"] == NSOrderedSame)
|
|
return YES;
|
|
else
|
|
return NO;
|
|
}
|
|
|
|
|
|
-(NSDictionary*) deviceDescription
|
|
{
|
|
NSMutableDictionary *result;
|
|
|
|
result = [NSMutableDictionary dictionary];
|
|
|
|
if ([self isKey: @"DefaultResolution"
|
|
inTable:@"PPD"])
|
|
{
|
|
int dpi = [self intForKey: @"DefaultResolution"
|
|
inTable: @"PPD"];
|
|
|
|
[result setObject: [NSNumber numberWithInt: dpi]
|
|
forKey: NSDeviceResolution];
|
|
}
|
|
|
|
if ([self isKey: @"ColorDevice"
|
|
inTable: @"PPD"])
|
|
{
|
|
BOOL color = [self booleanForKey: @"ColorDevice"
|
|
inTable: @"PPD"];
|
|
|
|
// FIXME: Should NSDeviceWhiteColorSpace be NSDeviceBlackColorSpace?
|
|
// FIXME #2: Are they calibrated?
|
|
// Basically I'm not sure which color spaces should be used...
|
|
if (color == YES)
|
|
{
|
|
[result setObject: NSDeviceCMYKColorSpace
|
|
forKey: NSDeviceColorSpaceName];
|
|
}
|
|
else
|
|
{
|
|
[result setObject: NSDeviceWhiteColorSpace
|
|
forKey: NSDeviceColorSpaceName];
|
|
}
|
|
}
|
|
|
|
if ([self isKey: @"DefaultBitsPerPixel"
|
|
inTable: @"PPD"])
|
|
{
|
|
int bits = [self intForKey: @"DefaultBitsPerPixel"
|
|
inTable: @"PPD"];
|
|
|
|
[result setObject: [NSNumber numberWithInt: bits]
|
|
forKey: NSDeviceBitsPerSample];
|
|
}
|
|
|
|
if ([self isKey: @"DefaultPageSize"
|
|
inTable: @"PPD"])
|
|
{
|
|
NSString* defaultPageSize = [self stringForKey: @"DefaultPageSize"
|
|
inTable: @"PPD"];
|
|
|
|
if (defaultPageSize)
|
|
{
|
|
NSSize paperSize = [self pageSizeForPaper: defaultPageSize];
|
|
|
|
[result setObject: [NSValue valueWithSize:paperSize]
|
|
forKey: NSDeviceSize];
|
|
}
|
|
}
|
|
|
|
[result setObject: [NSNumber numberWithBool:NO]
|
|
forKey: NSDeviceIsScreen];
|
|
|
|
[result setObject: [NSNumber numberWithBool:YES]
|
|
forKey: NSDeviceIsPrinter];
|
|
|
|
NSDebugMLLog(@"GSPrinting", @"Device Description: %@", [result description]);
|
|
return result;
|
|
}
|
|
|
|
|
|
-(float) floatForKey: (NSString*) key
|
|
inTable: (NSString*) table
|
|
{
|
|
NSString *result;
|
|
|
|
result = [self stringForKey: key
|
|
inTable: table];
|
|
|
|
if (!result) //raise exception?
|
|
return 0.0;
|
|
|
|
return [result floatValue];
|
|
}
|
|
|
|
|
|
-(int) intForKey: (NSString*) key
|
|
inTable: (NSString*) table
|
|
{
|
|
NSString *result;
|
|
|
|
result = [self stringForKey: key
|
|
inTable: table];
|
|
|
|
if (!result) //raise exception?
|
|
return 0;
|
|
|
|
return [result intValue];
|
|
}
|
|
|
|
|
|
-(NSRect) rectForKey: (NSString*) key
|
|
inTable: (NSString*) table
|
|
{
|
|
NSString *result;
|
|
NSScanner *bits;
|
|
float x1, y1, x2, y2;
|
|
|
|
result = [self stringForKey: key
|
|
inTable: table];
|
|
|
|
if (!result) //raise exception?
|
|
return NSZeroRect;
|
|
|
|
bits = [NSScanner scannerWithString: result];
|
|
if ([bits scanFloat: &x1] &&
|
|
[bits scanFloat: &y1] &&
|
|
[bits scanFloat: &x2] &&
|
|
[bits scanFloat: &y2])
|
|
{
|
|
return NSMakeRect(x1, y1, x2-x1, y2-y1);
|
|
}
|
|
return NSZeroRect;
|
|
}
|
|
|
|
-(NSSize) sizeForKey: (NSString*) key
|
|
inTable: (NSString*) table
|
|
{
|
|
NSString *result;
|
|
NSScanner *bits;
|
|
float x, y;
|
|
|
|
result = [self stringForKey: key
|
|
inTable: table];
|
|
|
|
if (!result) //raise exception?
|
|
return NSZeroSize;
|
|
|
|
bits = [NSScanner scannerWithString: result];
|
|
if ([bits scanFloat: &x] &&
|
|
[bits scanFloat: &y])
|
|
{
|
|
return NSMakeSize(x,y);
|
|
}
|
|
return NSZeroSize;
|
|
}
|
|
|
|
|
|
-(NSString*) stringForKey: (NSString*) key
|
|
inTable: (NSString*) table
|
|
{
|
|
NSArray *results;
|
|
|
|
results = [self stringListForKey: key
|
|
inTable: table];
|
|
|
|
if (results == nil)
|
|
return nil;
|
|
|
|
return [results objectAtIndex: 0];
|
|
}
|
|
|
|
-(NSArray*) stringListForKey: (NSString*) key
|
|
inTable: (NSString*) table
|
|
{
|
|
NSDictionary *tableObj;
|
|
NSMutableArray *result;
|
|
|
|
tableObj = [_tables objectForKey: table ];
|
|
|
|
if (tableObj == nil) //raise exception?
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
result = [tableObj objectForKey: key];
|
|
if ([[result objectAtIndex:0] isEqual:@""])
|
|
{
|
|
NSMutableArray *origResult = result;
|
|
result = [NSMutableArray array];
|
|
[result addObjectsFromArray: origResult];
|
|
[result removeObjectAtIndex: 0];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
-(NSPrinterTableStatus) statusForTable: (NSString*) table
|
|
{
|
|
NSDictionary *tableObj;
|
|
|
|
// Select correct table
|
|
tableObj = [_tables objectForKey: table];
|
|
|
|
if (tableObj == nil)
|
|
return NSPrinterTableNotFound;
|
|
else if (![tableObj isKindOfClass: [NSDictionary class]])
|
|
return NSPrinterTableError;
|
|
else
|
|
return NSPrinterTableOK;
|
|
}
|
|
|
|
|
|
-(BOOL) isKey: (NSString*) key
|
|
inTable: (NSString*) table
|
|
{
|
|
NSMutableDictionary *tableObj;
|
|
|
|
// Select correct table
|
|
tableObj = [_tables objectForKey: table];
|
|
|
|
if (tableObj == nil) //raise exception?
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
// And check it
|
|
if ([tableObj objectForKey: key] == nil)
|
|
return NO;
|
|
else
|
|
return YES;
|
|
}
|
|
|
|
//
|
|
// NSCoding protocol
|
|
//
|
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
|
{
|
|
if ([aCoder allowsKeyedCoding])
|
|
{
|
|
// TODO: Determine keys for NSPrinter.
|
|
}
|
|
else
|
|
{
|
|
[aCoder encodeObject: _printerHost];
|
|
[aCoder encodeObject: _printerName];
|
|
[aCoder encodeObject: _printerNote];
|
|
[aCoder encodeObject: _printerType];
|
|
[aCoder encodeObject: _tables];
|
|
}
|
|
}
|
|
|
|
- (id) initWithCoder: (NSCoder*)aDecoder
|
|
{
|
|
if ([aDecoder allowsKeyedCoding])
|
|
{
|
|
// TODO: Determine keys for NSPrinter.
|
|
}
|
|
else
|
|
{
|
|
_printerHost = [aDecoder decodeObject];
|
|
_printerName = [aDecoder decodeObject];
|
|
_printerNote = [aDecoder decodeObject];
|
|
_printerType = [aDecoder decodeObject];
|
|
_tables = [aDecoder decodeObject];
|
|
}
|
|
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);
|
|
|
|
_tables = RETAIN([NSMutableDictionary dictionary]);
|
|
|
|
return self;
|
|
}
|
|
|
|
//
|
|
// Deallocation of instance variables
|
|
//
|
|
-(void) dealloc
|
|
{
|
|
RELEASE(_printerHost);
|
|
RELEASE(_printerName);
|
|
RELEASE(_printerNote);
|
|
RELEASE(_printerType);
|
|
RELEASE(_tables);
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSPrinter (PPDParsing)
|
|
|
|
-(BOOL) parsePPDAtPath: (NSString*) ppdPath
|
|
{
|
|
NSAutoreleasePool* subpool;
|
|
NSMutableDictionary* ppdSymbolValues;
|
|
NSEnumerator* objEnum;
|
|
NSMutableArray* valArray;
|
|
|
|
//make sure the class variables for scanning are created
|
|
if (!newlineSet)
|
|
{
|
|
newlineSet = [NSCharacterSet characterSetWithCharactersInString: @"\n\r"];
|
|
RETAIN(newlineSet);
|
|
}
|
|
|
|
if (!keyEndSet)
|
|
{
|
|
keyEndSet = [NSCharacterSet characterSetWithCharactersInString: @"\n\r\t: "];
|
|
RETAIN(keyEndSet);
|
|
}
|
|
|
|
if (!optKeyEndSet)
|
|
{
|
|
optKeyEndSet = [NSCharacterSet characterSetWithCharactersInString: @"\n\r:/"];
|
|
RETAIN(optKeyEndSet);
|
|
}
|
|
|
|
if (!valueEndSet)
|
|
{
|
|
valueEndSet = [NSCharacterSet characterSetWithCharactersInString: @"\n\r/"];
|
|
RETAIN(valueEndSet);
|
|
}
|
|
|
|
|
|
|
|
[_tables setObject: [NSMutableDictionary dictionary]
|
|
forKey: @"PPD"];
|
|
|
|
[_tables setObject: [NSMutableDictionary dictionary]
|
|
forKey: @"PPDOptionTranslation"];
|
|
|
|
[_tables setObject: [NSMutableDictionary dictionary]
|
|
forKey: @"PPDArgumentTranslation"];
|
|
|
|
[_tables setObject: [NSMutableDictionary dictionary]
|
|
forKey: @"PPDOrderDependency"];
|
|
|
|
[_tables setObject: [NSMutableDictionary dictionary]
|
|
forKey: @"PPDUIConstraints"];
|
|
|
|
|
|
// Create a temporary autorelease pool, as many temporary objects are used
|
|
subpool = [[NSAutoreleasePool alloc] init];
|
|
|
|
|
|
// NB: There are some structure keywords (such as OpenUI/CloseUI) that may
|
|
// be repeated, but as yet are not used. Since they are structure keywords,
|
|
// they'll probably need special processing anyway, and so aren't
|
|
// added to this list.
|
|
|
|
// Create dictionary for temporary storage of symbol values
|
|
ppdSymbolValues = [NSMutableDictionary dictionary];
|
|
|
|
//The inclusion set keeps track of what PPD files have been *Include(d).
|
|
//If one comes up twice recursion has occurred and we stop it.
|
|
// And scan the PPD itself
|
|
[self loadPPDAtPath: ppdPath
|
|
symbolValues: ppdSymbolValues
|
|
inclusionSet: [NSMutableSet setWithCapacity:10]];
|
|
|
|
// Search the PPD dictionary for symbolvalues and substitute them.
|
|
objEnum = [[_tables objectForKey: @"PPD"] objectEnumerator];
|
|
while ((valArray = [objEnum nextObject]))
|
|
{
|
|
NSString *oldValue;
|
|
NSString *newValue;
|
|
int i, max;
|
|
|
|
max = [valArray count];
|
|
for (i=0 ; i < max ; i++)
|
|
{
|
|
oldValue = [valArray objectAtIndex: i];
|
|
if ([oldValue isKindOfClass: [NSString class]]
|
|
&& ![oldValue isEqual: @""]
|
|
&& [[oldValue substringToIndex: 1] isEqual: @"^"])
|
|
{
|
|
newValue = [ppdSymbolValues
|
|
objectForKey: [oldValue substringFromIndex: 1]];
|
|
|
|
if (!newValue)
|
|
{
|
|
[NSException raise: NSPPDParseException
|
|
format: @"Unknown symbol value, ^%@ in PPD file %@.ppd",
|
|
oldValue, ppdPath];
|
|
}
|
|
|
|
[valArray replaceObjectAtIndex: i
|
|
withObject: newValue];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Make sure all the required keys are present
|
|
//Too many PPDs don't pass the test....
|
|
/*
|
|
objEnum = [[NSArray arrayWithObjects: @"NickName",
|
|
@"ModelName",
|
|
@"PCFileName",
|
|
@"Product",
|
|
@"PSVersion",
|
|
@"FileVersion",
|
|
@"FormatVersion",
|
|
@"LanguageEncoding",
|
|
@"LanguageVersion",
|
|
@"PageSize",
|
|
@"PageRegion",
|
|
@"ImageableArea",
|
|
@"PaperDimension",
|
|
@"PPD-Adobe",
|
|
nil] objectEnumerator];
|
|
|
|
while ((checkVal = [objEnum nextObject]))
|
|
{
|
|
if (![self isKey: checkVal
|
|
inTable: @"PPD"])
|
|
{
|
|
[NSException raise:NSPPDParseException
|
|
format:@"Required keyword *%@ not found in PPD file %@.ppd",
|
|
checkVal, PPDPath];
|
|
}
|
|
}
|
|
*/
|
|
|
|
// Release the local autoreleasePool
|
|
[subpool drain];
|
|
|
|
|
|
//Sometimes it's good to see the tables...
|
|
/*
|
|
NSDebugMLLog(@"GSPrinting", @"\n\nPPD: %@\n\n",
|
|
[[_tables objectForKey: @"PPD"] description]);
|
|
|
|
NSDebugMLLog(@"GSPrinting", @"\n\nPPDOptionTranslation: %@\n\n",
|
|
[[_tables objectForKey: @"PPDOptionTranslation"] description]);
|
|
|
|
NSDebugMLLog(@"GSPrinting", @"\n\nPPDArgumentTranslation: %@\n\n",
|
|
[[_tables objectForKey: @"PPDArgumentTranslation"] description]);
|
|
|
|
NSDebugMLLog(@"GSPrinting", @"\n\nPPDOrderDependency: %@\n\n",
|
|
[[_tables objectForKey: @"PPDOrderDependency"] description]);
|
|
|
|
NSDebugMLLog(@"GSPrinting", @"\n\nPPDUIConstraints: %@\n\n",
|
|
[[_tables objectForKey: @"PPDUIConstraints"] description]);
|
|
*/
|
|
|
|
|
|
return YES;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSPrinter (PPDParsingPrivate)
|
|
|
|
|
|
-(void) loadPPDAtPath: (NSString*) ppdPath
|
|
symbolValues: (NSMutableDictionary*) ppdSymbolValues
|
|
inclusionSet: (NSMutableSet*) inclusionSet
|
|
{
|
|
NSString* ppdString;
|
|
NSScanner* ppdData;
|
|
NSString* keyword;
|
|
|
|
|
|
//See if this ppd has been processed before
|
|
if ([inclusionSet member: ppdPath])
|
|
{
|
|
//this ppd has been done already!
|
|
[NSException raise: NSPPDIncludeStackOverflowException
|
|
format: @"Recursive *Includes! PPD *Include stack: %@",
|
|
[[inclusionSet allObjects] description] ];
|
|
}
|
|
|
|
[inclusionSet addObject: ppdPath];
|
|
|
|
ppdString = [NSString stringWithContentsOfFile: ppdPath];
|
|
|
|
// Set up the scanner - Appending a newline means that it should be
|
|
// able to process the last line correctly
|
|
ppdData = [NSScanner scannerWithString:
|
|
[ppdString stringByAppendingString: @"\n"]];
|
|
|
|
[ppdData setCharactersToBeSkipped: [NSCharacterSet whitespaceCharacterSet]];
|
|
|
|
// Main processing starts here...
|
|
while (YES) //Only check for the end after accounting for whitespace
|
|
{
|
|
// Get to the start of a new keyword, skipping blank lines
|
|
[ppdData scanCharactersFromSet:
|
|
[NSCharacterSet whitespaceAndNewlineCharacterSet]
|
|
intoString: NULL];
|
|
|
|
//this could be the end...
|
|
if ([ppdData isAtEnd])
|
|
break;
|
|
|
|
// All new entries should starts '*'
|
|
if (![ppdData scanString: @"*"
|
|
intoString: NULL])
|
|
{
|
|
[NSException raise: NSPPDParseException
|
|
format: @"Line not starting with * in PPD file %@",
|
|
ppdPath];
|
|
}
|
|
|
|
// Skip lines starting '*%', '*End', '*SymbolLength', or '*SymbolEnd'
|
|
if ([ppdData scanString: @"%"
|
|
intoString: NULL]
|
|
|| [ppdData scanString: @"End" //if we get this there is problem, yes?
|
|
intoString: NULL]
|
|
|| [ppdData scanString: @"SymbolLength"
|
|
intoString: NULL]
|
|
|| [ppdData scanString: @"SymbolEnd" //if we get this there is problem, yes?
|
|
intoString: NULL])
|
|
{
|
|
[ppdData scanUpToCharactersFromSet: newlineSet
|
|
intoString: NULL];
|
|
continue;
|
|
}
|
|
|
|
// Read main keyword, up to a colon, space or newline
|
|
[ppdData scanUpToCharactersFromSet: keyEndSet
|
|
intoString: &keyword];
|
|
|
|
// Loop if there is no value section, these keywords are ignored
|
|
if ([ppdData scanCharactersFromSet: newlineSet
|
|
intoString: NULL])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Add the line to the relevant table
|
|
if ([keyword isEqual: @"OrderDependency"])
|
|
{
|
|
[self addPPDOrderDependency: ppdData
|
|
withPPDPath: ppdPath];
|
|
}
|
|
else if ([keyword isEqual: @"UIConstraints"])
|
|
{
|
|
[self addPPDUIConstraint: ppdData
|
|
withPPDPath: ppdPath];
|
|
}
|
|
else if ([keyword isEqual: @"Include"])
|
|
{
|
|
NSFileManager *fileManager;
|
|
NSString *fileName = nil;
|
|
NSString *path = nil;
|
|
|
|
fileManager = [NSFileManager defaultManager];
|
|
|
|
[ppdData scanString: @":"
|
|
intoString: NULL];
|
|
|
|
// Find the filename between two "s"
|
|
[ppdData scanString: @"\"" /*"*/
|
|
intoString: NULL];
|
|
|
|
[ppdData scanUpToString: @"\"" /*"*/
|
|
intoString: &fileName];
|
|
|
|
[ppdData scanString: @"\"" /*"*/
|
|
intoString: NULL];
|
|
|
|
//the fileName could be an absolute path or just a filename.
|
|
if ([fileManager fileExistsAtPath: fileName])
|
|
{
|
|
//it was absolute, we are done
|
|
path = fileName;
|
|
}
|
|
//it was not absolute. Check to see if it exists in the
|
|
//directory of this ppd
|
|
else if ([fileManager fileExistsAtPath:
|
|
[[ppdPath stringByDeletingLastPathComponent]
|
|
stringByAppendingPathComponent: fileName] ])
|
|
{
|
|
path = [[ppdPath stringByDeletingLastPathComponent]
|
|
stringByAppendingPathComponent: fileName];
|
|
}
|
|
else //could not find the *Include fileName
|
|
{
|
|
[NSException raise: NSPPDIncludeNotFoundException
|
|
format: @"Could not find *Included PPD file %@", path];
|
|
}
|
|
|
|
[self loadPPDAtPath: path
|
|
symbolValues: ppdSymbolValues
|
|
inclusionSet: inclusionSet];
|
|
}
|
|
else if ([keyword isEqual: @"SymbolValue"])
|
|
{
|
|
NSString *symbolName;
|
|
NSString *symbolVal;
|
|
|
|
if (![ppdData scanString: @"^"
|
|
intoString: NULL])
|
|
{
|
|
[NSException raise: NSPPDParseException
|
|
format:@"Badly formatted *SymbolValue in PPD file %@",
|
|
ppdPath];
|
|
}
|
|
|
|
[ppdData scanUpToString: @":"
|
|
intoString: &symbolName];
|
|
|
|
|
|
[ppdData scanString: @":"
|
|
intoString: NULL];
|
|
|
|
[ppdData scanString: @"\"" /*"*/
|
|
intoString: NULL];
|
|
|
|
[ppdData scanUpToString: @"\"" /*"*/
|
|
intoString: &symbolVal];
|
|
|
|
if (!symbolVal)
|
|
symbolVal = @"";
|
|
|
|
[ppdData scanString: @"\"" /*"*/
|
|
intoString: NULL];
|
|
|
|
[ppdSymbolValues setObject: symbolVal
|
|
forKey: symbolName];
|
|
}
|
|
else
|
|
{
|
|
[self addPPDKeyword: keyword
|
|
withScanner: ppdData
|
|
withPPDPath: ppdPath];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-(void) addPPDKeyword: (NSString*) mainKeyword
|
|
withScanner: (NSScanner*) ppdData
|
|
withPPDPath: (NSString*) ppdPath
|
|
{
|
|
NSArray *repKeys;
|
|
NSString* optionKeyword = nil;
|
|
NSString* optionTranslation = nil;
|
|
NSString* value = nil;
|
|
NSString* valueTranslation = nil;
|
|
|
|
// Array of Repeated Keywords (Appendix B of the PostScript Printer
|
|
// Description File Format Specification).
|
|
repKeys = [NSArray arrayWithObjects:@"Emulators",
|
|
@"Extensions",
|
|
@"FaxSupport",
|
|
//@"Include", (handled separately)
|
|
@"Message",
|
|
@"PrinterError",
|
|
@"Product",
|
|
@"Protocols",
|
|
@"PSVersion",
|
|
@"Source",
|
|
@"Status",
|
|
//@"UIConstraints", (handled separately)
|
|
// Even though this is not mentioned in the list of repeated keywords,
|
|
// it's often repeated anyway, so I'm putting it here.
|
|
@"InkName",
|
|
nil];
|
|
|
|
|
|
// Scan off any optionKeyword
|
|
[ppdData scanUpToCharactersFromSet: optKeyEndSet
|
|
intoString: &optionKeyword];
|
|
|
|
if ([ppdData scanCharactersFromSet: newlineSet
|
|
intoString: NULL])
|
|
{
|
|
[NSException raise: NSPPDParseException
|
|
format: @"Keyword has optional keyword but no value in PPD file %@",
|
|
ppdPath];
|
|
}
|
|
|
|
if ([ppdData scanString: @"/"
|
|
intoString: NULL])
|
|
{
|
|
// Option keyword translation exists - scan it
|
|
[ppdData scanUpToString: @":"
|
|
intoString: &optionTranslation];
|
|
}
|
|
|
|
[ppdData scanString: @":"
|
|
intoString: NULL];
|
|
|
|
// Read the value part
|
|
// Values starting with a " are read until the second ", ignoring \n etc.
|
|
|
|
if ([ppdData scanString: @"\"" /*"*/
|
|
intoString: NULL])
|
|
{
|
|
[ppdData scanUpToString: @"\"" /*"*/
|
|
intoString: &value];
|
|
|
|
if (!value)
|
|
value = @"";
|
|
|
|
[ppdData scanString: @"\"" /*"*/
|
|
intoString: NULL];
|
|
|
|
// It is a QuotedValue if it's in quotes, and there is no option
|
|
// key, or the main key is a *JCL keyword
|
|
if (!optionKeyword || [[mainKeyword substringToIndex:3]
|
|
isEqualToString: @"JCL"])
|
|
{
|
|
value = [self interpretQuotedValue: value];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, scan up to the end of line or '/'
|
|
[ppdData scanUpToCharactersFromSet: valueEndSet
|
|
intoString: &value];
|
|
}
|
|
// If there is a value translation, scan it
|
|
if ([ppdData scanString: @"/"
|
|
intoString: NULL])
|
|
{
|
|
[ppdData scanUpToCharactersFromSet: newlineSet
|
|
intoString: &valueTranslation];
|
|
}
|
|
|
|
// The translations also have to have any hex substrings interpreted
|
|
if (optionTranslation)
|
|
optionTranslation = [self interpretQuotedValue: optionTranslation];
|
|
|
|
if (valueTranslation)
|
|
valueTranslation = [self interpretQuotedValue: valueTranslation];
|
|
|
|
// The keyword (or keyword/option pair, if there's a option), should only
|
|
// only have one value, unless it's one of the optionless keywords which
|
|
// allow multiple instances.
|
|
// If a keyword is read twice, 'first instance is correct', according to
|
|
// the standard.
|
|
// Finally, add the strings to the tables
|
|
if (optionKeyword)
|
|
{
|
|
NSString *mainAndOptionKeyword;
|
|
|
|
mainAndOptionKeyword=[mainKeyword stringByAppendingFormat: @"/%@",
|
|
optionKeyword];
|
|
|
|
if ([self isKey: mainAndOptionKeyword
|
|
inTable: @"PPD"])
|
|
{
|
|
return;
|
|
}
|
|
|
|
[self addValue: value
|
|
andValueTranslation: valueTranslation
|
|
andOptionTranslation: optionTranslation
|
|
forKey: mainAndOptionKeyword];
|
|
|
|
// Deal with the oddities of stringForKey:inTable:
|
|
// If this method is used to find a keyword with options, using
|
|
// just the keyword it should return an empty string
|
|
// stringListForKey:inTable:, however, should return the list of
|
|
// option keywords.
|
|
// This is done by making the first item in the array an empty
|
|
// string, which will be skipped by stringListForKey:, if necessary
|
|
if (![[_tables objectForKey: @"PPD"] objectForKey: mainKeyword])
|
|
{
|
|
[self addString: @""
|
|
forKey: mainKeyword
|
|
inTable: @"PPD"];
|
|
|
|
[self addString: @""
|
|
forKey: mainKeyword
|
|
inTable: @"PPDOptionTranslation"];
|
|
|
|
[self addString: @""
|
|
forKey: mainKeyword
|
|
inTable: @"PPDArgumentTranslation"];
|
|
|
|
}
|
|
|
|
[self addValue: optionKeyword
|
|
andValueTranslation: optionKeyword
|
|
andOptionTranslation: optionKeyword
|
|
forKey: mainKeyword];
|
|
}
|
|
else
|
|
{
|
|
if ([self isKey: mainKeyword
|
|
inTable: @"PPD"] &&
|
|
![repKeys containsObject: mainKeyword])
|
|
{
|
|
return;
|
|
}
|
|
|
|
[self addValue: value
|
|
andValueTranslation: valueTranslation
|
|
andOptionTranslation: optionTranslation
|
|
forKey: mainKeyword];
|
|
}
|
|
}
|
|
|
|
|
|
-(void) addPPDUIConstraint: (NSScanner*) constraint
|
|
withPPDPath: (NSString*) ppdPath
|
|
{
|
|
NSString* mainKey1 = nil;
|
|
NSString* optionKey1 = nil;
|
|
NSString* mainKey2 = nil;
|
|
NSString* optionKey2 = nil;
|
|
|
|
// UIConstraint should have no option keyword
|
|
if (![constraint scanString: @":"
|
|
intoString: NULL])
|
|
{
|
|
[NSException raise:NSPPDParseException
|
|
format:@"UIConstraints has option keyword in PPD File %@",
|
|
ppdPath];
|
|
}
|
|
|
|
// Skip the '*'
|
|
[constraint scanString: @"*"
|
|
intoString: NULL];
|
|
|
|
// Scan the bits. Stuff not starting with * must be an optionKeyword
|
|
[constraint scanUpToCharactersFromSet: [NSCharacterSet whitespaceCharacterSet]
|
|
intoString: &mainKey1];
|
|
|
|
if (![constraint scanString: @"*"
|
|
intoString: NULL])
|
|
{
|
|
[constraint scanUpToCharactersFromSet: [NSCharacterSet whitespaceCharacterSet]
|
|
intoString: &optionKey1];
|
|
|
|
[constraint scanString: @"*"
|
|
intoString: NULL];
|
|
|
|
}
|
|
|
|
[constraint scanUpToCharactersFromSet:
|
|
[NSCharacterSet whitespaceAndNewlineCharacterSet]
|
|
intoString: &mainKey2];
|
|
|
|
if (![constraint scanCharactersFromSet: newlineSet
|
|
intoString: NULL])
|
|
{
|
|
[constraint scanUpToCharactersFromSet:
|
|
[NSCharacterSet whitespaceAndNewlineCharacterSet]
|
|
intoString: &optionKey2];
|
|
}
|
|
else
|
|
{
|
|
optionKey2 = @"";
|
|
}
|
|
|
|
// Add to table
|
|
if (optionKey1)
|
|
mainKey1 = [mainKey1 stringByAppendingFormat: @"/%@", optionKey1];
|
|
|
|
[self addString: mainKey2
|
|
forKey: mainKey1
|
|
inTable: @"PPDUIConstraints"];
|
|
|
|
[self addString: optionKey2
|
|
forKey: mainKey1
|
|
inTable: @"PPDUIConstraints"];
|
|
|
|
}
|
|
|
|
|
|
|
|
-(void) addPPDOrderDependency: (NSScanner*) dependency
|
|
withPPDPath: (NSString*) ppdPath
|
|
{
|
|
NSString *realValue = nil;
|
|
NSString *section = nil;
|
|
NSString *keyword = nil;
|
|
NSString *optionKeyword = nil;
|
|
|
|
// Order dependency should have no option keyword
|
|
if (![dependency scanString: @":"
|
|
intoString: NULL])
|
|
{
|
|
[NSException raise: NSPPDParseException
|
|
format:@"OrderDependency has option keyword in PPD file %@",
|
|
ppdPath];
|
|
}
|
|
|
|
[dependency scanUpToCharactersFromSet: [NSCharacterSet whitespaceCharacterSet]
|
|
intoString: &realValue];
|
|
|
|
[dependency scanUpToCharactersFromSet: [NSCharacterSet whitespaceCharacterSet]
|
|
intoString: §ion];
|
|
|
|
[dependency scanString: @"*"
|
|
intoString: NULL];
|
|
|
|
[dependency scanUpToCharactersFromSet:
|
|
[NSCharacterSet whitespaceAndNewlineCharacterSet]
|
|
intoString: &keyword];
|
|
|
|
if (![dependency scanCharactersFromSet: newlineSet
|
|
intoString: NULL])
|
|
{
|
|
// Optional keyword exists
|
|
[dependency scanUpToCharactersFromSet:
|
|
[NSCharacterSet whitespaceAndNewlineCharacterSet]
|
|
intoString: &optionKeyword];
|
|
}
|
|
|
|
// Go to next line of PPD file
|
|
[dependency scanCharactersFromSet: newlineSet
|
|
intoString: NULL];
|
|
|
|
// Add to table
|
|
if (optionKeyword)
|
|
keyword = [keyword stringByAppendingFormat: @"/%@", optionKeyword];
|
|
|
|
[self addString: realValue
|
|
forKey: keyword
|
|
inTable: @"PPDOrderDependency"];
|
|
|
|
[self addString: section
|
|
forKey: keyword
|
|
inTable: @"PPDOrderDependency"];
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Adds the various values to the relevant tables, for the given key
|
|
//
|
|
-(void) 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"];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Adds the string to the array of strings.
|
|
// Or creates the array if it does not exist and adds the string
|
|
//
|
|
-(id) addString: (NSString*) string
|
|
forKey: (NSString*) key
|
|
inTable: (NSString*) table
|
|
{
|
|
NSMutableDictionary *tableObj;
|
|
NSMutableArray *array;
|
|
|
|
tableObj = [_tables objectForKey: table];
|
|
|
|
if (tableObj == nil)
|
|
NSDebugMLLog(@"GSPrinting", @"Could not find table %@!", table);
|
|
|
|
array = (NSMutableArray*)[tableObj objectForKey:key];
|
|
|
|
if (array == nil) //it does not exist, create it
|
|
{
|
|
array = [NSMutableArray array];
|
|
[tableObj setObject: array
|
|
forKey: key];
|
|
}
|
|
|
|
[array addObject: string];
|
|
|
|
return self;
|
|
}
|
|
|
|
|
|
|
|
// Function to convert hexadecimal substrings
|
|
-(NSString*) interpretQuotedValue: (NSString*) qString
|
|
{
|
|
NSScanner *scanner;
|
|
NSCharacterSet *emptySet;
|
|
NSString *value = nil;
|
|
NSString *part;
|
|
int stringLength;
|
|
int location;
|
|
NSRange range;
|
|
|
|
// Don't bother unless there's something to convert
|
|
range = [qString rangeOfString: @"<"];
|
|
if (!range.length)
|
|
return qString;
|
|
|
|
scanner = [NSScanner scannerWithString: qString];
|
|
emptySet = [NSCharacterSet characterSetWithCharactersInString: @""];
|
|
[scanner setCharactersToBeSkipped: emptySet];
|
|
|
|
if (![scanner scanUpToString: @"<"
|
|
intoString: &value])
|
|
{
|
|
value = [NSString string];
|
|
}
|
|
|
|
stringLength = [qString length];
|
|
|
|
while (![scanner isAtEnd])
|
|
{
|
|
[scanner scanString: @"<"
|
|
intoString: NULL];
|
|
|
|
// "<<" is a valid part of a PS string
|
|
if ([scanner scanString: @"<"
|
|
intoString: NULL])
|
|
{
|
|
value = [value stringByAppendingString: @"<<"];
|
|
}
|
|
else
|
|
{
|
|
[scanner scanCharactersFromSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]
|
|
intoString: NULL];
|
|
|
|
while (![scanner scanString: @">"
|
|
intoString: NULL])
|
|
{
|
|
location = [scanner scanLocation];
|
|
if (location+2 > stringLength)
|
|
{
|
|
[NSException raise: NSPPDParseException
|
|
format: @"Badly formatted hexadecimal substring '%@' in \
|
|
PPD printer file.", qString];
|
|
// NOT REACHED
|
|
}
|
|
value = [value stringByAppendingFormat: @"%c",
|
|
16 * [self gethex: [qString characterAtIndex: location]]
|
|
+ [self gethex: [qString characterAtIndex: location+1]]];
|
|
|
|
[scanner setScanLocation: location+2];
|
|
|
|
[scanner scanCharactersFromSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]
|
|
intoString: NULL];
|
|
}
|
|
}
|
|
|
|
if ([scanner scanUpToString:@"<" intoString:&part])
|
|
{
|
|
value = [value stringByAppendingString: part];
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
// Convert a character to a value between 0 and 15
|
|
-(int) gethex: (unichar) character
|
|
{
|
|
switch (character)
|
|
{
|
|
case '0': return 0;
|
|
case '1': return 1;
|
|
case '2': return 2;
|
|
case '3': return 3;
|
|
case '4': return 4;
|
|
case '5': return 5;
|
|
case '6': return 6;
|
|
case '7': return 7;
|
|
case '8': return 8;
|
|
case '9': return 9;
|
|
case 'A': return 10;
|
|
case 'B': return 11;
|
|
case 'C': return 12;
|
|
case 'D': return 13;
|
|
case 'E': return 14;
|
|
case 'F': return 15;
|
|
case 'a': return 10;
|
|
case 'b': return 11;
|
|
case 'c': return 12;
|
|
case 'd': return 13;
|
|
case 'e': return 14;
|
|
case 'f': return 15;
|
|
}
|
|
[NSException
|
|
raise: NSPPDParseException
|
|
format: @"Badly formatted hexadeximal character '%d' in PPD printer file.",
|
|
character];
|
|
|
|
return 0; /* Quiet compiler warnings */
|
|
}
|
|
|
|
@end
|