13.02.2011
für CorePlot 0.2.2, Xcode 3.2
CorePlot: Drucken von Diagrammen
Im Folgenden wird erklärt, wie ein CorePlot-Diagramm auf einem Drucken ausgedruckt werden kann. Als Vorlage kann entweder Tutorial: CorePlot Liniendiagramm oder Tutorial: CorePlot Balkendiagramm verwendet werden. In dem Beispiel wird ein zusätzlicher Button in die Oberfläche eingebaut, der mit einer Methode zum Drucken verbunden wird. Die Methode öffnet den Druck-Dialog, der es dem Benutzer ermöglicht eine Druckvorschau zu öffnen, das Diagramm zu speichern und natürlich zu drucken.
Als erstes muß eine IBAction
in der Header-Datei hinzugefügt werden. In dem Beispiel hat sie den Namen printDiagramm
. Diese Methode soll aufgerufen werden, wenn der Benutzer den Print-Button anklickt.
-(IBAction)printDiagramm:(id)sender;
Die Implementierung sieht folgendermaßen aus:
-(IBAction)printDiagramm:(id)sender {
NSPrintInfo *printInfo = [NSPrintInfo sharedPrintInfo];
[printInfo setOrientation:1];
[printInfo setHorizontallyCentered:YES];
[printInfo setVerticallyCentered:YES];
[printInfo setRightMargin:0];
[printInfo setLeftMargin:0];
[printInfo setTopMargin:0];
[printInfo setBottomMargin:0];
NSData *pdfRepresentation = [view.hostedLayer dataForPDFRepresentationOfLayer];
PDFView* pdfView = [[PDFView alloc] initWithFrame: NSMakeRect(1.0f, 1.0f, printInfo.paperSize.width, printInfo.paperSize.height)];
PDFDocument *pdfDocument = [[PDFDocument alloc] initWithData:pdfRepresentation];
[pdfView setDocument: pdfDocument];
NSWindow *window = [[NSWindow alloc]init];
[window setFrame:NSMakeRect(1.0f, 1.0f, printInfo.paperSize.width, printInfo.paperSize.height) display:false];
[window setContentView:pdfView];
NSPrintOperation *printOp;
printOp = [NSPrintOperation printOperationWithView:pdfView printInfo:printInfo];
[printOp runOperation];
}
Als erstes wird das Objekt NSPrintInfo
mit Werten belegt. Danach wird eine Funktionalität von CorePlot benutzt, um über den hostedLayer
der view
eine PDF-Representation des Diagramms abzufragen. Der Rückgabewert ist einfach ein Objekt vom Typ NSData
. Diese Daten werden verwendet, um ein PDFDocument
zu erstellen, welches dann einer PDFView
zugewiesen wird. Obwohl das NSWindow
auf den ersten Blick unnötigt aussieht, wird es benötigt. Läßt man es weg, dann würde das Ausdrucken nicht funktionieren, weil eine NSView
nicht ohne NSWindow
verwendet werden sollte. Abschließend wird einfach eine NSPrintOperation
erstellt und gestartet.
In der Lösung oben wurde zum Drucken eine NSPrintOperation
verwendet. Allerdings besitzt die PDFView
auch eine eigene print-Methode. Um diese zu nutzen sind folgende Anpassungen notwendig.
-(IBAction)printDiagramm:(id)sender {
NSPrintInfo *printInfo = [NSPrintInfo sharedPrintInfo];
[printInfo setOrientation:1];
[printInfo setHorizontallyCentered:YES];
[printInfo setVerticallyCentered:YES];
[printInfo setRightMargin:0];
[printInfo setLeftMargin:0];
[printInfo setTopMargin:0];
[printInfo setBottomMargin:0];
NSData *pdfRepresentation = [view.hostedLayer dataForPDFRepresentationOfLayer];
PDFDocument *pdfDoc = [[[PDFDocument alloc] initWithData: pdfRepresentation] autorelease];
PDFView *pdfView = [[[PDFView alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)] autorelease];
[[[NSApp keyWindow] contentView] addSubview: pdfView];
[pdfView setDocument: pdfDoc];
[pdfView print: nil];
}
Der Unterschied zu der Lösung oben ist, daß über die globale Konstante NSApp
auf das NSWindow
zugegriffen wird, welches zur Zeit Tastatur-Events empfängt. Zudem wird zur contentView
eine Subview
hinzugefügt. Nach dem Setzen des PDF-Dokuments pdfDoc
kann dann der Print-Befehl der pdfView
aufgerufen werden. Diese Lösung wurde von Chris im OS X Entwicklerforum vorgeschlagen.
Im Folgenden wir eine weitere Möglichkeit zum Drucken gezeigt. Die Lösung kommt ohne PDFView
aus. Allerdings gibt es Nachteile, die auch erwähnt werden.
Was man sich natürlich fragt ist: Warum braucht man die PDFView
überhaupt? Die Antwort ist: Eigentlich nur um die NSPrintOperation
zu erstellen. Die nächste Frage ist: Warum kann das PDFDocument
das eigentlich nicht? Die Antwort ist: Die Klasse kann es, aber die Methode ist private
.
Es existiert also eine Methode - (NSPrintOperation *)getPrintOperationForPrintInfo:(NSPrintInfo *)printInfo autoRotate:(BOOL)doRotate
in der Klasse PDFDocument
. Diese Information habe ich von How to print a PDF with Cocoa und einen Artikel bei Stackoverflow.
Um die private Methode zu verwenden, muß einfach eine Category in der Klasse definiert werden. Für das Beispiel heißt das, daß die Category am Beginn der Datei CorePlotLiniendiagramm.m oder CorePlotBalkendiagramm.m eingefügt werden muß. Falls man die Category nicht angibt, dann wird der Compiler eine Warning anzeigen.
@interface PDFDocument (PDFPrintInfo)
- (NSPrintOperation *)getPrintOperationForPrintInfo:(NSPrintInfo *)printInfo autoRotate:(BOOL)doRotate;
@end
Nun läßt sich die Methode direkt aufrufen. Die Implementierung für die Methode printDiagramm
sieht nun so aus:
-(IBAction)printDiagramm:(id)sender {
NSPrintInfo *printInfo = [NSPrintInfo sharedPrintInfo];
[printInfo setOrientation:1];
[printInfo setHorizontallyCentered:YES];
[printInfo setVerticallyCentered:YES];
[printInfo setRightMargin:0];
[printInfo setLeftMargin:0];
[printInfo setTopMargin:0];
[printInfo setBottomMargin:0];
NSData *pdfRepresentation = [view.hostedLayer dataForPDFRepresentationOfLayer];
PDFDocument *pdfDocument = [[PDFDocument alloc] initWithData:pdfRepresentation];
NSPrintOperation *printOp = [pdfDocument getPrintOperationForPrintInfo:printInfo autoRotate:YES];
[printOp runOperation];
}
Als erstes werden wieder die Parameter für NSPrintInfo
gesetzt. Danach wird die PDF-Repräsentation des CorePlot-Diagramms ausgelesen und daraus ein PDFDocument
erstellt. Dieses liefert als Rückgabewert der Methode getPrintOperationForPrintInfo:autoRotate:
die gewünschte NSPrintOperation
. Zu beachten ist natürlich, daß es sich um eine private Methode handelt, die Apple jederzeit ändern kann und der Code dann nicht mehr funktioniert! Mit Xcode 3.2.4 klappt es aber.
Als letztes muß jetzt nur noch der Button in die Oberfläche eingefügt werden, so daß die Methode printDiagramm
aufgerufen werden kann. Dazu wird der InterfaceBuilder geöffnet und es wird ein Button mit dem Titel Print eingefügt.
Der neue Button wird nun mit der Methode printDiagramm
verbunden. Dazu wird im InterfaceBuilder das MainMenu.xib ausgewählt, der Push Button (Print) selektiert und mittels crtl-drag auf den AppDelegate gezogen. Dort wird printDiagramm ausgewählt.
Nach dem Speichern und Kompilieren läßt sich nun das CorePlot-Diagramm drucken. Wenn die Anwendung gestartet ist und der Button Print angeklickt wird, dann sollte ungefähr folgendes zu sehen sein:
Wird dort beispielsweise Preview ausgewählt, dann wird die Vorschau mit dem CorePlot-Diagramm angezeigt.