Skip to content
This repository has been archived by the owner on Jul 23, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1 from IdeasOnCanvas/master
Browse files Browse the repository at this point in the history
Add support for NSData-based PDF renderers (and code cleanup)
  • Loading branch information
alloy committed Apr 21, 2016
2 parents 9e49095 + a871df2 commit 5910788
Show file tree
Hide file tree
Showing 20 changed files with 772 additions and 628 deletions.
49 changes: 21 additions & 28 deletions Example/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,28 @@

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
- (BOOL)application:(__unused UIApplication *)application didFinishLaunchingWithOptions:(__unused NSDictionary *)launchOptions
{
CGRect frame = [[UIScreen mainScreen] bounds];

self.window = [[UIWindow alloc] initWithFrame:frame];
self.window.backgroundColor = [UIColor lightGrayColor];

UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(CGRectGetMidX(frame) - 50, 40, 100, 40);
[self.window addSubview:button];

FTPDFAssetRenderer *renderer = [FTPDFAssetRenderer rendererForPDFNamed:@"restaurant-icon-mask"];
[renderer fitSize:button.bounds.size];

renderer.targetColor = [UIColor blueColor];
[button setImage:[renderer imageWithCacheIdentifier:@"normal"] forState:UIControlStateNormal];
renderer.targetColor = [UIColor whiteColor];
[button setImage:[renderer imageWithCacheIdentifier:@"highlighted"] forState:UIControlStateHighlighted];

// UIImage *image = [FTPDFAssetRenderer imageOfPDFNamed:@"restaurant-icon-mask"
// targetWidth:frame.size.width
// targetColor:[UIColor greenColor]
// withIdentifier:@"pink is always good"];
// UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
// imageView.frame = CGRectMake(0, 100, imageView.bounds.size.width, imageView.bounds.size.height);
// [self.window addSubview:imageView];

[self.window makeKeyAndVisible];
return YES;
CGRect frame = [[UIScreen mainScreen] bounds];

self.window = [[UIWindow alloc] initWithFrame:frame];
self.window.backgroundColor = [UIColor lightGrayColor];
self.window.rootViewController = [UIViewController new];

UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(CGRectGetMidX(frame) - 50, 40, 100, 40);
[self.window addSubview:button];

FTPDFAssetRenderer *renderer = [FTPDFAssetRenderer rendererForPDFNamed:@"restaurant-icon-mask"];
[renderer fitSize:button.bounds.size];

renderer.targetColor = [UIColor blueColor];
[button setImage:[renderer imageWithCacheIdentifier:@"normal"] forState:UIControlStateNormal];
renderer.targetColor = [UIColor whiteColor];
[button setImage:[renderer imageWithCacheIdentifier:@"highlighted"] forState:UIControlStateHighlighted];

[self.window makeKeyAndVisible];
return YES;
}

@end
268 changes: 151 additions & 117 deletions Example/FTAssetRenderer.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions FTAssetRendererTests/FTAssetRendererTests.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#import <XCTest/XCTest.h>
#import "FTAssetRenderer.h"

@interface FTAssetRendererTests : XCTestCase

@property (nonatomic) FTAssetRenderer *renderer;

@end
49 changes: 49 additions & 0 deletions FTAssetRendererTests/FTAssetRendererTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#import "FTAssetRendererTests.h"

@implementation FTAssetRendererTests

- (void)setUp
{
[super setUp];
[[NSFileManager defaultManager] createDirectoryAtPath:[FTAssetRenderer cacheDirectory]
withIntermediateDirectories:YES
attributes:nil
error:NULL];
NSURL *URL = [[NSBundle mainBundle] URLForResource:@"restaurant-icon-mask" withExtension:@"pdf"];
self.renderer = [[FTAssetRenderer alloc] initWithURL:URL];
}

- (void)tearDown
{
[super tearDown];
self.renderer = nil;
[[NSFileManager defaultManager] removeItemAtPath:[FTAssetRenderer cacheDirectory]
error:NULL];
}

#pragma mark - caching

- (void)testCachesInSpecificCacheDirectory
{
NSString *expected = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
expected = [expected stringByAppendingPathComponent:@"__FTAssetRenderer_Cache__"];
XCTAssertEqualObjects(expected, [FTAssetRenderer cacheDirectory]);
}

- (void)testChangesCachePathBasedOnIdentifier
{
NSString *path = [self.renderer cachePathWithIdentifier:@"normal"];
XCTAssertEqualObjects(path, [self.renderer cachePathWithIdentifier:@"normal"]);

NSString *newPath = [self.renderer cachePathWithIdentifier:@"highlighted"];
XCTAssertFalse([path isEqualToString:newPath]);
XCTAssertEqualObjects(newPath, [self.renderer cachePathWithIdentifier:@"highlighted"]);
}

- (void)testCacheWithEmptyURL
{
FTAssetRenderer *renderer = [[FTAssetRenderer alloc] initWithURL:nil];
XCTAssertThrows([renderer assertCanCacheWithIdentifier:@"normal"]);
}

@end
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#import <SenTestingKit/SenTestingKit.h>
#import <XCTest/XCTest.h>
#import "FTImageAssetRenderer.h"

@interface FTImageAssetRendererTests : SenTestCase
@interface FTImageAssetRendererTests : XCTestCase

@property (nonatomic) FTImageAssetRenderer *renderer;

Expand Down
52 changes: 52 additions & 0 deletions FTAssetRendererTests/FTImageAssetRendererTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#import "FTImageAssetRendererTests.h"

@implementation FTImageAssetRendererTests

- (void)setUp
{
[super setUp];
[[NSFileManager defaultManager] createDirectoryAtPath:[FTAssetRenderer cacheDirectory]
withIntermediateDirectories:YES
attributes:nil
error:NULL];
self.renderer = [FTAssetRenderer rendererForImageNamed:@"restaurant-icon-mask" withExtension:@"png"];
self.renderer.targetColor = [UIColor redColor];
}

- (void)tearDown
{
[super tearDown];
self.renderer = nil;
[[NSFileManager defaultManager] removeItemAtPath:[FTAssetRenderer cacheDirectory]
error:NULL];
}

- (void)testConvenienceMethodLoadsImageAppropriateForScreenScale
{
NSString *filename = [[self.renderer.URL lastPathComponent] stringByDeletingPathExtension];
int scale = (int)[[UIScreen mainScreen] scale];
if (scale > 1) {
XCTAssertTrue([filename hasSuffix:@"@2x"]);
} else {
XCTAssertFalse([filename hasSuffix:@"@2x"]);
}
}

- (void)testReturnsImageOfExpectedSizeAndScale
{
UIImage *result = [self.renderer imageWithCacheIdentifier:@"test"];
XCTAssertTrue(CGSizeEqualToSize(self.renderer.sourceImage.size, result.size));
}

- (void)testCreatesExpectedSizeImageAtCachePath
{
NSString *path = [self.renderer cachePathWithIdentifier:@"test"];
[self.renderer imageWithCacheIdentifier:@"test"];
sleep(2); // lame, should check if file exists with timeout
UIImage *result = [UIImage imageWithContentsOfFile:path];
CGFloat scale = [[UIScreen mainScreen] scale];
CGSize sourceSize = self.renderer.sourceImage.size;
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(sourceSize.width * scale, sourceSize.height * scale), result.size));
}

@end
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#import <SenTestingKit/SenTestingKit.h>
#import <XCTest/XCTest.h>
#import "FTPDFAssetRenderer.h"

@interface FTPDFAssetRendererTests : SenTestCase
@interface FTPDFAssetRendererTests : XCTestCase

@property (nonatomic) FTPDFAssetRenderer *renderer;

Expand Down
119 changes: 119 additions & 0 deletions FTAssetRendererTests/FTPDFAssetRendererTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#import "FTPDFAssetRendererTests.h"

// TODO
// * add portrait and landscape fixtures

@implementation FTPDFAssetRendererTests

- (void)setUp
{
[super setUp];
[[NSFileManager defaultManager] createDirectoryAtPath:[FTAssetRenderer cacheDirectory]
withIntermediateDirectories:YES
attributes:nil
error:NULL];
self.renderer = [FTAssetRenderer rendererForPDFNamed:@"restaurant-icon-mask"];
}

- (void)tearDown
{
[super tearDown];
self.renderer = nil;
[[NSFileManager defaultManager] removeItemAtPath:[FTAssetRenderer cacheDirectory]
error:NULL];
}

#pragma - mark sizing

- (void)testByDefaultUsesMediaBoxSizeAsTarget
{
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(88, 88), self.renderer.targetSize));
}

- (void)testFitsPDFWithinGivenSizeFillingShortestEdge
{
[self.renderer fitSize:CGSizeMake(100, 200)];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 100), self.renderer.targetSize));
[self.renderer fitSize:CGSizeMake(300, 200)];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(200, 200), self.renderer.targetSize));
}

- (void)testFitsPDFWithinGivenTargetWidth
{
[self.renderer fitWidth:100];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 100), self.renderer.targetSize));
}

- (void)testFitsPDFWithinGivenTargetHeight
{
[self.renderer fitHeight:100];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 100), self.renderer.targetSize));
}

#pragma mark - caching

- (void)testChangesCachePathBasedOnTargetSize
{
NSString *path = [self.renderer cachePathWithIdentifier:nil];
XCTAssertEqualObjects(path, [self.renderer cachePathWithIdentifier:nil]);

self.renderer.targetSize = CGSizeMake(123, 456);
NSString *newPath = [self.renderer cachePathWithIdentifier:nil];
XCTAssertFalse([path isEqualToString:newPath]);
XCTAssertEqualObjects(newPath, [self.renderer cachePathWithIdentifier:nil]);
}

- (void)testChangesCachePathBasedOnSourcePageIndex
{
NSString *path = [self.renderer cachePathWithIdentifier:nil];
XCTAssertEqualObjects(path, [self.renderer cachePathWithIdentifier:nil]);

self.renderer.sourcePageIndex = 2;
NSString *newPath = [self.renderer cachePathWithIdentifier:nil];
XCTAssertFalse([path isEqualToString:newPath]);
XCTAssertEqualObjects(newPath, [self.renderer cachePathWithIdentifier:nil]);
}

- (void)testRaisesWhenUsedAsMaskAndCachingWithoutCacheIdentifier
{
self.renderer.targetColor = [UIColor redColor];

self.renderer.useCache = NO;
XCTAssertNoThrow([self.renderer imageWithCacheIdentifier:nil]);

self.renderer.useCache = YES;
XCTAssertThrowsSpecificNamed([self.renderer imageWithCacheIdentifier:nil], NSException, @"FTAssetRendererError");
}

#pragma mark - drawing

- (void)testReturnsImageOfExpectedSizeAndScale
{
self.renderer.targetSize = CGSizeMake(100, 50);
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 50), [self.renderer image].size));
}

- (void)testCreatesExpectedSizeImageAtCachePath
{
self.renderer.targetSize = CGSizeMake(100, 50);
NSString *path = [self.renderer cachePathWithIdentifier:nil];
[self.renderer image];
sleep(2); // lame, should check if file exists with timeout
UIImage *image = [UIImage imageWithContentsOfFile:path];
CGFloat scale = [[UIScreen mainScreen] scale];
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100 * scale, 50 * scale), image.size));
}

#pragma mark - data

- (void)testRendererWithNSData
{
NSURL *URL = [[NSBundle mainBundle] URLForResource:@"restaurant-icon-mask" withExtension:@"pdf"];
NSData *data = [NSData dataWithContentsOfURL:URL];
FTPDFAssetRenderer *dataRenderer = [[FTPDFAssetRenderer alloc] initWithData:data];

XCTAssertTrue(CGSizeEqualToSize(dataRenderer.targetSize, CGSizeMake(88, 88)));
XCTAssertTrue(CGSizeEqualToSize(dataRenderer.image.size, CGSizeMake(88, 88)));
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.fngtps.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
Expand Down
30 changes: 19 additions & 11 deletions Source/FTAssetRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,41 @@

@class FTPDFAssetRenderer;


NS_ASSUME_NONNULL_BEGIN

@interface FTAssetRenderer : NSObject

@property (readonly, nonatomic) NSURL *URL;
@property (nonatomic) UIColor *targetColor;
@property (assign, nonatomic) BOOL cache;
@property (nonatomic, readonly, nullable) NSURL *URL;
@property (nonatomic, strong, nullable) UIColor *targetColor;
@property (nonatomic, assign) BOOL useCache; // defaults to YES
@property (nonatomic, readonly) CGSize targetSize;

+ (NSString *)cacheDirectory;

- (id)initWithURL:(NSURL *)URL;

- (CGSize)targetSize;
- (instancetype)initWithURL:(NSURL * _Nullable)URL NS_DESIGNATED_INITIALIZER;

// When caching is enabled and the image is used as a mask then an identifier
// *has* to be specified.
//
// For example, when generating button icons, you could use an identifier like
// `normal` or `highlighted`, depending on the state.
- (UIImage *)image;
- (UIImage *)imageWithCacheIdentifier:(NSString *)identifier;
- (UIImage *)imageWithCacheIdentifier:(nullable NSString *)identifier;

@end


// PRIVATE
@interface FTAssetRenderer (FTProtected)

- (void)drawImageInContext:(CGContextRef)context;
- (void)drawTargetColorInContext:(CGContextRef)context;

- (NSString *)cachePathWithIdentifier:(NSString *)identifier;
- (void)canCacheWithIdentifier:(NSString *)identifier;
- (NSString *)cacheRawFilenameWithIdentifier:(NSString *)identifier;
- (void)assertCanCacheWithIdentifier:(NSString * _Nullable)identifier;
- (NSString *)cachePathWithIdentifier:(NSString * _Nullable)identifier;
- (NSString *)cacheRawFilenameWithIdentifier:(NSString * _Nullable)identifier;

@end


NS_ASSUME_NONNULL_END
Loading

0 comments on commit 5910788

Please sign in to comment.