iOS打印界面会导致自定义视图出现转换问题

时间:2014-01-21 16:40:23

标签: objective-c printing core-graphics drawrect uiprintpagerenderer

我现在用iOS的打印界面挣扎了很长时间。我所拥有的是一个带有自定义UIView的视图控制器,它在drawRect中执行绘制:

此处的图形在iPad上的示例(包括一些彩色测试线):

enter image description here

我已经设置了一个打印页眉和页脚的UIPrintRenderer子类,并提供内容rect drawContentForPageAtIndex:inRect:for custom UIView的drawRect:方法。调试器告诉我,drawRect:总是被调用 - 如果rect drawContentForPageAtIndex:inRect:在渲染器类中是否存在则无关紧要。所以我不在rect drawContentForPageAtIndex:inRect中绘图:我使用drawRect:for。

当打印机模拟器提供A4纸张格式时,我得到的纸张为[0,0,841.89,595.276],可打印的矩形为[11.9905,11.9906,817.909,571.294],内容矩形为[11.9905,33.9906, 817.909,530.294]。我真的不明白的是,drawRect:从打印界面获取[0,0,695,530.294]的矩形。这是内容rect的高度,但宽度是绘制正常完成时从矩形开始的宽度:[0,0,695,648]。它在模拟器输出页面中看起来像这样(当然还添加了一些测试框架,线条和圆圈):

enter image description here

这里有一些代码,首先是视图控制器中设置打印的部分:

- (IBAction)pressedPrint:(id)sender
{
    UIPrintInteractionController*   printCtrl = [UIPrintInteractionController sharedPrintController];
    UIPrintInfo*                    printInfo = [UIPrintInfo printInfo];

    NSLog(@"%s prepare printing parameters etc.", __func__);
    printCtrl.delegate       = self;
    printInfo.outputType     = UIPrintInfoOutputPhoto;
    printInfo.orientation    = UIPrintInfoOrientationLandscape;
    printInfo.jobName        = [NSString stringWithFormat:@"%@ - %@", NSLocalizedString(@"NavTitleMain", @""), self.title];
    printInfo.duplex         = UIPrintInfoDuplexNone;
    printCtrl.printInfo      = printInfo;
    printCtrl.showsPageRange = NO;

    // This code uses a custom UIPrintPageRenderer so that it can draw a header and footer.
    DVPrintPageRenderer*    myRenderer = [[DVPrintPageRenderer alloc] init];

    // The DVPrintPageRenderer class provides a jobtitle that it will label each page with.
    myRenderer.jobTitle      = printInfo.jobName;
    myRenderer.isTwoPageView = NO;
    myRenderer.footerText    = [centralDocument sharedInstance].docTitle;
    myRenderer.drawView      = self.drawArea;

    // To draw the content of each page, a UIViewPrintFormatter is used.
    UIViewPrintFormatter*   viewFormatter = [self.drawArea viewPrintFormatter];
    UIFont*                 titleFont     = [UIFont fontWithName:@"Helvetica" size:HEADER_TEXT_HEIGHT];
    CGSize                  titleSize     = [myRenderer.jobTitle getSizeWithFont:titleFont];
    UIFont*                 footerFont    = [UIFont fontWithName:@"Helvetica" size:FOOTER_TEXT_HEIGHT];
    CGSize                  footerSize    = [myRenderer.footerText getSizeWithFont:footerFont];

    viewFormatter.startPage = 0;
    myRenderer.headerHeight = titleSize.height  + HEADER_FOOTER_MARGIN_PADDING;
    myRenderer.footerHeight = footerSize.height + HEADER_FOOTER_MARGIN_PADDING;
    [myRenderer addPrintFormatter:viewFormatter startingAtPageAtIndex:0];
    // Set our custom renderer as the printPageRenderer for the print job.
    printCtrl.printPageRenderer = myRenderer;
    drawArea.isPrinting     = YES;
    drawArea.printRenderer  = myRenderer;
    NSLog(@"%s before presenting the printer dialog", __func__);

    void (^completionHandler)(UIPrintInteractionController*, BOOL, NSError*) =
    ^(UIPrintInteractionController* printController, BOOL completed, NSError* error)
    {
        drawArea.isPrinting    = NO;
        drawArea.printRenderer = nil;

        if (!completed && error)
        {
            NSLog(@"Printing could not complete because of error: %@", error);
        }
    };

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
    {
        [printCtrl presentFromBarButtonItem:sender animated:YES completionHandler:completionHandler];
    }
    else
    {
        [printCtrl presentAnimated:YES completionHandler:completionHandler];
    }
}

drawRect的第一个代码部分:也可能与此相关:

- (void)drawRect: (CGRect)rect
{
    // Drawing code.
    NSLog(@"%s entered for %@", __func__, (isPrinting ? @"printing" : @"screen drawing"));
    NSInteger   colorCount = (colorArray  == nil) ? -1 : [colorArray count];
    NSInteger   graphCount = (graphPoints == nil) ? -1 : [graphPoints count];

    CGContextRef    context = UIGraphicsGetCurrentContext();

    CGContextSaveGState(context);

    if (isPrinting)
    {
        // make sure that we'll print the frame rectangle
        CGRect  newRect = CGRectInset(printRenderer.rectContent, 2.0, 2.0);

        saveGraphRect = drawGraphRect;
        saveBackColor = viewBackColor;
        saveTextColor = viewBackColor;
        CGContextTranslateCTM(context, printRenderer.rectContent.origin.x, -printRenderer.rectContent.origin.y);
        NSLog(@"%s content = [%g, %g, %g, %g]", __func__,
              printRenderer.rectContent.origin.x, printRenderer.rectContent.origin.y,
              printRenderer.rectContent.size.width, printRenderer.rectContent.size.height);
        NSLog(@"%s rect(1) = [%g, %g, %g, %g]", __func__,
              rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
        rect          = CGRectMake(ceilf(newRect.origin.x),    ceilf(newRect.origin.y),
                                   floorf(newRect.size.width), floorf(newRect.size.height));
        CGContextTranslateCTM(context, rect.origin.x, rect.origin.y);
        rect.origin   = CGPointMake(0.0, 0.0);

        usedBounds    = rect;
        drawGraphRect = [self makeInsetDrawRectFrom:rect];
        viewBackColor = [UIColor whiteColor];
        axisTextColor = [UIColor blackColor];
        NSLog(@"%s prepared for printing", __func__);
        NSLog(@"%s bounds = [%g, %g, %g, %g]", __func__,
              self.bounds.origin.x, self.bounds.origin.y,
              self.bounds.size.width, self.bounds.size.height);
        NSLog(@"%s paper = [%g, %g, %g, %g]", __func__,
              printRenderer.paperRect.origin.x, printRenderer.paperRect.origin.y,
              printRenderer.paperRect.size.width, printRenderer.paperRect.size.height);
        NSLog(@"%s rect(2) = [%g, %g, %g, %g]", __func__,
              rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
        NSLog(@"%s draw = [%g, %g, %g, %g]", __func__,
              drawGraphRect.origin.x, drawGraphRect.origin.y, drawGraphRect.size.width, drawGraphRect.size.height);
    }
    else
    {
        usedBounds = self.bounds;
        ...

drawRect:中的rects的NSLog输出也是相关的:

2014-01-21 17:15:39.906 iELANA[19015:a0b] -[DrawView drawRect:] entered for screen drawing
2014-01-21 17:15:39.906 iELANA[19015:a0b] -[DrawView drawRect:] bounds = [0, 0, 695, 648]
2014-01-21 17:15:39.906 iELANA[19015:a0b] -[DrawView drawRect:] rect = [0, 0, 695, 648]
2014-01-21 17:15:39.906 iELANA[19015:a0b] -[DrawView drawRect:] draw = [62, 2, 631, 584]
2014-01-21 17:15:40.645 iELANA[19015:a0b] -[viewDiagram pressedPrint:] prepare printing parameters etc.
2014-01-21 17:15:40.645 iELANA[19015:a0b] -[viewDiagram pressedPrint:] before presenting the printer dialog
2014-01-21 17:15:46.131 iELANA[19015:a0b] printableRect = [11.9905, 11.9906, 817.909, 571.294]
2014-01-21 17:15:46.131 iELANA[19015:a0b] headerRect = [11.9905, 11.9906, 817.909, 22]
2014-01-21 17:15:46.131 iELANA[19015:a0b] header text size = [297, 17]
2014-01-21 17:15:46.133 iELANA[19015:a0b] -[DVPrintPageRenderer drawContentForPageAtIndex:inRect:] page = 0, rect [11.9905, 33.9906, 817.909, 530.294]
2014-01-21 17:15:46.133 iELANA[19015:a0b] paper     rect = [0, 0, 841.89, 595.276]
2014-01-21 17:15:46.133 iELANA[19015:a0b] printable rect = [11.9905, 11.9906, 817.909, 571.294]
2014-01-21 17:15:46.133 iELANA[19015:a0b] -[DrawView drawRect:] entered for printing
2014-01-21 17:15:46.134 iELANA[19015:a0b] -[DrawView drawRect:] content = [11.9905, 33.9906, 817.909, 530.294]
2014-01-21 17:15:46.134 iELANA[19015:a0b] -[DrawView drawRect:] rect(1) = [0, 0, 695, 530.294]
2014-01-21 17:15:46.134 iELANA[19015:a0b] -[DrawView drawRect:] prepared for printing
2014-01-21 17:15:46.134 iELANA[19015:a0b] -[DrawView drawRect:] bounds = [0, 0, 695, 648]
2014-01-21 17:15:46.134 iELANA[19015:a0b] -[DrawView drawRect:] paper = [0, 0, 841.89, 595.276]
2014-01-21 17:15:46.134 iELANA[19015:a0b] -[DrawView drawRect:] rect(2) = [0, 0, 813, 526]
2014-01-21 17:15:46.135 iELANA[19015:a0b] -[DrawView drawRect:] draw = [62, 2, 749, 462]
2014-01-21 17:15:46.140 iELANA[19015:a0b] printableRect = [11.9905, 11.9906, 817.909, 571.294]
2014-01-21 17:15:46.140 iELANA[19015:a0b] footerRect = [11.9905, 564.285, 817.909, 19]
2014-01-21 17:15:46.140 iELANA[19015:a0b] footer text size = [296, 14]

绘图代码使用Core Graphics和UIKit来表示帧,线等。文本输出使用Core Text。因此,仍然有一些工作应用正确的CTM转换。但主要的第一个问题是:

为什么drawRect:从打印界面获取一个奇怪的矩形而不是内容rect?如果我不能打破这个规则,那么对于iPhone打印而不是iPad而言,情况会非常糟糕 - 使用当前编码的iPhone看起来更糟糕。

我是否应该在打印渲染器的drawContentForPageAtIndex中更好地打印绘图代码:inRect:?

非常感谢您的帮助: - )

Konran

加了:

似乎确实如此,传递给drawRect的图形上下文不能用于打印要求。当我直接从drawRect返回时:如果是打印并将打印代码添加到drawContentForPageAtIndex:inRect :,我得到一个更好的(测试)输出:

enter image description here

但我不明白的地方以及我仍然需要你帮助的地方是轴文本的定位代码,它是用Core Text绘制的。我尝试过非常不同的CTM转换组合......但是我使用的任何组合都会导致文本的错误定位。这就是我为x轴上的文本输出所做的:

- (void)drawAxisValueXin: (CGContextRef)context withText: (NSString*)axisValueX dockToPoint: (CGPoint)dockPoint
{
    BOOL        isRetina  = (!isPrinting && AfxGetApp().isRetina);
    CGFloat     rMul      = (isRetina) ? 2.0 : 1.0;
    CGFloat     fontSize  = 10.0;
    CTFontRef   helvetica = CTFontCreateWithName(CFSTR("Helvetica"), fontSize, NULL);

    if (isRetina)
    {
        dockPoint.x *= 2.0;
        dockPoint.y *= 2.0;
    }

    // flip the coordinate system
    CGContextSaveGState(context);

    if (isPrinting)
    {
        CGContextSetTextMatrix(context, CGAffineTransformIdentity);
        CGContextTranslateCTM(context, 0.0f, CGRectGetHeight(printRenderer.paperRect));
        CGContextScaleCTM(context, 1.0f, -1.0f);
    }
    else
    {
        CGContextSetTextMatrix(context, CGAffineTransformIdentity);
        CGContextTranslateCTM(context, 0.0f, CGRectGetHeight(usedBounds) * rMul);
        CGContextScaleCTM(context, 1.0f, -1.0f);
    }

    // make the attributed string
    NSMutableAttributedString*  attrString = [[NSMutableAttributedString alloc] initWithString:axisValueX];
    NSRange                     strRange   = NSMakeRange(0, [attrString length]);

    [attrString addAttribute: (id)kCTFontAttributeName
                       value: (__bridge id)helvetica
                       range: strRange];
    [attrString addAttribute: (id)kCTForegroundColorAttributeName
                       value: (id)axisTextColor.CGColor
                       range: strRange];

    // draw the text
    CTLineRef   ctLine    = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString);
    CGFloat     ascent;
    CGFloat     descent;
    CGFloat     width     = ceilf(CTLineGetTypographicBounds(ctLine, &ascent, &descent, NULL));
    CGFloat     height    = ceilf(ascent + descent);
    CGSize      textSize  = CGSizeMake(width, height);
    CGPoint     atPoint   = CGPointMake(dockPoint.x + (1.0 - textSize.width) * rMul,
                                        dockPoint.y + (10.0 - 2.0) * rMul); // x = text right edge, y = text mid
    CGPoint     userPoint = CGContextConvertPointToUserSpace(context, atPoint);

    if (fabsf(tiltLeftAxisX) > 1.0e-8)
    {
        CGContextRotateCTM(context, -tiltLeftAxisX);
        dockPoint.y += 2.0 * rMul;      // offset y 2 points down
        dockPoint = CGContextConvertPointToUserSpace(context, dockPoint);
        dockPoint.x = nearbyintf(dockPoint.x);
        dockPoint.y = nearbyintf(dockPoint.y);
        userPoint = CGPointMake(dockPoint.x - width,
                                dockPoint.y - height / 2.0);
    }

    CGContextSetTextPosition(context, userPoint.x, userPoint.y);
    CTLineDraw(ctLine, context);

    CGRect  rectTempl = CGRectMake(-5.0, -5.0, 10.0, 10.0);
    CGRect  rectDot   = CGRectOffset(rectTempl, userPoint.x, userPoint.y);

    [[UIColor redColor] set];
    CGContextFillEllipseInRect(context, rectDot);
    rectDot = CGRectOffset(rectTempl, atPoint.x, atPoint.y);
    [[UIColor greenColor] set];
    CGContextFillEllipseInRect(context, rectDot);

    // clean up
    CFRelease(ctLine);
    CFRelease(helvetica);
    CGContextRestoreGState(context);
}

1 个答案:

答案 0 :(得分:0)

打印上下文与绘图上下文非常不同。所以最终有效的上述方法的结论就像这样:

- (void)drawAxisValueXin: (CGContextRef)context withText: (NSString*)axisValueX dockToPoint: (CGPoint)dockPoint
{
    BOOL        isRetina  = (!isPrinting && AfxGetApp().isRetina);
    CGFloat     rMul      = (isRetina) ? 2.0 : 1.0;
    CGFloat     fontSize  = 10.0;
    CTFontRef   helvetica = CTFontCreateWithName(CFSTR("Helvetica"), fontSize, NULL);

    if (isRetina)
    {
        dockPoint.x *= 2.0;
        dockPoint.y *= 2.0;
    }
    else if (isPrinting)
    {
        dockPoint.y += 2.0;
    }

    // flip the coordinate system
    CGContextSaveGState(context);

    if (isPrinting)
    {
        CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1.0, -1.0));
    }
    else
    {
        CGContextSetTextMatrix(context, CGAffineTransformIdentity);
        CGContextTranslateCTM(context, 0.0f, CGRectGetHeight(usedBounds) * rMul);
        CGContextScaleCTM(context, 1.0f, -1.0f);
    }

    // make the attributed string
    NSMutableAttributedString*  attrString = [[NSMutableAttributedString alloc] initWithString:axisValueX];
    NSRange                     strRange   = NSMakeRange(0, [attrString length]);

    [attrString addAttribute: (id)kCTFontAttributeName
                       value: (__bridge id)helvetica
                       range: strRange];
    [attrString addAttribute: (id)kCTForegroundColorAttributeName
                       value: (id)axisTextColor.CGColor
                       range: strRange];

    // draw the text
    CTLineRef   ctLine    = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString);
    CGFloat     ascent;
    CGFloat     descent;
    CGFloat     width     = ceilf(CTLineGetTypographicBounds(ctLine, &ascent, &descent, NULL));
    CGFloat     height    = ceilf(ascent + descent);
    CGSize      textSize  = CGSizeMake(width, height);
    CGPoint     atPoint   = CGPointMake(dockPoint.x + (1.0 - textSize.width) * rMul,
                                        dockPoint.y + (10.0 - 2.0) * rMul); // x = text right edge, y = text mid
    CGPoint     userPoint = (isPrinting) ? atPoint : CGContextConvertPointToUserSpace(context, atPoint);

    if (fabsf(tiltLeftAxisX) > 1.0e-8)
    {
        if (isPrinting)
        {
            CGFloat xMove = textSize.height * sin(tiltLeftAxisX);
            CGFloat yMove = textSize.width  * sin(tiltLeftAxisX);

            userPoint.x -= xMove;
            userPoint.y -= yMove;
            userPoint    = CGContextConvertPointToDeviceSpace(context, userPoint);
            CGContextRotateCTM(context, tiltLeftAxisX);
            userPoint    = CGContextConvertPointToUserSpace(context, userPoint);
        }
        else
        {
            CGContextRotateCTM(context, -tiltLeftAxisX);
            dockPoint.y += 2.0 * rMul;      // offset y 2 points down
            dockPoint    = CGContextConvertPointToUserSpace(context, dockPoint);
            dockPoint.x  = nearbyintf(dockPoint.x);
            dockPoint.y  = nearbyintf(dockPoint.y);
            userPoint    = CGPointMake(dockPoint.x - width,
                                       dockPoint.y - height / 2.0);
        }
    }

    CGContextSetTextPosition(context, userPoint.x, userPoint.y);
    CTLineDraw(ctLine, context);

    // clean up
    CFRelease(ctLine);
    CFRelease(helvetica);
    CGContextRestoreGState(context);
}