Cocoa Programming for Mac OS X - Aaron Hillegass [106]
You can create your own NSValueTransformer subclasses and attach them to bindings in your application. Unlike formatters, value transformers are used only by bindings.
Chapter 27. Printing
Code to handle printing is always relatively hard to write. Many factors are at play: pagination, margins, and page orientation (landscape versus portrait). This chapter is designed to get you started on your journey toward the perfect printout.
Compared to most operating systems, Mac OS X makes writing print routines considerably easier. After all, your views already know how to generate PDF, and Mac OS X knows how to print PDF. If you have a document-based application and a view that knows how to draw itself, you simply implement printOperationWithSettings:error:. In this method, you create an NSPrintOperation object, using a view, and return it. The code, in your NSDocument subclass, would look like this:
- (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)ps
error:(NSError **)e;
{
NSPrintInfo *printInfo = [self printInfo];
NSPrintOperation *printOp
= [NSPrintOperation printOperationWithView:aView
printInfo:printInfo];
return printOp;
}
Dealing with Pagination
What about multiple pages? A view, after all, has only a single page. How will you get a view to print multiple-page documents? Off-screen, you will make a huge view that can display all the pages of the document simultaneously (Figure 27.1). The print system will ask the view how many pages it is displaying and will ask the view where each page can be found in the view.
Figure 27.1. Each Page Is a Rectangle on the View
Your view, then, must override two methods:
// How many pages?
- (BOOL)knowsPageRange:(NSRange *)rptr;
// Where is each page?
- (NSRect)rectForPage:(NSInteger)pageNum;
Instead of creating a huge view and returning a different rectangle for each page, you can note what page is being printed and always return the same rectangle in rectForPage:. This is the technique you will be using in the exercise.
As an example, you will add printing to the RaiseMan application. You will print the name and expected raise for as many people as will fit on the paper size that the user selected from the Print panel (Figure 27.2).
Figure 27.2. Completed Application
To do so, you will create a view that does the printing. Instead of making the view big enough to display all the people simultaneously, we will simply note which page the system is printing and draw only the names on that page in drawRect:.
Create a class called PeopleView that is a subclass of NSView. Make PeopleView.h look like this:
#import @interface PeopleView : NSView { NSArray *people; NSMutableDictionary *attributes; float lineHeight; NSRect pageRect; NSInteger linesPerPage; NSInteger currentPage; } - (id)initWithPeople:(NSArray *)array; @end In PeopleView.m, you will implement the initWithPeople: method. This initializer will call NSView’s initWithFrame: method. #import "PeopleView.h" #import "Person.h" @implementation PeopleView - (id)initWithPeople:(NSArray *)persons { // Call the superclass's designated initializer with some // dummy frame self = [super initWithFrame:NSMakeRect(0, 0, 700, 700)]; if (self) { people = [persons copy]; // The attributes of the text to be printed attributes = [[NSMutableDictionary alloc] init]; NSFont *font = [NSFont fontWithName:@"Monaco" size:12.0]; lineHeight = [font capHeight] * 1.7; [attributes setObject:font forKey:NSFontAttributeName]; } return self; } #pragma mark Pagination - (BOOL)knowsPageRange:(NSRange *)range { NSPrintOperation *po = [NSPrintOperation currentOperation]; NSPrintInfo *printInfo = [po printInfo]; // Where can I draw? pageRect = [printInfo imageablePageBounds]; NSRect newFrame; newFrame.origin = NSZeroPoint; newFrame.size = [printInfo paperSize]; [self setFrame:newFrame];