Create a gist now

Instantly share code, notes, and snippets.

//
// RecipePageRenderer.swift
// (c) 2014 Nate Cook, licensed under the MIT License
//
import UIKit
import AVFoundation
/// Units for printing content insets
let POINTS_PER_INCH: CGFloat = 72
/// The alignment for drawing an NSString inside a bounding rectangle.
enum NCStringAlignment {
case LeftTop
case CenterTop
case RightTop
case LeftCenter
case Center
case RightCenter
case LeftBottom
case CenterBottom
case RightBottom
}
extension NSString {
/// Draw the `NSString` inside the bounding rectangle with a given alignment.
func drawAtPointInRect(rect: CGRect, withAttributes attributes: [NSObject: AnyObject]?, andAlignment alignment: NCStringAlignment) {
let size = self.sizeWithAttributes(attributes)
var x, y: CGFloat
switch alignment {
case .LeftTop, .LeftCenter, .LeftBottom:
x = CGRectGetMinX(rect)
case .CenterTop, .Center, .CenterBottom:
x = CGRectGetMidX(rect) - size.width / 2
case .RightTop, .RightCenter, .RightBottom:
x = CGRectGetMaxX(rect) - size.width
}
switch alignment {
case .LeftTop, .CenterTop, .RightTop:
y = CGRectGetMinY(rect)
case .LeftCenter, .Center, .RightCenter:
y = CGRectGetMidY(rect) - size.height / 2
case .LeftBottom, .CenterBottom, .RightBottom:
y = CGRectGetMaxY(rect) - size.height
}
self.drawAtPoint(CGPoint(x: x, y: y), withAttributes: attributes)
}
}
class RecipePrintPageRenderer: UIPrintPageRenderer {
let authorName: NSString
let recipe: Recipe
let pageNumberAttributes = [NSFontAttributeName: UIFont(name: "Georgia-Italic", size: 11)!]
let nameAttributes = [NSFontAttributeName: UIFont(name: "Georgia", size: 11)!]
init(authorName: String, recipe: Recipe) {
self.authorName = authorName
self.recipe = recipe
super.init()
self.headerHeight = 0.5 * POINTS_PER_INCH
// create formatter with the recipe's HTML text
// per page insets should be one-inch, with 3.5 inches on the right to make room for photos
let formatter = UIMarkupTextPrintFormatter(markupText: recipe.html)
formatter.perPageContentInsets = UIEdgeInsets(top: POINTS_PER_INCH, left: POINTS_PER_INCH, bottom: POINTS_PER_INCH,
right: POINTS_PER_INCH * 3.5)
addPrintFormatter(formatter, startingAtPageAtIndex: 0)
}
override func drawHeaderForPageAtIndex(pageIndex: Int, var inRect headerRect: CGRect) {
var headerInsets = UIEdgeInsets(top: CGRectGetMinY(headerRect), left: POINTS_PER_INCH, bottom: CGRectGetMaxY(paperRect) - CGRectGetMaxY(headerRect), right: POINTS_PER_INCH)
headerRect = UIEdgeInsetsInsetRect(paperRect, headerInsets)
// author name on left
authorName.drawAtPointInRect(headerRect, withAttributes: nameAttributes, andAlignment: .LeftCenter)
// page number on right
let pageNumberString: NSString = "\(pageIndex + 1)"
pageNumberString.drawAtPointInRect(headerRect, withAttributes: pageNumberAttributes, andAlignment: .RightCenter)
}
func drawImages(images: [UIImage], var inRect sourceRect: CGRect) {
// we'll use 1/8 of an inch of vertical padding between each image
let imagePadding = UIEdgeInsets(top: pointsPerInch / 8, left: 0, bottom: 0, right: 0)
for image in images {
// get the aspect-fit size of the image
var sizedRect = AVMakeRectWithAspectRatioInsideRect(image.size, sourceRect)
// if the new width of the image doesn't match the source rect, there wasn't enough vertical room: bail
if sizedRect.width != sourceRect.width {
return
}
// use divide to separate the image rect from the rest of the column
CGRectDivide(sourceRect, &sizedRect, &sourceRect, sizedRect.height, .MinYEdge)
// draw the image
image.drawInRect(sizedRect)
// inset the source rect to make a little padding before the next image
sourceRect = UIEdgeInsetsInsetRect(sourceRect, imagePadding)
}
}
override func drawContentForPageAtIndex(pageIndex: Int, inRect contentRect: CGRect) {
if pageIndex == 0 {
// only use rightmost two inches of contentRect
let imagesRectWidth = POINTS_PER_INCH * 2
let imagesRectHeight = paperRect.height - POINTS_PER_INCH - (CGRectGetMaxY(paperRect) - CGRectGetMaxY(contentRect))
let imagesRect = CGRect(x: CGRectGetMaxX(paperRect) - imagesRectWidth - POINTS_PER_INCH, y: paperRect.origin.y + POINTS_PER_INCH, width: imagesRectWidth, height: imagesRectHeight)
drawImages(recipe.images, inRect: imagesRect)
}
}
}
//
// NSString+NCPrinting.h
// (c) 2014 Nate Cook, licensed under the MIT License
//
#import <UIKit/UIKit.h>
// The alignment for drawing an NSString inside a bounding rectangle.
typedef NS_ENUM(NSInteger, NCStringAlignment) {
NCStringAlignmentLeftTop,
NCStringAlignmentCenterTop,
NCStringAlignmentRightTop,
NCStringAlignmentLeftCenter,
NCStringAlignmentCenter,
NCStringAlignmentRightCenter,
NCStringAlignmentLeftBottom,
NCStringAlignmentCenterBottom,
NCStringAlignmentRightBottom
};
@interface NSString (NCPrinting)
// Draw the `NSString` inside the bounding rectangle with a given alignment.
- (void)drawAtPointInRect:(CGRect)rect withAttributes:(NSDictionary *)attrs andAlignment:(NCStringAlignment)alignment;
@end
//
// NSString+NCPrinting.m
// (c) 2014 Nate Cook, licensed under the MIT License
//
#import "NSString+NCPrinting.h"
@implementation NSString (NCPrinting)
- (void)drawAtPointInRect:(CGRect)rect withAttributes:(NSDictionary *)attrs andAlignment:(NCStringAlignment)alignment {
CGSize size = [self sizeWithAttributes:attrs];
CGFloat x, y;
// x position
switch (alignment) {
case NCStringAlignmentLeftTop:
case NCStringAlignmentLeftCenter:
case NCStringAlignmentLeftBottom:
x = CGRectGetMinX(rect); break;
case NCStringAlignmentCenterTop:
case NCStringAlignmentCenter:
case NCStringAlignmentCenterBottom:
x = CGRectGetMidX(rect) - size.width / 2; break;
case NCStringAlignmentRightTop:
case NCStringAlignmentRightCenter:
case NCStringAlignmentRightBottom:
x = CGRectGetMaxX(rect) - size.width; break;
}
// y position
switch (alignment) {
case NCStringAlignmentLeftTop:
case NCStringAlignmentCenterTop:
case NCStringAlignmentRightTop:
y = CGRectGetMinY(rect); break;
case NCStringAlignmentLeftCenter:
case NCStringAlignmentCenter:
case NCStringAlignmentRightCenter:
y = CGRectGetMidY(rect) - size.height / 2; break;
case NCStringAlignmentLeftBottom:
case NCStringAlignmentCenterBottom:
case NCStringAlignmentRightBottom:
y = CGRectGetMaxY(rect) - size.height; break;
}
[self drawAtPoint:CGPointMake(x, y) withAttributes:attrs];
}
@end
//
// RecipePrintPageRenderer.h
// (c) 2014 Nate Cook, licensed under the MIT License
//
#import <UIKit/UIKit.h>
#define POINTS_PER_INCH 72
@class Recipe;
@interface RecipePrintPageRenderer : UIPrintPageRenderer
@property (nonatomic, strong) NSString *authorName;
@property (nonatomic, weak) Recipe *recipe;
- (id)initWithAuthorName:(NSString *)authorName recipe:(Recipe *)recipe;
@end
//
// RecipePrintPageRenderer.m
// (c) 2014 Nate Cook, licensed under the MIT License
//
#import "RecipePrintPageRenderer.h"
#import "NSString+NCPrinting.h"
#import <AVFoundation/AVFoundation.h>
@interface RecipePrintPageRenderer ()
@property (nonatomic, strong) NSDictionary *pageNumberAttributes;
@property (nonatomic, strong) NSDictionary *nameAttributes;
@end
@implementation RecipePrintPageRenderer
- (id)initWithAuthorName:(NSString *)authorName recipe:(Recipe *)recipe {
if (self = [super init]) {
self.authorName = authorName;
self.recipe = recipe;
self.headerHeight = 0.5;
self.pageNumberAttributes = @{ NSFontAttributeName : [UIFont fontWithName:@"Georgia-Italic" size: 11] };
self.nameAttributes = @{ NSFontAttributeName : [UIFont fontWithName:@"Georgia" size: 11] };
// create formatter with the recipe's HTML text
// per page insets should be one-inch, with 3.5 inches on the right to make room for photos
UIMarkupTextPrintFormatter *formatter = [[UIMarkupTextPrintFormatter alloc] initWithMarkupText:recipe.html];
formatter.perPageContentInsets = UIEdgeInsetsMake(POINTS_PER_INCH, POINTS_PER_INCH, POINTS_PER_INCH, POINTS_PER_INCH * 3.5);
[self addPrintFormatter:formatter startingAtPageAtIndex:0];
}
return self;
}
- (void)drawHeaderForPageAtIndex:(NSInteger)index inRect:(CGRect)headerRect {
UIEdgeInsets headerInsets = UIEdgeInsetsMake(CGRectGetMinY(headerRect), POINTS_PER_INCH, CGRectGetMaxY(self.paperRect) - CGRectGetMaxY(headerRect), POINTS_PER_INCH);
headerRect = UIEdgeInsetsInsetRect(self.paperRect, headerInsets);
// author name on left
[self.authorName drawAtPointInRect:headerRect withAttributes:self.nameAttributes andAlignment:NCStringAlignmentLeftCenter];
// page number on right
NSString *pageNumberString = [NSString stringWithFormat:@"%ld", index + 1];
[pageNumberString drawAtPointInRect:headerRect withAttributes:self.pageNumberAttributes andAlignment:NCStringAlignmentRightCenter];
}
- (void)drawImages:(NSArray *)images inRect:(CGRect)sourceRect {
// we'll use 1/8 of an inch of vertical padding between each image
UIEdgeInsets imagePadding = UIEdgeInsetsMake(POINTS_PER_INCH / 8, 0, 0, 0);
for (UIImage *image in images) {
// get the aspect-fit size of the image
CGRect sizedRect = AVMakeRectWithAspectRatioInsideRect(image.size, sourceRect);
// if the new width of the image doesn't match the source rect, there wasn't enough vertical room: bail
if (CGRectGetWidth(sizedRect) != CGRectGetWidth(sourceRect)) {
return;
}
// use divide to separate the image rect from the rest of the column
CGRectDivide(sourceRect, &sizedRect, &sourceRect, CGRectGetHeight(sizedRect), CGRectMinYEdge);
// draw the image
[image drawInRect:sizedRect];
// inset the source rect to make a little padding before the next image
sourceRect = UIEdgeInsetsInsetRect(sourceRect, imagePadding);
}
}
- (void)drawContentForPageAtIndex:(NSInteger)pageIndex inRect:(CGRect)contentRect {
if (pageIndex == 0) {
// only use rightmost two inches of contentRect
CGFloat imagesRectWidth = POINTS_PER_INCH * 2;
CGFloat imagesRectHeight = CGRectGetHeight(self.paperRect) - POINTS_PER_INCH - (CGRectGetMaxY(self.paperRect) - CGRectGetMaxY(contentRect));
CGRect imagesRect = CGRectMake(CGRectGetMaxX(self.paperRect) - imagesRectWidth - POINTS_PER_INCH, CGRectGetMinY(self.paperRect) + POINTS_PER_INCH, imagesRectWidth, imagesRectHeight);
[self drawImages:self.recipe.images inRect:imagesRect];
}
}
@end
@vineethvijayan

Hey Matt, Thanks for this gist, I am trying to make this work for footer

screen shot 2016-02-14 at 6 44 55 pm

but it is getting sliced. like in the pic any idea why?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment