Forked from natecook1000/ RecipePageRenderer.swift
Last active
February 28, 2023 03:22
-
-
Save mattt/bd5e48ae461848cdbd1e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey Matt, Thanks for this gist, I am trying to make this work for footer
but it is getting sliced. like in the pic any idea why?