5

我有一个我正在开发的可可应用程序,我在其中创建了一个我想发送到打印机的 customView。在子类 NSView 中,我也设置了一些框架选项,代码如下。我有 2 个全局变量来保存在 main() 函数之外声明的打印信息。

- (id)initWithFrame:(NSRect)frame
{
    extern NSPrintInfo *globalPrintInfo;
    extern NSPrintOperation *globalPrintOperation;

    //Modify the frame before it's sent to it's super method.  Also set the global variables to there default values.
    globalPrintOperation = [NSPrintOperation printOperationWithView:self];
    globalPrintInfo = [globalPrintOperation printInfo];//Get the print information from it.

    [globalPrintInfo setBottomMargin:0.0];
    [globalPrintInfo setLeftMargin:0.0];
    [globalPrintInfo setTopMargin:0.0];
    [globalPrintInfo setRightMargin:0.0];

    [globalPrintOperation setPrintInfo:globalPrintInfo];//save the printInfo changes.

    //modify the frame to reflect the correct height & width of the paper.
    frame.size.height = globalPrintInfo.paperSize.height-globalPrintInfo.topMargin-globalPrintInfo.bottomMargin;
    frame.size.width = globalPrintInfo.paperSize.width-globalPrintInfo.leftMargin-globalPrintInfo.rightMargin;
    frame.origin.x=0;
    frame.origin.y=0;

    NSLog(@"Printer Name=%@, Printer Type=%@",globalPrintInfo.printer.name,globalPrintInfo.printer.type);

    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code here.

    }

    return self;
}

对于子类 NSView 以便我可以看到它的边界,我将以下代码添加到它的 drawRect 方法中。

- (void)drawRect:(NSRect)dirtyRect
{
    if ( [NSGraphicsContext currentContextDrawingToScreen] ) {
        NSLog(@"Drawing To Screen");
    } else {
        NSLog(@"Drawing To Printer");
    }

    // Draw common elements here

    CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];

    //Set color of drawing to green, and fill the rectangle green, so we can see it's boundaries.
    [[NSColor greenColor] setFill];
    NSRectFill(dirtyRect);

    CGContextSelectFont(myContext, "Helvetica-Bold", 18, kCGEncodingMacRoman);
    CGContextSetCharacterSpacing(myContext, 10);
    CGContextSetTextDrawingMode(myContext, kCGTextFillStroke);

    CGContextSetRGBFillColor(myContext, 0, 0, 0, 1);//black
    CGContextSetRGBStrokeColor (myContext, 0, 0, 1, 1);//blue stroke
    CGContextShowTextAtPoint(myContext, 40, 0, "Here is some text!", 18);

}

当我去使用全局变量运行打印操作时,就像这样......

- (IBAction)print:(id)sender {
    NSLog(@"Testing Print");

    extern NSPrintOperation *globalPrintOperation;

    [globalPrintOperation runOperation];
}

我得到了打印窗口,我看到了我的“绿色背景”,但由于某种原因,它被分成了 2 页。我不确定到底发生了什么,因为我将框架的宽度和高度设置为 pagesize.width 和高度,感谢任何帮助。我看到的一些图片如下。

我的猜测是页面大小的宽度和高度与用于定义视图框架的像素单位类型的单位不同。

我的最终目标是制作一个程序,让用户选择他们想要的内容并根据他们选择的选项打印特定页面,但首先我必须弄清楚如何将我期望的“内容”放到“1”页面上'2'。我可以通过实验手动计算出宽度和高度,但对于我假设的不同纸张尺寸,这不会是非常动态的。

提前致谢。

图片1

图片2

编辑 ***

我刚刚将我的代码编辑到下面的子类 NSVIEW

//METHOD OVERIDES
- (id)initWithFrame:(NSRect)frame
{
    extern NSPrintInfo *globalPrintInfo;
    extern NSPrintOperation *globalPrintOperation;

    //Modify the frame before it's sent to it's super method.  Also set the global variables to there default values.
    globalPrintOperation = [NSPrintOperation printOperationWithView:self];//use whatever is currently there as the default print operation.
    globalPrintInfo = [globalPrintOperation printInfo];//Get the print information from it.

    [globalPrintInfo setBottomMargin:0.0];
    [globalPrintInfo setLeftMargin:0.0];
    [globalPrintInfo setTopMargin:0.0];
    [globalPrintInfo setRightMargin:0.0];

    [globalPrintOperation setPrintInfo:globalPrintInfo];//save the printInfo changes.

    //modify the frame to reflect the correct height & width of the paper.
    frame.size.height = (globalPrintInfo.paperSize.height-globalPrintInfo.topMargin-globalPrintInfo.bottomMargin);
    frame.size.width = globalPrintInfo.paperSize.width-globalPrintInfo.leftMargin-globalPrintInfo.rightMargin;
    frame.origin.x=0;
    frame.origin.y=0;

    NSLog(@"Printer Name=%@, Printer Type=%@",globalPrintInfo.printer.name,globalPrintInfo.printer.type);

    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code here.

    }

    return self;
}

- (void)drawRect:(NSRect)dirtyRect
{
    if ( [NSGraphicsContext currentContextDrawingToScreen] ) {
        NSLog(@"Drawing To Screen");
    } else {
        NSLog(@"Drawing To Printer");
    }

    // Draw common elements here

    CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];

    //Set color of drawing to green, and fill the rectangle green, so we can see it's boundaries.
    [[NSColor greenColor] setFill];
    NSRectFill(dirtyRect);

    CGContextSelectFont(myContext, "Helvetica-Bold", 18, kCGEncodingMacRoman);
    CGContextSetCharacterSpacing(myContext, 10);
    CGContextSetTextDrawingMode(myContext, kCGTextFillStroke);

    CGContextSetRGBFillColor(myContext, 0, 0, 0, 1);//black
    CGContextSetRGBStrokeColor (myContext, 0, 0, 1, 1);//blue stroke
    CGContextShowTextAtPoint(myContext, 40, 0, "Here is some text!", 18);

}


- (BOOL)knowsPageRange:(NSRangePointer)range {
    NSRect bounds = [self bounds];
    float printHeight = [self calculatePrintHeight];

    range->location = 1;
    range->length = NSHeight(bounds) / printHeight + 1;

    NSLog(@"Calculated Page Range");
    return YES;
}

// Return the drawing rectangle for a particular page number
- (NSRect)rectForPage:(int)page {
    NSRect bounds = [self bounds];
    float pageHeight = [self calculatePrintHeight];
    NSLog(@"Created Rect For View");
    return NSMakeRect( NSMinX(bounds), NSMaxY(bounds) - page * pageHeight,
                      NSWidth(bounds), pageHeight );
}

//CUSTOM METHODS

// Calculate the vertical size of the view that fits on a single page
- (float)calculatePrintHeight {

    extern NSPrintInfo *globalPrintInfo;
    extern NSPrintOperation *globalPrintOperation;

    // Obtain the print info object for the current operation

    // Calculate the page height in points
    NSSize paperSize = [globalPrintInfo paperSize];
    float pageHeight = paperSize.height - [globalPrintInfo topMargin] - [globalPrintInfo bottomMargin];

    // Convert height to the scaled view
    float scale = [[[globalPrintInfo dictionary] objectForKey:NSPrintScalingFactor]
                   floatValue];

    NSLog(@"Calculated Print Height:%f",(pageHeight/scale));
    return (pageHeight / scale);
}

@end

我现在能够得到我想要的东西,当我去打印预览时接受它仍然认为由于某种原因还有第二页?现在不知道为什么。我把我看到的上传...

请注意它是如何说 1 of 2 的?第二页只是空白的白色。

在此处输入图像描述

4

4 回答 4

2

所以我改进了我的打印类,使其对许多页面更加灵活,并希望共享代码。我仍然在底部有那个烦人的白色边框,我不太清楚,但是当我去打印时,它似乎不存在?所以我需要一些帮助来解决这个问题,但除此之外,我设计了一个类,你只需向它发送一组视图,它就会按照你收到它们的顺序打印视图。

为此,我创建了 2 个类,PSPrint 和 PSPrintView。两者都是 NSView 的子类

这是 PSPrint.h 和 PSPrint.m 的代码

#import <Foundation/Foundation.h>
#import "PSPrintView.h"

@interface PSPrint : NSView
@property NSMutableArray *printViews;
@property (strong) NSPrintOperation *printOperation;
- (void)printTheViews;
@end

#import "PSPrint.h"
#import "PSPrintView.h"

@implementation PSPrint

- (id)initWithFrame:(NSRect)frame
{
    NSLog(@"Initializing Main PSPrintView");
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code here.
        _printViews = [[NSMutableArray alloc]initWithCapacity:1];//start it with capacity of 1
    }

    return self;
}

- (void)drawRect:(NSRect)dirtyRect{

}

- (BOOL)knowsPageRange:(NSRangePointer)range {
    NSRect bounds = [self bounds];
    float printHeight = [self calculatePrintHeight];

    range->location = 1;
    range->length = NSHeight(bounds) / printHeight + 0;

    NSLog(@"Calculated Page Range");
    return YES;
}

- (void)printTheViews{
    NSLog(@"Starting printTheViews Function of PSPrint");

    NSPrintInfo *sharedPrintInfo = [NSPrintInfo sharedPrintInfo];

    NSUInteger numOfViews = _printViews.count;

     NSLog(@"Creating %ld SubViews",numOfViews);

    NSUInteger totalHeight = 0;//if not initialized to 0 weird problems occur after '3' clicks to print, TODO: Find out why? Maybe because address space in memory not guaranteed to be 0 again?
    NSUInteger heightOfView = 0;
    PSPrintView *tempView;

    for (NSUInteger i=0; i<numOfViews; i++) {
        tempView = [_printViews objectAtIndex:i];
        heightOfView = tempView.frame.size.height;
        totalHeight = totalHeight + heightOfView;
    }

    //Change the frame size to reflect the amount of pages.
    NSSize newsize;
    newsize.width = sharedPrintInfo.paperSize.width-sharedPrintInfo.leftMargin-sharedPrintInfo.rightMargin;
    newsize.height = totalHeight;
    [self setFrameSize:newsize];

    NSLog(@"Total Height Of Main Print View Is %f",_frame.size.height);

    NSInteger incrementor=-1;//default the incrementor for the loop below.  This controls what page a 'view' will appear on.

    //Add the views in reverse, because the Y position is bottom not top.  So Page 3 will have y coordinate of 0.  Doing this so order views is placed in array reflects what is printed.
    for (NSInteger i=numOfViews-1; i>=0; i--) {
        incrementor++;
        tempView = [_printViews objectAtIndex:i];//starts with the last item added to the array, in this case rectangles, and then does circle and square.
        heightOfView = tempView.frame.size.height;

        NSPoint origin;
        origin.x = 0;
        origin.y = heightOfView*incrementor;//So for the rectangle it's placed at position '0', or the very last page.

        [tempView setFrameOrigin:origin];

        [self addSubview:tempView];
    }


    _printOperation = [NSPrintOperation printOperationWithView:self printInfo:sharedPrintInfo];
    [_printOperation runOperation];
}

// Return the drawing rectangle for a particular page number
- (NSRect)rectForPage:(int)page {
    NSRect bounds = [self bounds];
    float pageHeight = [self calculatePrintHeight];
    NSLog(@"Created Rect For View");
    return NSMakeRect( NSMinX(bounds), NSMaxY(bounds) - page * pageHeight,
                      NSWidth(bounds), pageHeight );
}

// Calculate the vertical size of the view that fits on a single page
- (float)calculatePrintHeight {

    NSPrintInfo *sharedPrintInfo = [NSPrintInfo sharedPrintInfo];

    // Obtain the print info object for the current operation

    // Calculate the page height in points
    NSSize paperSize = [sharedPrintInfo paperSize];
    float pageHeight = paperSize.height - [sharedPrintInfo topMargin] - [sharedPrintInfo bottomMargin];

    // Convert height to the scaled view
    float scale = [[[sharedPrintInfo dictionary] objectForKey:NSPrintScalingFactor]
                   floatValue];

    NSLog(@"Calculated Print Height:%f",(pageHeight/scale));
    return (pageHeight / scale);
}

@end

PSPrintView.h 和 PSPrintView.m 的代码如下

#import <Cocoa/Cocoa.h>

@interface PSPrintView : NSView
enum optionsForView{drawRectangle,drawCircle,drawSquare};
@property enum optionsForView myOptions;
- (void)drawSquare;
- (void)drawCircle;
- (void)drawRectangle;
@end


#import "PSPrintView.h"

@implementation PSPrintView

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code here.
    }

    return self;
}

- (void)drawRect:(NSRect)dirtyRect
{
    NSLog(@"Drawing Green View Boundaries");
    NSPrintInfo *sharedPrintInfo = [NSPrintInfo sharedPrintInfo];

    //So we know the boundaries for the page. Remove in actual application.
    [[NSColor greenColor] setFill];
    NSRectFill(dirtyRect);

    NSString *drawType;
    const char *cString;

    if(sharedPrintInfo.orientation == NSPortraitOrientation){
        drawType = @"Portrait";
    }else{
        drawType = @"Landscape";
    }

    cString = [drawType cStringUsingEncoding:NSASCIIStringEncoding];

    //Draw the print mode for reference.
    CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
    CGContextSelectFont(myContext, "Helvetica-Bold", 30, kCGEncodingMacRoman);
    CGContextSetCharacterSpacing(myContext, 10);
    CGContextSetTextDrawingMode(myContext, kCGTextFillStroke);
    CGContextSetRGBFillColor(myContext, 0, 0, 0, 1);//black
    CGContextSetRGBStrokeColor (myContext, 1, 1, 1, 1);//white stroke
    CGContextShowTextAtPoint(myContext, 0, 0, cString, drawType.length);

    //Control what type of page is drawn.
    switch (_myOptions){
        case drawSquare:
            [self drawSquare];
            break;
        case drawCircle:
            [self drawCircle];
            break;
        case drawRectangle:
            [self drawRectangle];
            break;
    }

}

- (void)drawSquare{
    NSLog(@"Drawing Square");

    //Because this function can only be called from drawRect we are guaranteed with the function below to be in the correct graphics context
    CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];

    NSRect redSquare = CGRectMake (0, 0, 200, 200 );
    redSquare.origin.y = self.bounds.size.height-redSquare.size.height;//move it to the top of the page.

    CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);//set to red color
    CGContextFillRect (myContext,redSquare);//Y coordinate set to height to put it in upper left, 200 is total height of the view.

}

-(void)drawCircle{
    NSLog(@"Drawing Circle");

    //Because this function can only be called from drawRect we are guaranteed with the function below to be in the correct graphics context
    CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];

    CGContextSetRGBFillColor (myContext, 1, 0, 1, 1);//set to red color
    NSRect ovalFrame = CGRectMake (0, 0, 200, 200 );

    ovalFrame.origin.x=0;//from within the view it's self.
    ovalFrame.origin.y=0;//at the top of the page.

    CGContextFillEllipseInRect(myContext,ovalFrame);
}

-(void)drawRectangle{
    NSLog(@"Drawing Rectangle");

    //Because this function can only be called from drawRect we are guaranteed with the function below to be in the correct graphics context
    CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];

    NSRect redRectangle = CGRectMake (0, 0, 300, 100 );
    redRectangle.origin.y = self.bounds.size.height-redRectangle.size.height-(self.bounds.size.height/2);//move it to the top of the page.

    CGContextSetRGBFillColor (myContext, 1, 1, 0, 1);//set to red color
    CGContextFillRect (myContext,redRectangle);//Y coordinate set to height to put it in upper left, 200 is total height of the view.
}

@end

下面是我的 AppController 中使用这些类的示例。我只是创建了一个按钮,并在屏幕上为矩形、圆形和正方形设置了 3 个复选框。

这是 appController.h 和 appController.m

#import <Foundation/Foundation.h>
#import "PSPrint.h"
#import "PSPrintView.h"

@interface AppController : NSObject
- (IBAction)printResultsButton:(id)sender;
@property (weak) IBOutlet NSButton *squareCheckBox;
@property (weak) IBOutlet NSButton *circleCheckBox;
@property (weak) IBOutlet NSButton *rectangleCheckBox;
@property (strong) PSPrint *PSPrintObject;
- (IBAction)pageSetup:(id)sender;
@end

#import "AppController.h"
#import "PSPrint.h"
#import "PSPrintView.h"

@implementation AppController

- (IBAction)printResultsButton:(id)sender {
    NSLog(@"Print Button Pressed");

    //First get the shared print info object so we know page sizes.  The shared print info object acts like a global variable.
    NSPrintInfo *sharedPrintInfo = [NSPrintInfo sharedPrintInfo];

    //initialize it's base values.
    sharedPrintInfo.leftMargin = 0;
    sharedPrintInfo.rightMargin = 0;
    sharedPrintInfo.topMargin = 0;
    sharedPrintInfo.bottomMargin = 0;

    PSPrintView *printPageView;
    NSRect frame;
    frame.size.height = sharedPrintInfo.paperSize.height-sharedPrintInfo.topMargin-sharedPrintInfo.bottomMargin;
    frame.size.width = sharedPrintInfo.paperSize.width-sharedPrintInfo.leftMargin-sharedPrintInfo.rightMargin;

    //Initiate the printObject without a frame, it's frame will be decided later.
    _PSPrintObject = [[PSPrint alloc]init];

    //[_PSPrintObject.printViews initWithCapacity:1];//start it off with a capacity of '1'
    if(_squareCheckBox.state == NSOnState){

        //Allocate a new instance of NSView into the variable printPageView
        printPageView =[[PSPrintView alloc] initWithFrame:frame];

        //Set the option for the printView for what it should draw.
        printPageView.myOptions=drawSquare;

        //Finally append the view to the PSPrint Object.
        [_PSPrintObject.printViews addObject:printPageView];

        NSLog(@"Added Square Print View To Mutable Array");
    }

    if(_circleCheckBox.state == NSOnState){
        //Allocate a new instance of NSView into the variable printPageView
        printPageView =[[PSPrintView alloc] initWithFrame:frame];

        //Set the option for the printView for what it should draw.
        printPageView.myOptions=drawCircle;

        //Finally append the view to the PSPrint Object.
        [_PSPrintObject.printViews addObject:printPageView];

        NSLog(@"Added Circle Print View To Mutable Array");
    }

    if(_rectangleCheckBox.state == NSOnState){
        //Allocate a new instance of NSView into the variable printPageView
        printPageView =[[PSPrintView alloc] initWithFrame:frame];

        //Set the option for the printView for what it should draw.
        printPageView.myOptions=drawRectangle;

        //Finally append the view to the PSPrint Object.
        [_PSPrintObject.printViews addObject:printPageView];

        NSLog(@"Added Rectangle Print View To Mutable Array");
    }

    NSLog(@"Attempting to print all views...");
    [_PSPrintObject printTheViews];//print all the views, each view being a 'page'.

}

- (IBAction)pageSetup:(id)sender {
    NSPageLayout *pageLayout = [NSPageLayout pageLayout];

    [pageLayout runModal];//runs the model for the page layout UI.  It saves the global copy of printInfo in printOperation, which can be used to make decisions

}
@end

我希望这可以帮助你们中的一些人在打印方面遇到困难,我花了一些时间来做这件事,但是这些课程让打印变得更容易。

于 2012-12-28T18:42:03.187 回答
1

对付这种固定打印,我真的更喜欢全页模式:请使用 NSFitPagination 而不是 NSAutoPagination。

[myPrintInfo setHorizontalPagination:NSFitPagination];
[myPrintInfo setVerticalPagination:NSFitPagination];

希望这对你有用!

于 2015-04-21T23:54:06.203 回答
1

我不完全确定,但我已经在一定程度上回答了我的问题。在我的代码中(我大部分是从苹果打印示例代码中获得的,其中有部分内容如下

- (BOOL)knowsPageRange:(NSRangePointer)range {
    NSRect bounds = [self bounds];
    float printHeight = [self calculatePrintHeight];

    range->location = 1;
    range->length = NSHeight(bounds) / printHeight + 0;

    NSLog(@"Calculated Page Range");
    return YES;
}

这是子类 NSView 的覆盖方法之一,在苹果代码中它说 printHeight + 1,所以我将其更改为 printHeight + 0,现在页面只显示 1 of 1。

但是我仍然有一个奇怪的问题,它下面似乎还有一些白色边框,我不确定那是什么。如果有人能弄清楚那是什么,请告诉我。它与问题中发布的图像中的白色边框相同,就在上面写着 1 of 1 或 1 of 2 的位置,在下面,“这是一些文字!”

于 2012-12-24T22:03:29.450 回答
0

白条可能是纸张不可打印区域的一部分。经过大量实验后,我可以根据需要在页面上打印视图,但我不仅要确保我的视图大小与 [printinfo paperSize] 给出的页面大小相匹配,还要进一步将其剪辑到 [printinfo] 给出的矩形imageablePageBounds]。(在我的子类的 rectForPage: 方法中。)

如果您提供小于 imageablePageBounds 的任何内容,您将在页面上留出空间,任何更大的内容都可能被剪裁或分页。

imageablePageBounds 和 paperSize 之间的差异空间在打印对话框的预览中显示为空白,并且在页面上未打印(根据您选择的打印机/纸张,这显然是页面的不可打印区域)

于 2015-02-19T11:53:31.443 回答