mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-25 01:01:10 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@11788 72102866-910b-0410-8b05-ffd578937521
1359 lines
40 KiB
Objective-C
1359 lines
40 KiB
Objective-C
/** <title>NSPrinter</title>
|
|
|
|
<abstract>Class representing a printer's or printer model's capabilities.</abstract>
|
|
|
|
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
|
|
|
Authors: Simon Frankau <sgf@frankau.demon.co.uk>
|
|
Date: June 1997
|
|
|
|
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 <gnustep/gui/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 <AppKit/AppKitExceptions.h>
|
|
#include <AppKit/NSGraphics.h>
|
|
|
|
#include <AppKit/NSPrinter.h>
|
|
|
|
|
|
// Define size used for the name and type maps - just use a small table
|
|
#define NAMEMAPSIZE 0
|
|
#define TYPEMAPSIZE 0
|
|
|
|
// The maximum level of nesting of *Include directives
|
|
#define MAX_PPD_INCLUDES 4
|
|
|
|
// A macro to skip whitespace over lines
|
|
#define skipSpace(x) [x scanCharactersFromSet:\
|
|
[NSCharacterSet whitespaceAndNewlineCharacterSet]\
|
|
intoString:NULL]
|
|
|
|
static NSString *NSPrinter_PATH = @"PrinterTypes";
|
|
static NSString *NSPrinter_INDEXFILE = @"Printers";
|
|
|
|
//
|
|
// Class variables:
|
|
//
|
|
|
|
// Maps holding NSPrinters with the types of printers, and the real printers
|
|
static NSMapTable *typeMap = NULL;
|
|
static NSMapTable *nameMap = NULL;
|
|
// Dictionary of real printers, from which NSPrinters can be made
|
|
static NSDictionary *nameDict = nil;
|
|
// An array to cache the available printer types
|
|
static NSArray *printerTypesAvailable = nil;
|
|
|
|
//
|
|
// 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;
|
|
// Array of Repeated Keywords (Appendix B of the PostScript Printer
|
|
// Description File Format Specification).
|
|
static NSArray *repKeys = nil;
|
|
// Array to collect the values of symbol values in.
|
|
static NSMutableDictionary *PPDSymbolValues;
|
|
// File name of the file being processed
|
|
static NSString *PPDFileName;
|
|
|
|
#ifndef LIB_FOUNDATION_LIBRARY
|
|
|
|
static void __NSRetainNothing(void *table, const void *anObject)
|
|
{
|
|
}
|
|
|
|
static void __NSReleaseNothing(void *table, void *anObject)
|
|
{
|
|
}
|
|
|
|
static NSString* __NSDescribeObjects(void *table, const void *anObject)
|
|
{
|
|
return [(NSObject*)anObject description];
|
|
}
|
|
|
|
static const NSMapTableValueCallBacks NSNonRetainedObjectMapValueCallBacks = {
|
|
(void (*)(NSMapTable *, const void *))__NSRetainNothing,
|
|
(void (*)(NSMapTable *, void *))__NSReleaseNothing,
|
|
(NSString *(*)(NSMapTable *, const void *))__NSDescribeObjects
|
|
};
|
|
|
|
#endif /* LIB_FOUNDATION_LIBRARY */
|
|
|
|
|
|
// Convert a character to a value between 0 and 15
|
|
static 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 substring in PPD printer file."];
|
|
// NOT REACHED
|
|
return 0; /* Quiet compiler warnings */
|
|
}
|
|
|
|
// Function to convert hexadecimal substrings
|
|
static 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];
|
|
skipSpace(scanner);
|
|
while (![scanner scanString:@">" intoString:NULL])
|
|
{
|
|
location = [scanner scanLocation];
|
|
if (location+2 > stringLength)
|
|
{
|
|
[NSException
|
|
raise:NSPPDParseException
|
|
format:@"Badly formatted hexadecimal substring in PPD printer file."];
|
|
// NOT REACHED
|
|
}
|
|
value = [value stringByAppendingFormat:@"%c",
|
|
16 * gethex([qString characterAtIndex:location])
|
|
+ gethex([qString characterAtIndex:location+1])];
|
|
[scanner setScanLocation:location+2];
|
|
skipSpace(scanner);
|
|
}
|
|
if([scanner scanUpToString:@"<" intoString:&part])
|
|
{
|
|
value = [value stringByAppendingString:part];
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static NSString *getFile(NSString *name, NSString *type)
|
|
{
|
|
return [NSBundle pathForGNUstepResource: name
|
|
ofType: type
|
|
inDirectory: NSPrinter_PATH];
|
|
}
|
|
|
|
|
|
@interface NSPrinter (private)
|
|
+ allocMaps;
|
|
- initWithPPD:(NSString *)PPDstring
|
|
withName:(NSString *)name
|
|
withType:(NSString *)type
|
|
withHost:(NSString *)host
|
|
withNote:(NSString *)note
|
|
fromFile:(NSString *)file
|
|
isReal:(BOOL)real;
|
|
- loadPPD:(NSString *)PPDstring
|
|
inclusionNum:(int)includeNum;
|
|
- addPPDKeyword:(NSString *)mainKeyword
|
|
withScanner:(NSScanner *)PPDdata;
|
|
- addPPDUIConstraint:(NSScanner *)constraint;
|
|
- addPPDOrderDependency:(NSScanner *)dependency;
|
|
- addValue:(NSString *)value
|
|
andValueTranslation:(NSString *)valueTranslation
|
|
andOptionTranslation:(NSString *)optionTranslation
|
|
forKey:(NSString *)key;
|
|
- addString:(NSString *)string
|
|
forKey:(NSString *)key
|
|
inTable:(NSMutableDictionary *)table;
|
|
@end
|
|
|
|
@implementation NSPrinter
|
|
|
|
//
|
|
// Class methods
|
|
//
|
|
+ (void)initialize
|
|
{
|
|
if (self == [NSPrinter class])
|
|
{
|
|
// Initial version
|
|
[self setVersion:1];
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finding an NSPrinter
|
|
//
|
|
+ (NSPrinter *)printerWithName:(NSString *)name
|
|
{
|
|
NSString *path;
|
|
NSArray *printerInfo;
|
|
NSPrinter *printer;
|
|
/* Contents of printerInfo array:
|
|
* [0]: NSString of the printer's type
|
|
* [1]: NSString of the printer's host
|
|
* [2]: NSString of the printer's note
|
|
*/
|
|
// Make sure the printer names dictionary etc. exists
|
|
if (!nameMap)
|
|
[self allocMaps];
|
|
printer = NSMapGet(nameMap, name);
|
|
// If the NSPrinter object for the printer already exists, return it
|
|
if (printer)
|
|
return printer;
|
|
// Otherwise, try to find the information in the nameDict
|
|
printerInfo = [nameDict objectForKey:name];
|
|
// Make sure you can find the printer name in the dictionary
|
|
if (!printerInfo)
|
|
{
|
|
[NSException raise:NSGenericException
|
|
format:@"Could not find printer named %@", name];
|
|
// NOT REACHED
|
|
}
|
|
// Create it
|
|
path = getFile([printerInfo objectAtIndex:0], @"ppd");
|
|
// If not found
|
|
if (path == nil || [path length] == 0)
|
|
{
|
|
[NSException raise:NSGenericException
|
|
format:@"Could not find PPD file %@.ppd",
|
|
[printerInfo objectAtIndex:0]];
|
|
// NOT REACHED
|
|
}
|
|
printer = AUTORELEASE([[self alloc]
|
|
initWithPPD:[NSString stringWithContentsOfFile:path]
|
|
withName:name
|
|
withType:[printerInfo objectAtIndex:0]
|
|
withHost:[printerInfo objectAtIndex:1]
|
|
withNote:[printerInfo objectAtIndex:2]
|
|
fromFile:[printerInfo objectAtIndex:0]
|
|
isReal:YES]);
|
|
// Once created, put it in the map table
|
|
NSMapInsert(nameMap, name, printer);
|
|
return printer;
|
|
}
|
|
|
|
+ (NSPrinter *)printerWithType:(NSString *)type
|
|
{
|
|
NSString *path;
|
|
NSPrinter *printer = nil;
|
|
// Make sure the printer types dictionary exists
|
|
if (!typeMap)
|
|
[self allocMaps];
|
|
else
|
|
printer = NSMapGet(typeMap, type);
|
|
// If the NSPrinter is already created, use it
|
|
if (printer)
|
|
return printer;
|
|
path = getFile(type, @"ppd");
|
|
// If not found
|
|
if (path == nil || [path length] == 0)
|
|
{
|
|
[NSException raise:NSGenericException
|
|
format:@"Could not find PPD file %@.ppd", type];
|
|
// NOT REACHED
|
|
}
|
|
printer = AUTORELEASE([[self alloc]
|
|
initWithPPD:[NSString stringWithContentsOfFile:path]
|
|
withName:type withType:type withHost:@"" withNote:@""
|
|
fromFile:path isReal:NO]);
|
|
// Once created, put it in the hash table
|
|
NSMapInsert(typeMap, type, printer);
|
|
return printer;
|
|
}
|
|
|
|
+ (NSArray *)printerNames
|
|
{
|
|
if(!nameDict)
|
|
[NSPrinter allocMaps];
|
|
return [nameDict allKeys];
|
|
}
|
|
|
|
+ (NSArray *)printerTypes
|
|
{
|
|
NSEnumerator *pathEnum;
|
|
NSBundle *lbdle;
|
|
NSString *lpath;
|
|
NSArray *ppdpaths;
|
|
NSMutableArray *printers;
|
|
NSString *path;
|
|
NSAutoreleasePool *subpool; // There's a lot of temp strings used...
|
|
int i, max;
|
|
|
|
if (printerTypesAvailable)
|
|
return printerTypesAvailable;
|
|
|
|
printers = RETAIN([NSMutableArray array]);
|
|
subpool = [[NSAutoreleasePool alloc] init];
|
|
|
|
pathEnum = [NSSearchPathForDirectoriesInDomains(GSLibrariesDirectory,
|
|
NSAllDomainsMask, YES) objectEnumerator];
|
|
while ((lpath = [pathEnum nextObject]))
|
|
{
|
|
lbdle = [NSBundle bundleWithPath: lpath];
|
|
ppdpaths = [lbdle pathsForResourcesOfType:@"ppd"
|
|
inDirectory:NSPrinter_PATH];
|
|
|
|
// FIXME - should get name from contents of PPD, not filename
|
|
max = [ppdpaths count];
|
|
for (i = 0; i < max; i++)
|
|
{
|
|
path = [[ppdpaths objectAtIndex:i] lastPathComponent];
|
|
[printers addObject:[path substringToIndex:[path length]-4]];
|
|
}
|
|
}
|
|
RELEASE(subpool);
|
|
|
|
printerTypesAvailable = printers;
|
|
return printers;
|
|
}
|
|
|
|
//
|
|
// Instance methods
|
|
//
|
|
//
|
|
// Deallocation of instance variables
|
|
//
|
|
- (void)dealloc
|
|
{
|
|
// Remove object from the appropriate table
|
|
if (_isRealPrinter)
|
|
NSMapRemove(nameMap, _printerName);
|
|
else
|
|
NSMapRemove(typeMap, _printerType);
|
|
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 encodeValueOfObjCType: @encode(BOOL) at: &_isRealPrinter];
|
|
|
|
[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(BOOL) at: &_isRealPrinter];
|
|
|
|
[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;
|
|
}
|
|
|
|
//
|
|
// Private Methods
|
|
//
|
|
|
|
//
|
|
// Allocate the printer name to PPD filename index dictionary
|
|
//
|
|
+ allocMaps
|
|
{
|
|
NSString *path;
|
|
|
|
// Allocate name and type maps
|
|
typeMap = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
|
NSNonRetainedObjectMapValueCallBacks,
|
|
TYPEMAPSIZE);
|
|
nameMap = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
|
NSNonRetainedObjectMapValueCallBacks,
|
|
NAMEMAPSIZE);
|
|
|
|
// Load the index file
|
|
path = getFile(NSPrinter_INDEXFILE, nil);
|
|
// If not found
|
|
if (path == nil || [path length] == 0)
|
|
{
|
|
[NSException raise:NSGenericException
|
|
format:@"Could not find index of printers, file %@",
|
|
NSPrinter_INDEXFILE];
|
|
// NOT REACHED
|
|
}
|
|
// And create the name dictionary, loading it
|
|
nameDict = RETAIN([NSDictionary dictionaryWithContentsOfFile:path]);
|
|
return self;
|
|
}
|
|
|
|
//
|
|
// Initialisation method
|
|
//
|
|
// To keep loading of PPDs relatively fast, not much checking is done on it.
|
|
- initWithPPD:(NSString *)PPDstring
|
|
withName:(NSString *)name
|
|
withType:(NSString *)type
|
|
withHost:(NSString *)host
|
|
withNote:(NSString *)note
|
|
fromFile:(NSString *)file
|
|
isReal:(BOOL)real
|
|
{
|
|
NSAutoreleasePool *subpool;
|
|
NSEnumerator *objEnum;
|
|
NSMutableArray *valArray;
|
|
|
|
// Initialise instance variables
|
|
_printerName = RETAIN(name);
|
|
_printerType = RETAIN(type);
|
|
_printerHost = RETAIN(host);
|
|
_printerNote = RETAIN(note);
|
|
_cacheAcceptsBinary = _cacheOutputOrder = -1;
|
|
_isRealPrinter = real;
|
|
_PPD = RETAIN([NSMutableDictionary dictionary]);
|
|
_PPDOptionTranslation = RETAIN([NSMutableDictionary dictionary]);
|
|
_PPDArgumentTranslation = RETAIN([NSMutableDictionary dictionary]);
|
|
_PPDOrderDependency = RETAIN([NSMutableDictionary dictionary]);
|
|
_PPDUIConstraints = RETAIN([NSMutableDictionary dictionary]);
|
|
// Create a temporary autorelease pool, as many temporary objects are used
|
|
subpool = [[NSAutoreleasePool alloc] init];
|
|
// Create character sets used during scanning
|
|
newlineSet = [NSCharacterSet characterSetWithCharactersInString:@"\n\r"];
|
|
keyEndSet = [NSCharacterSet characterSetWithCharactersInString:@"\n\r\t: "];
|
|
optKeyEndSet = [NSCharacterSet characterSetWithCharactersInString:@"\n\r:/"];
|
|
valueEndSet = [NSCharacterSet characterSetWithCharactersInString:@"\n\r/"];
|
|
// Allowed repeated keys, used during scanning.
|
|
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];
|
|
// Set the file name to use
|
|
PPDFileName = file;
|
|
// 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];
|
|
// And scan the PPD itself
|
|
[self loadPPD:PPDstring inclusionNum:0];
|
|
// Search the PPD dictionary for symbolvalues, and substitute them.
|
|
objEnum = [_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, PPDFileName];
|
|
// NOT REACHED
|
|
}
|
|
[valArray replaceObjectAtIndex:i withObject:newValue];
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// DISABLED: Though the following keywords *should* be present, there seems
|
|
// to be few problems is they are omitted. Many of the .ppd files I have
|
|
// don't have *LanguageEncoding, for example.
|
|
|
|
// Make sure all the required keys are present
|
|
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, PPDFileName];
|
|
// NOT REACHED
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Release the local autoreleasePool
|
|
RELEASE(subpool);
|
|
return self;
|
|
}
|
|
|
|
- loadPPD:(NSString *)PPDstring
|
|
inclusionNum:(int)includeNum
|
|
{
|
|
NSScanner *PPDdata;
|
|
NSString *keyword;
|
|
|
|
// 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 (1) // Check it is not at end only after skipping the blanks
|
|
{
|
|
// Get to the start of a new keyword, skipping blank lines
|
|
skipSpace(PPDdata);
|
|
if ([PPDdata isAtEnd])
|
|
break;
|
|
// All new entries should starts '*'
|
|
if (![PPDdata scanString:@"*" intoString:NULL])
|
|
{
|
|
[NSException raise:NSPPDParseException
|
|
format:@"Line not starting * in PPD file %@.ppd",
|
|
PPDFileName];
|
|
// NOT REACHED
|
|
}
|
|
// Skip lines starting '*%', '*End', '*SymbolLength', or '*SymbolEnd'
|
|
if ([PPDdata scanString:@"%" intoString:NULL]
|
|
|| [PPDdata scanString:@"End" intoString:NULL]
|
|
|| [PPDdata scanString:@"SymbolLength" intoString:NULL]
|
|
|| [PPDdata scanString:@"SymbolEnd" 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
|
|
if ([PPDdata scanCharactersFromSet:newlineSet intoString:NULL])
|
|
continue;
|
|
// Add the line to the relevant table
|
|
if ([keyword isEqual:@"OrderDependency"])
|
|
[self addPPDOrderDependency:PPDdata];
|
|
else if ([keyword isEqual:@"UIConstraints"])
|
|
[self addPPDUIConstraint:PPDdata];
|
|
else if ([keyword isEqual:@"Include"])
|
|
{
|
|
NSString *fileName;
|
|
NSString *path;
|
|
[PPDdata scanString:@":" intoString:NULL];
|
|
// Find the filename between two "s
|
|
[PPDdata scanString:@"\"" intoString:NULL];
|
|
[PPDdata scanUpToString:@"\"" intoString:&fileName];
|
|
[PPDdata scanString:@"\"" intoString:NULL];
|
|
// Load the file
|
|
path = getFile(fileName, nil);
|
|
// If not found
|
|
if (path == nil || [path length] == 0)
|
|
{
|
|
[NSException raise:NSPPDIncludeNotFoundException
|
|
format:@"Could not find included PPD file %@",
|
|
fileName];
|
|
// NOT REACHED
|
|
}
|
|
includeNum++;
|
|
if (includeNum > MAX_PPD_INCLUDES)
|
|
{
|
|
[NSException raise:NSPPDIncludeStackOverflowException
|
|
format:@"Too many *Includes in PPD"];
|
|
// NOT REACHED
|
|
}
|
|
[self loadPPD:[NSString stringWithContentsOfFile:path]
|
|
inclusionNum:includeNum];
|
|
}
|
|
else if ([keyword isEqual:@"SymbolValue"])
|
|
{
|
|
NSString *symbolName;
|
|
NSString *symbolVal;
|
|
if (![PPDdata scanString:@"^" intoString:NULL])
|
|
{
|
|
[NSException
|
|
raise:NSPPDParseException
|
|
format:@"Badly formatted *SymbolValue in PPD file %@.ppd",
|
|
PPDFileName];
|
|
// NOT REACHED
|
|
}
|
|
[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];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- addPPDKeyword:(NSString *)mainKeyword
|
|
withScanner:(NSScanner *)PPDdata
|
|
{
|
|
NSString *optionKeyword = nil;
|
|
NSString *optionTranslation = nil;
|
|
NSString *value = nil;
|
|
NSString *valueTranslation = 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 %@.ppd",
|
|
PPDFileName];
|
|
// NOT REACHED
|
|
}
|
|
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 = 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 = interpretQuotedValue(optionTranslation);
|
|
if (valueTranslation)
|
|
valueTranslation = 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=[mainKeyword
|
|
stringByAppendingFormat:@"/%@",
|
|
optionKeyword];
|
|
if ([self isKey:mainAndOptionKeyword inTable:@"PPD"])
|
|
return self;
|
|
[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 (![_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;
|
|
[self addValue:value
|
|
andValueTranslation:valueTranslation
|
|
andOptionTranslation:optionTranslation
|
|
forKey:mainKeyword];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- addPPDUIConstraint:(NSScanner *)constraint
|
|
{
|
|
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 PPDFileName %@.ppd",
|
|
PPDFileName];
|
|
// NOT REACHED
|
|
}
|
|
// 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];
|
|
return self;
|
|
}
|
|
|
|
- addPPDOrderDependency:(NSScanner *)dependency
|
|
{
|
|
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 %@.ppd",
|
|
PPDFileName];
|
|
// NOT REACHED
|
|
}
|
|
[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:@"/%s", optionKeyword];
|
|
[self addString:realValue forKey:keyword inTable:_PPDOrderDependency];
|
|
[self addString:section forKey:keyword inTable:_PPDOrderDependency];
|
|
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
|