/* NSPrinter.m NSPrinter, NSPrintInfo, NSPrintPanel, NSPageLayout, NSPrintOperation Printing classes. Copyright (C) 1996, 1997 Free Software Foundation, Inc. Authors: Simon Frankau Date: June 1997 - January 1998 Author: H. N. Schaller Date: Jan 2006 _NSPDFGraphicsContext added This file is part of the mySTEP Library and is provided under the terms of the GNU Library General Public License. */ #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import "NSBackendPrivate.h" #import "NSAppKitPrivate.h" //***************************************************************************** // // NSPrinter // //***************************************************************************** @implementation NSPrinter + (NSPrinter *) printerWithName:(NSString *) name domain:(NSString *) domain includeUnavailable:(BOOL) flag; { return [[[self alloc] _initWithName:name host:@"localhost" type:@"PDF" note:@""] autorelease]; } + (NSPrinter *) printerWithName:(NSString *)name { return [[[self alloc] _initWithName:name host:@"localhost" type:@"PDF" note:@""] autorelease]; } + (NSPrinter *) printerWithType:(NSString *)type { return [[[self alloc] _initWithName:@"default" host:@"localhost" type:type note:@""] autorelease]; } + (NSArray *) printerNames { // read from AppKit-Info.plist? return [NSArray arrayWithObject:@"default"]; } + (NSArray *) printerTypes { return [NSArray arrayWithObject:@"PDF"]; } - (id) _initWithName:(NSString *) name host:(NSString *) host type:(NSString *) type note:(NSString *) note; { if((self=[super init])) { printerHost=[host retain]; printerName=[name retain]; printerNote=[note retain]; printerType=[type retain]; } return self; } - (void) dealloc { [printerHost release]; [printerName release]; [printerNote release]; [printerType release]; [printerDomain release]; [super dealloc]; } // // Printer Attributes // - (NSString *) host { return printerHost; } - (NSString *) name { return printerName; } - (NSString *) note { return printerNote; } - (NSString *) type { return printerType; } - (NSString *) domain; { return printerDomain; } - (BOOL) acceptsBinary { return NO; } - (NSRect) imageRectForPaper:(NSString *)paperName { return NSMakeRect(0,0,0,0); } - (NSSize) pageSizeForPaper:(NSString *)paperName { return NSMakeSize(0,0); } - (BOOL) isColor { return YES; } - (BOOL) isFontAvailable:(NSString *)fontName { return NO; } - (int) languageLevel { return 0; } - (BOOL) isOutputStackInReverseOrder { return NO; } - (BOOL) booleanForKey:(NSString *)key inTable:(NSString *)table { return NO; } - (NSDictionary *) deviceDescription { // NSSize resolution, size; // infinite for a PDF printer static NSDictionary *_device; if(!_device) { _device=[[NSMutableDictionary alloc] initWithObjectsAndKeys: @"DeviceRGBColorSpace", NSDeviceColorSpaceName, @"YES", NSDeviceIsPrinter, @"NO", NSDeviceIsScreen, // [NSValue valueWithSize:resolution], NSDeviceResolution, // [NSValue valueWithSize:size], NSDeviceSize, nil]; } return _device; } - (float) floatForKey:(NSString *)key inTable:(NSString *)table { return 0; } - (int) intForKey:(NSString *)key inTable:(NSString *)table; { return 0; } - (NSRect) rectForKey:(NSString *)key inTable:(NSString *)table { return NSMakeRect(0,0,0,0); } - (NSSize) sizeForKey:(NSString *)key inTable:(NSString *)table { return NSMakeSize(0,0); } - (NSString *) stringForKey:(NSString *)key inTable:(NSString *)table { return nil; } - (NSArray *) stringListForKey:(NSString *)key inTable:(NSString *)table { return nil; } - (NSPrinterTableStatus) statusForTable:(NSString *)table { return NSPrinterTableError; } - (BOOL) isKey:(NSString *)key inTable:(NSString *)table { return NO; } // // NSCoding protocol // - (void) encodeWithCoder:(NSCoder *) aCoder { // [super encodeWithCoder:aCoder]; [aCoder encodeObject:printerHost]; [aCoder encodeObject:printerName]; [aCoder encodeObject:printerNote]; [aCoder encodeObject:printerType]; [aCoder encodeValueOfObjCType:"i" at:&cacheAcceptsBinary]; [aCoder encodeValueOfObjCType:"i" at:&cacheOutputOrder]; [aCoder encodeValueOfObjCType:@encode(BOOL) at:&isRealPrinter]; } - (id) initWithCoder:(NSCoder *) aDecoder { if([aDecoder allowsKeyedCoding]) return self; printerHost = [[aDecoder decodeObject] retain]; printerName = [[aDecoder decodeObject] retain]; printerNote = [[aDecoder decodeObject] retain]; printerType = [[aDecoder decodeObject] retain]; [aDecoder decodeValueOfObjCType:"i" at:&cacheAcceptsBinary]; [aDecoder decodeValueOfObjCType:"i" at:&cacheOutputOrder]; [aDecoder decodeValueOfObjCType:@encode(BOOL) at:&isRealPrinter]; return self; } @end /* NSPrinter */ //***************************************************************************** // // NSPrintInfo // //***************************************************************************** // Class variables static NSPrintInfo *sharedPrintInfoObject = nil; @implementation NSPrintInfo + (void) setSharedPrintInfo:(NSPrintInfo *)printInfo { NSAssert(printInfo, @"nil printInfo"); ASSIGN(sharedPrintInfoObject, printInfo); } + (NSPrintInfo *)sharedPrintInfo { if(!sharedPrintInfoObject) { NSDictionary *info=[[NSBundle bundleForClass:[self class]] objectForInfoDictionaryKey:@"NSPrintInfoDefault"]; sharedPrintInfoObject=[[self alloc] initWithDictionary:info]; // create a default object } return sharedPrintInfoObject; } // // Managing the Printing Rectangle // + (NSSize)sizeForPaperName:(NSString *)name { return [[self defaultPrinter] pageSizeForPaper:name]; } + (NSPrinter *)defaultPrinter { return nil; } + (void)setDefaultPrinter:(NSPrinter *)printer { } - (id) initWithDictionary:(NSDictionary *)aDict { if((self=[super init])) { info=[aDict mutableCopy]; } return self; } - (id) copyWithZone:(NSZone *) z { return [[isa alloc] initWithDictionary:info]; // return a (mutable) copy } - (void) dealloc; { [info release]; [super dealloc]; } // // Managing the Printing Rectangle // - (float)bottomMargin { return [(NSNumber *)[info objectForKey:NSPrintBottomMargin] floatValue]; } - (float)leftMargin { return [(NSNumber *)[info objectForKey:NSPrintLeftMargin] floatValue]; } - (NSPrintingOrientation)orientation { return [(NSNumber *)[info objectForKey:NSPrintOrientation] intValue]; } - (NSString *)paperName { return [info objectForKey:NSPrintPaperName]; } - (NSSize)paperSize { return [(NSValue *)[info objectForKey:NSPrintPaperSize] sizeValue]; } - (float)rightMargin { return [(NSNumber *)[info objectForKey:NSPrintRightMargin] floatValue]; } - (void)setBottomMargin:(float)value { } - (void)setLeftMargin:(float)value { } - (void)setOrientation:(NSPrintingOrientation)mode { } - (void)setPaperName:(NSString *)name { } - (void)setPaperSize:(NSSize)size { } - (void)setRightMargin:(float)value { } - (void)setTopMargin:(float)value { } - (float)topMargin { return [(NSNumber *)[info objectForKey:NSPrintTopMargin] floatValue]; } // // Pagination // - (NSPrintingPaginationMode)horizontalPagination { return [(NSNumber *)[info objectForKey:NSPrintHorizontalPagination] intValue]; } - (void)setHorizontalPagination:(NSPrintingPaginationMode)mode { } - (void)setVerticalPagination:(NSPrintingPaginationMode)mode { } - (NSPrintingPaginationMode) verticalPagination { return [(NSNumber *)[info objectForKey:NSPrintVerticalPagination] intValue]; } // // Positioning the Image on the Page // - (BOOL) isHorizontallyCentered { return [(NSNumber *)[info objectForKey:NSPrintHorizontallyCentered] boolValue]; } - (BOOL) isVerticallyCentered { return [(NSNumber *)[info objectForKey:NSPrintVerticallyCentered] boolValue]; } - (void) setHorizontallyCentered:(BOOL)flag { } - (void) setVerticallyCentered:(BOOL)flag { } // // Specifying the Printer // - (NSPrinter *)printer { return [info objectForKey:NSPrintPrinter]; } - (void)setPrinter:(NSPrinter *)aPrinter { [info setObject:aPrinter forKey:NSPrintPrinter]; } // // Controlling Printing // - (NSString *)jobDisposition { return [info objectForKey:NSPrintJobDisposition]; } - (void)setJobDisposition:(NSString *)disposition { [info setObject:disposition forKey:NSPrintJobDisposition]; } - (void)setUpPrintOperationDefaultValues { } - (NSMutableDictionary *)dictionary { return info; } - (id) initWithCoder:(NSCoder *) aDecoder // NSCoding protocol { if([aDecoder allowsKeyedCoding]) return self; info = [aDecoder decodePropertyList]; return self; } - (void) encodeWithCoder:(NSCoder *) aCoder { [aCoder encodePropertyList:info]; } @end /* NSPrintInfo */ //***************************************************************************** // // NSPrintPanel // //***************************************************************************** @implementation NSPrintPanel + (NSPrintPanel *) printPanel; { return [[[self alloc] init] autorelease]; } - (NSView *)accessoryView { return _accessoryView; } - (NSString *)jobStyleHint { return _jobStyleHint; } - (void)setAccessoryView:(NSView *)aView { ASSIGN(_accessoryView, aView); } - (void)setJobStyleHint:(NSString *)hint { ASSIGN(_jobStyleHint, hint); } - (id) init; { if((self=[super init])) { } return self; } - (void) dealloc; { [_accessoryView release]; [_jobStyleHint release]; [super dealloc]; } - (void) _printPanelDidEnd:(NSPrintPanel *) panel returnCode:(int) code contextInfo:(void *) context; { _returnValue=code; } - (NSInteger) runModal; { return [self runModalWithPrintInfo:[[NSPrintOperation currentOperation] printInfo]]; } - (NSInteger) runModalWithPrintInfo:(NSPrintInfo *) info; { [self beginSheetWithPrintInfo:info modalForWindow:nil delegate:self didEndSelector:@selector(_printPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; // runs modal [self finalWritePrintInfo]; return _returnValue; } - (void) beginSheetWithPrintInfo:(NSPrintInfo *) info modalForWindow:(NSWindow *) window delegate:(id) delegate didEndSelector:(SEL) sel contextInfo:(void *) context; { int r; NSPanel *panel; [self updateFromPrintInfo]; panel=NIMP; // create a sheet or popup window if(window) { ; // create a sheet [NSApp beginSheet:panel modalForWindow:window modalDelegate:delegate didEndSelector:sel contextInfo:context]; // run modal loop } else { void (*didend)(id, SEL, NSPrintPanel *, int, void *); ; // create a popup r=[NSApp runModalForWindow:panel]; didend = (void (*)(id, SEL, NSPrintPanel *, int, void *))[delegate methodForSelector:sel]; (*didend)(self, sel, self, r, context); } } - (void)pickedButton:(id)sender { } - (void)pickedAllPages:(id)sender { } - (void)pickedLayoutList:(id)sender { } - (void)updateFromPrintInfo { } - (void)finalWritePrintInfo { } @end /* NSPrintPanel */ //***************************************************************************** // // NSPageLayout // //***************************************************************************** @implementation NSPageLayout // PageLayout panel queries the user for // paper type and orientation info + (NSPageLayout *) pageLayout { return nil; } - (int) runModal { return 0; } - (int) runModalWithPrintInfo:(NSPrintInfo *)pInfo { return 0; } // // Customizing the Panel // - (NSView *)accessoryView { return nil; } - (void)setAccessoryView:(NSView *)aView {} // // Updating the Panel's Display // - (void)convertOldFactor:(float *)old newFactor:(float *)new {} - (void)pickedButton:(id)sender {} - (void)pickedOrientation:(id)sender {} - (void)pickedPaperSize:(id)sender {} - (void)pickedUnits:(id)sender {} // // Communicating with the NSPrintInfo Object // - (NSPrintInfo *)printInfo { return nil; } - (void)readPrintInfo {} - (void)writePrintInfo {} - (id) initWithCoder:(NSCoder *) aDecoder // NSCoding protocol { if([aDecoder allowsKeyedCoding]) return self; return self; } - (void)encodeWithCoder:(NSCoder *) aCoder { return; } @end /* NSPageLayout */ /* * _NSPDFGraphicsContext - by H. N. Schaller */ @interface _NSPDFReference : NSObject { id _object; unsigned int _index; unsigned int _position; } + (_NSPDFReference *) referenceWithObject:(id) object; @end @implementation _NSPDFReference + (_NSPDFReference *) referenceWithObject:(id) object; { _NSPDFReference *r=[[self new] autorelease]; if(r) r->_object=object; return r; } @end @interface _NSPDFGraphicsContext : NSGraphicsContext { @public NSOutputStream *_pdf; // stream to write to NSMutableArray *_objects; // objects (incl. streams) NSMutableArray *_references; // objects (incl. streams) NSMutableArray *_fonts; // fonts NSMutableArray *_xobjects; // images NSMutableDictionary *_parent; // page tree entry NSMutableArray *_pages; // page catalogs NSMutableDictionary *_catalog; // catalog entry NSAffineTransform *_ctm; NSBezierPath *_currentPath; // to compare attributes NSColor *_currentFillColor; // ??? do we need to save that ??? NSColor *_currentStrokeColor; } @end @interface NSObject (_NSPDFGraphicsContext) - (void) appendToStream:(NSOutputStream *) stream; @end @interface NSOutputStream (_NSPDFGraphicsContext) - (void) appendString:(NSString *) string; - (void) appendFormat:(NSString *) format, ...; @end @implementation _NSPDFReference (_NSPDFGraphicsContext) - (void) appendToStream:(NSOutputStream *) stream; { _NSPDFGraphicsContext *ctx=(_NSPDFGraphicsContext *) [NSGraphicsContext currentContext]; if(_position == 0) { // not yet encoded _index=[ctx->_objects count]; [ctx->_objects addObject:self]; _position=0; // FIXME: current writing position // output 'obj %u' // [object _PDFstringRepresenation]; // where to write? } [stream appendFormat:@" R %u %u", _index, _position]; } @end @implementation NSArray (_NSPDFGraphicsContext) - (void) appendToStream:(NSOutputStream *) stream; { NSEnumerator *e=[self objectEnumerator]; id o; [stream appendString:@" [ "]; while((o=[e nextObject])) [o appendToStream:stream]; [stream appendString:@" ]"]; } @end @implementation NSDictionary (_NSPDFGraphicsContext) - (void) appendToStream:(NSOutputStream *) stream; { NSEnumerator *e=[self keyEnumerator]; id key; [stream appendString:@" << "]; while((key=[e nextObject])) { [key appendToStream:stream]; [[self objectForKey:key] appendToStream:stream]; } [stream appendString:@" >> "]; } @end @implementation NSOutputStream (_NSPDFGraphicsContext) // should be able to handle additional attributes - (void) appendToStream:(NSOutputStream *) stream; { NSData *data=[self propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; NSDictionary *a=[NSDictionary dictionaryWithObjectsAndKeys: @"Stream", @"Type", [NSNumber numberWithInt:[data length]], @"Length", nil]; [a appendToStream:stream]; // append dictionary [stream appendFormat:@"stream\n"]; // handle encoding/compression etc. // append data [stream appendFormat:@"endstream\n"]; } - (void) appendString:(NSString *) string; { const char *cstr=[string UTF8String]; [self write:(unsigned char *)cstr maxLength:strlen(cstr)]; #if 1 NSLog(@"PDF: %@", string); #endif } - (void) appendFormat:(NSString *) format, ...; { va_list ap; NSString *str; va_start(ap, format); str=[[NSString alloc] initWithFormat:format arguments:ap]; [self appendString:str]; [str release]; va_end(ap); } @end @implementation NSString (_NSPDFGraphicsContext) - (void) appendToStream:(NSOutputStream *) stream; { [stream appendFormat:@" /%@", self]; } @end @implementation NSNumber (_NSPDFGraphicsContext) - (void) appendToStream:(NSOutputStream *) stream; { [stream appendFormat:@" %@", [self description]]; } @end @implementation _NSPDFGraphicsContext // NSGraphicsContext - (BOOL) isDrawingToScreen; { return NO; } - (NSDictionary *) attributes { return [NSDictionary dictionaryWithObjectsAndKeys: NSGraphicsContextPDFFormat, NSGraphicsContextRepresentationFormatAttributeName, nil]; } // shouldn't that be initWithAttributes? - (id) initWithAtributes:(NSDictionary *) attribs; { if((self=[super init])) { _pdf=[[attribs objectForKey:@"PDFOutputStream"] retain]; [_pdf open]; [_pdf appendString:@"%%PDF-1.3\n"]; _objects=[[NSMutableArray arrayWithCapacity:20] retain]; _references=[[NSMutableArray arrayWithCapacity:20] retain]; _fonts=[[NSMutableArray arrayWithCapacity:20] retain]; _xobjects=[[NSMutableArray arrayWithCapacity:20] retain]; _pages=[[NSMutableArray arrayWithCapacity:20] retain]; _parent=[[NSMutableDictionary dictionaryWithObjectsAndKeys: @"Pages", @"Type", // -, @"Parent", // we are the root tree node _pages, @"Kids", [NSNumber numberWithInt:0], @"Count", nil] retain]; _catalog=[[NSMutableDictionary dictionaryWithObjectsAndKeys: @"Catalog", @"Type", _parent, @"Pages", nil] retain]; } return self; } - (void) dealloc; { unsigned pos=[[_pdf propertyForKey:NSStreamFileCurrentOffsetKey] unsignedIntValue]; [_pdf appendString:@"\nxref\n"]; // write _references table [_pdf appendString:@"\ntrailer\n"]; [[NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:[_references count]], @"Size", // optional, @"Prev", _catalog, @"Root", // optional, @"Encrypt", // optional, @"Info", // optional, @"ID", nil] appendToStream:_pdf]; // write trailer dictionary [_pdf appendFormat:@"\nstartxref\n%u\n%%EOF\n", pos]; [_pdf close]; [_pdf release]; // must have been retained somewhere else to persist beyond this operation! [_objects release]; [_references release]; [_fonts release]; [_xobjects release]; [_ctm release]; [_pages release]; [_catalog release]; [super dealloc]; } - (void) saveGraphicsState; { [_pdf appendString:@" q"]; } - (void) restoreGraphicsState; { [_pdf appendString:@" Q"]; } - (void) flushGraphics; { return; } // NSColor - (void) _setFillColor:(NSColor *) clr; { ASSIGN(_currentFillColor, clr); // we might need that if we change only one component???? [_pdf appendString:@" ***set fill color'''"]; } - (void) _setStrokeColor:(NSColor *) clr; { ASSIGN(_currentStrokeColor, clr); [_pdf appendString:@" ***set stroke color***"]; } - (void) _setColor:(NSColor *) clr; { // set both [self _setFillColor:clr]; [self _setStrokeColor:clr]; } - (void) _setCursor:(NSCursor *) cursor; { return; } // ignore // NSBezierPath - (void) _bezierPath:(NSBezierPath *) path; { NSPoint points[3]; NSPoint current={ -999.4, -3.141592 }; // highly improbable - and 'good' code will start with a move element unsigned int i, count=[path elementCount]; for(i=0; i 0 && _currentPage <= pages.length) { // print page(s) of _view NSRect pageRect=paginated?[_view rectForPage:_currentPage]:_insideRect; // give loop a chance to run once and handle needsDisplay events!!! #if 1 NSLog(@"Print Page %d of %d", _currentPage, pages.length); #endif if(_progressIndicator) { [_progressIndicator setDoubleValue:(double) _currentPage]; [_progressIndicator display]; [_progessMessage setStringValue:[NSString stringWithFormat:@"Page %d of %d", _currentPage, pages.length]]; [_progessMessage display]; #if 1 sleep(5); #endif } // set up everything in context so that we write into a new Page stream [_view beginPageInRect:pageRect atPlacement:[_view locationOfPrintRect:pageRect]]; // this locks focus and sets the CTM so that the pageRectangle is placed properly on the page [_view drawPageBorderWithSize:_insideRect.size]; [[_view pageHeader] drawAtPoint:NSMakePoint(10.0, 2000.0)]; // set clipping rect to pageRect #if 1 NSLog(@"Print int rect %@", NSStringFromRect(pageRect)); #endif [_view drawRect:pageRect]; [[_view pageFooter] drawAtPoint:NSMakePoint(10.0, 10.0)]; [_view endPage]; // unlocks focus if(_pageOrder != NSDescendingPageOrder) _currentPage++; // next else _currentPage--; // previous } [_view endDocument]; // unlocks focus [self destroyContext]; if([self deliverResult]) [self cleanUpOperation]; } [_progressPanel close]; } // FIXME: may run in different thread // FIXME: selector has a different signature: // [delegate :self success:_cancelled?NSPrntCancelled:NSPrintSuccess contextInfo:contextInfo]; if(didRunSelector) [delegate performSelector:didRunSelector withObject:(id)contextInfo]; [isa setCurrentOperation:nil]; } @end /* NSPrintOperation */