多页文件可可的打印

时间:2018-07-13 09:38:35

标签: objective-c macos cocoa printing nsdocument

我正在开发基于文档的应用程序。该文档可以有多个页面。所以我有一个可用的NSView对象数组。现在,我想在此应用程序中提供打印功能,但是NSPrintOpertion只接受一个NSView对象,因此我无法生成打印预览以及打印文档的多个页面。

可可中是否可以打印多页文档?

1 个答案:

答案 0 :(得分:0)

不幸的是,可可印刷不是免费的。您必须实现自己的NSView子类来处理图形。它必须有权访问您的数据,并根据页码在每页上绘制正确的数据。

这是一个示例源代码,可以打印任何NSTableView。我在这里使用它来展示您如何计算要在页面上绘制的东西的位置:

MyPrintView.h

#import <Cocoa/Cocoa.h>

@interface MyPrintView : NSView {
    NSString *printJobTitle;
}

@property (copy, readwrite) NSString *printJobTitle;

- (id)initWithTableView:(NSTableView *)tableToPrint andHeader:(NSString *)header;

@end

MyPrintView.m

#import "MyPrintView.h"    

@interface MyPrintView ()

@property (nonatomic, weak) NSTableView *tableToPrint;
@property (nonatomic, strong) NSString *header;
@property (nonatomic, strong) NSDictionary *attributes;
@property (nonatomic, strong) NSFont *listFont;
@property (nonatomic) float headerHeight;
@property (nonatomic) float footerHeight;
@property (nonatomic) float lineHeight;
@property (nonatomic) float entryHeight;
@property (nonatomic) NSRect pageRect;
@property (nonatomic) int linesPerPage;
@property (nonatomic) int currentPage;

@end


@implementation MyPrintView

@synthesize printJobTitle;


- (id)initWithTableView:(NSTableView *)tableToPrint andHeader:(NSString *)header
{
    // Initialize with dummy frame
    self = [super initWithFrame:NSMakeRect(0, 0, 700, 700)];

    if (self) {
        self.tableToPrint = tableToPrint;
        self.header = header;
        self.listFont = [NSFont fontWithName:@"Helvetica Narrow" size:10.0];
        CGFloat x = self.listFont.capHeight;
        x = self.listFont.ascender;
        x = self.listFont.descender;
        self.lineHeight = self.listFont.boundingRectForFont.size.height;
        self.entryHeight = [self.listFont capHeight] * 3;
        self.headerHeight = 20 + self.entryHeight;
        self.footerHeight = 20;
        if (self.listFont) {
            self.attributes = @{ NSFontAttributeName: self.listFont };
        } else {
            self.attributes = nil;
        }

        printJobTitle = @"My Print Job";
    }

    return self;
}


#pragma mark Pagination

- (BOOL)knowsPageRange:(NSRangePointer)range
{
    NSPrintInfo *printInfo = [[NSPrintOperation currentOperation] printInfo];

    self.pageRect = [printInfo imageablePageBounds];

    NSRect newFrame;
    newFrame.origin = NSZeroPoint;
    newFrame.size = [printInfo paperSize];
    [self setFrame:newFrame];

    // Number of lines per page
    self.linesPerPage = (self.pageRect.size.height - self.headerHeight - self.footerHeight) / self.entryHeight - 1;
    // Number of full pages
    NSUInteger noPages = self.tableToPrint.numberOfRows / self.linesPerPage;
    // Rest of lines on last page
    if (self.tableToPrint.numberOfRows % self.linesPerPage > 0) {
        noPages++;
    }

    range->location = 1;
    range->length = noPages;

    return YES;
}


- (NSRect)rectForPage:(NSInteger)page
{
    self.currentPage = (int)page - 1;
    return self.pageRect;
}


- (NSAttributedString *)pageHeader
{
     return [[NSAttributedString alloc] initWithString:self.header];
}


#pragma mark Drawing

- (BOOL)isFlipped
{
    // Origin top left
    return YES;
}

// We need this to find any transformers which are used to display the values of a certain column    
static NSValueTransformer *TransformerFromInfoDict( NSDictionary *dict )
{
    NSDictionary *options = dict[NSOptionsKey];
    if (options == nil) return nil;

    NSValueTransformer *transformer = options[NSValueTransformerBindingOption];

    if (transformer == nil || (id)transformer == [NSNull null]) {
        transformer = nil;
        NSString *name = options[NSValueTransformerNameBindingOption];
        if (name != nil && (id)name != [NSNull null]) {
            transformer = [NSValueTransformer valueTransformerForName: name];
        }
    }

    return transformer;
}

// This is where the drawing takes place
- (void)drawRect:(NSRect)dirtyRect
{
    float margin = 20;
    float leftMargin = self.pageRect.origin.x + margin;
    float topMargin = self.pageRect.origin.y + self.headerHeight;

    [NSBezierPath setDefaultLineWidth:0.25];

    CGFloat originalWidth = 0;
    for (NSTableColumn *col in self.tableToPrint.tableColumns) {
        originalWidth += col.width;
    }
    CGFloat widthQuotient = (self.pageRect.size.width - margin) / originalWidth;

    CGFloat inset = (self.entryHeight - self.lineHeight - 1.0)/2.0;

    // Column titles
    CGFloat horOffset = 0;
    for (NSTableColumn *col in self.tableToPrint.tableColumns) {
        NSRect rect = NSMakeRect(linkerRand + horOffset, topMargin, widthQuotient * spalte.width, self.entryHeight);
        horOffset += widthQuotient * col.width;
        [NSBezierPath strokeRect:rect];
        NSString *theTitle = @"--";
        if ([col respondsToSelector:@selector(title)]) { // OS X 10.10 and higher
            theTitle = col.title;
        } else {
            NSTableHeaderCell *cell = col.headerCell;
            theTitle = cell.title;
        }
        [theTitle drawInRect:NSInsetRect(rect, inset, inset) withAttributes:self.attributes];
    }

    NSUInteger firstEntryOfPage = self.currentPage * self.linesPerPage;
    NSUInteger lastEntryOfPage = ((self.currentPage + 1) * self.linesPerPage) > self.tableToPrint.numberOfRows ? self.tableToPrint.numberOfRows : ((self.currentPage + 1) * self.linesPerPage);

    for (NSUInteger i = 0; i < lastEntryOfPage - firstEntryOfPage; i++) {
        @autoreleasepool {  // to avoid memory hogging
            NSUInteger row = firstEntryOfPage + i;
            CGFloat horOffset = 0;
            for (NSTableColumn *col in self.tableToPrint.tableColumns) {
                NSDictionary *bindingInfo = [spalte infoForBinding: @"value"];
                NSArray *columnValues = [bindingInfo[NSObservedObjectKey] valueForKeyPath: bindingInfo[NSObservedKeyPathKey]];

                NSString *valueAsStr = @"";
                id value = [columnValues objectAtIndex:col];

                if ((value != nil) && (![value isKindOfClass:[NSNull class]])) {
                    // Do we have a transformer for that column? Then transform accordingly.
                    NSValueTransformer *transformer = TransformerFromInfoDict(bindingInfo);
                    if (transformer != nil)
                        value = [transformer transformedValue: value];

                    if ([value isKindOfClass:[NSString class]]) {
                        valueAsStr = value;
                    } else if ([value isKindOfClass:[NSNumber class]]) {
                        NSCell *cell = [col dataCellForRow:zeile];
                        if (cell.formatter != nil) {
                            valueAsStr = [cell.formatter stringForObjectValue:value];
                        } else {
                            valueAsStr = [value stringValue];
                        }
                    } else {
                        // We don't know what that is
                        NSLog(@"value class: %@", [value class]);
                        valueAsStr = @"????!";
                    }
                }

                NSRect rect = NSMakeRect(leftMargin + horOffset, topMargin + (i+1) * self.entryHeight, widthQuotient * col.width, self.entryHeight);
                horOffset += widthQuotient * col.width;

                // Now we can finally draw the entry
                [NSBezierPath strokeRect:rect];
                NSRect stringRect = NSInsetRect(rect, inset, inset);
                [valueAsStr drawInRect:stringRect withAttributes:self.attributes];
            }
        }
    }
}


@end

这是您必须包含在printDocumentWithSettings中的文档类中的内容,当用户选择“打印...”时会调用该类:

    [[self.printInfo dictionary] setValue:@YES forKey:NSPrintHeaderAndFooter];
    NSString *headerLine = @"My first printed Table View";

    MyPrintView *myPrintView = [[MyPrintView alloc] initWithTableView:theTableView andHeader:headerLine];

    NSPrintOperation *op = [NSPrintOperation
                            printOperationWithView:myPrintView
                            printInfo:[self printInfo]];

    [op setShowsPrintPanel:showPrintPanel];

    // Run print operation, which shows the print panel if showPanels was YES
    [self runModalPrintOperation:op
                        delegate:self
                  didRunSelector:nil
                     contextInfo:NULL];