Reputation: 45
I have a custom NSView that I want to print. After setting things up with the NSView and the print options, I make this call:
NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView: printView printInfo: printInfo];
[NSPrintOperation setCurrentOperation: printOperation];
[printView beginDocument];
NSGraphicsContext* theContext = printOperation.context;
"theContext" is always nil. If I ignore that, when I make this call:
[printView beginPageInRect: rect atPlacement: location];
I get an exception, saying: "[General] lockFocus/unlockFocus sent to a view which is not in a window"
If I comment that out, I get about a billion messages that say this: "CGContextDrawPath: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable." Turning on the backtrace just shows all of my drawing is what is causing it.
If I look at the graphics context within my view's "DrawRect:" function:
NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
both graphicsContext and context are nil.
So, what do I have to do to get a valid printing context? I see that there is a NSPrintOperation method createContext, but the docs say not to call it directly, and if I ignore that, it doesn't help and shoots about eight empty jobs to the printer.
Latest version of code, which still results in a null context:
NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView: printView printInfo: printInfo];
[printView setCurrentForm: [formSet objectAtIndex: 0]];
NSInteger pageCounter = 0;
formHeight = 0;
formWidth = 0;
for (AFVForm *oneForm in formSet)
{
printView.verticalOffset = formHeight;
NSRect rect = NSMakeRect(0, 0, oneForm.pageWidth, oneForm.pageHeight);
NSPoint location = [printView locationOfPrintRect: rect];
formHeight += [oneForm pageHeight];
if ([oneForm pageWidth] > formWidth)
formWidth = [oneForm pageWidth];
pageCounter++;
printView.currentForm = oneForm;
[printView setPrintMode: YES];
[printView drawRect: NSZeroRect];
[printView setPrintMode: NO];
}
[printOperation setShowsPrintPanel:YES];
[printOperation runOperationModalForWindow: [self window] delegate: nil didRunSelector: nil contextInfo: nil];
Upvotes: 0
Views: 520
Reputation: 15589
beginDocument
and beginPageInRect:atPlacement:
are called at the beginning of the printing session and at the beginning of each page. Override these methods if you want but don't call them. Don't call setCurrentOperation
, just create a NSPrintOperation
and call runOperation
or runOperationModalForWindow:delegate:didRunSelector:contextInfo:
.
See Printing Programming Guide for Mac
Edit, example:
PrintView.h
@interface PrintView : NSView
@property (strong) NSArray *forms;
@end
PrintView.m
@interface PrintView ()
@property (weak) NSTextField *titleField;
@property (weak) NSDictionary *currentForm;
@end
@implementation PrintView
- (instancetype)initWithFrame:(NSRect)frameRect {
if (self = [super initWithFrame:frameRect]) {
// add a title text field
NSTextField *textField = [[NSTextField alloc] initWithFrame:NSMakeRect(25.0, 225.0, 250.0, 25.0)];
textField.alignment = NSTextAlignmentCenter;
[self addSubview:textField];
self.titleField = textField;
}
return self;
}
- (BOOL)knowsPageRange:(NSRangePointer)range {
range->location = 1;
range->length = self.forms.count;
return YES;
}
- (NSRect)rectForPage:(NSInteger)page {
return self.bounds;
}
- (void)beginPageInRect:(NSRect)rect atPlacement:(NSPoint)location {
[super beginPageInRect:rect atPlacement:location];
NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
self.currentForm = self.forms[printOperation.currentPage - 1];
// set the title
[self.titleField setStringValue:
[NSString stringWithFormat:@"Form ‘%@’ page %li",
self.currentForm[@"title"], (long)printOperation.currentPage]];
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
// draw a colored frame
NSColor *formColor = self.currentForm[@"color"];
NSRect rect = self.bounds;
NSInsetRect(rect, 20.0, 20.0);
[formColor set];
NSFrameRect(rect);
}
@end
somewhere else
- (IBAction)printAction:(id)sender {
PrintView *printView = [[PrintView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 300.0, 300.0)];
printView.forms = @[
@{@"title":@"Form A", @"color":[NSColor redColor]},
@{@"title":@"Form B", @"color":[NSColor greenColor]},
@{@"title":@"Form C", @"color":[NSColor blueColor]},
];
NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printView];
[printOperation setShowsPrintPanel:YES];
[printOperation runOperationModalForWindow:[self window] delegate:nil didRunSelector:NULL contextInfo:NULL];
}
Upvotes: 0
Reputation: 748
A Swift example, goes in your NSView subclass. I apologize if it isn't helpful:
func letsPrint() {
let pInfo = NSPrintInfo.shared
let d = pInfo.dictionary()
d["NSLastPage"] = 1
super.printView(self)
}
override func knowsPageRange(_ range: NSRangePointer) -> Bool {
return true
}
override func rectForPage(_ page: Int) -> NSRect {
if page > 1 { return NSZeroRect }
return NSRect(x: 0, y: 0, width: 612, height: 792)
}
This example prints 1 page, 8.5 x 11, hence the constants in rectForPage
letsPrint is wired to the app menu first responder.
Upvotes: -1