Skip to content

Commit

Permalink
Merge branch 'release/1.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
defagos committed May 15, 2017
2 parents 7b0aa64 + 6ae887c commit 54d9327
Show file tree
Hide file tree
Showing 8 changed files with 286 additions and 5 deletions.
1 change: 1 addition & 0 deletions Framework/Sources/SRGAppearance.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ FOUNDATION_EXPORT NSString *SRGAppearanceMarketingVersion(void);
// Public headers.
#import "UIColor+SRGAppearance.h"
#import "UIFont+SRGAppearance.h"
#import "UIImage+SRGAppearance.h"
47 changes: 47 additions & 0 deletions Framework/Sources/UIImage+SRGAppearance.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// Copyright (c) SRG SSR. All rights reserved.
//
// License information is available from the LICENSE file.
//

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface UIImage (SRGAppearance)

/**
* Return an image generated from the vector image at the specified path.
*
* @param filePath The path of the vector image to use.
* @param size The size of the image to create.
*
* @return The generated image, `nil` if generation failed.
*/
+ (nullable UIImage *)srg_vectorImageAtPath:(NSString *)filePath withSize:(CGSize)size;

/**
* Return the file URL of an image generated from the vector image at the specified path.
*
* @param filePath The path of the vector image to use.
* @param size The size of the image to create.
*
* @return The generated image, `nil` if generation failed.
*
* @discussion Images are stored in the `/Library/Caches` directory.
*/
+ (nullable NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)size;

/**
* Clears the vector image cache.
*/
+ (void)srg_clearVectorImageCache;

/**
* Return the receiver, tinted with the specified color (if the color is `nil`, the image is returned as is).
*/
- (UIImage *)srg_imageTintedWithColor:(nullable UIColor *)color;

@end

NS_ASSUME_NONNULL_END
155 changes: 155 additions & 0 deletions Framework/Sources/UIImage+SRGAppearance.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//
// Copyright (c) SRG SSR. All rights reserved.
//
// License information is available from the LICENSE file.
//

#import "UIImage+SRGAppearance.h"

static CGFloat SRGAppearanceImageAspectScaleFit(CGSize sourceSize, CGRect destRect)
{
CGSize destSize = destRect.size;
CGFloat scaleW = destSize.width / sourceSize.width;
CGFloat scaleH = destSize.height / sourceSize.height;
return fmin(scaleW, scaleH);
}

static CGRect SRGAppearanceImageRectAroundCenter(CGPoint center, CGSize size)
{
return CGRectMake(center.x - size.width / 2.f, center.y - size.height / 2.f, size.width, size.height);
}

static CGRect SRGAppearanceImageRectByFittingRect(CGRect sourceRect, CGRect destinationRect)
{
CGFloat aspect = SRGAppearanceImageAspectScaleFit(sourceRect.size, destinationRect);
CGSize targetSize = CGSizeMake(sourceRect.size.width * aspect, sourceRect.size.height * aspect);
CGPoint center = CGPointMake(CGRectGetMidX(destinationRect), CGRectGetMidY(destinationRect));
return SRGAppearanceImageRectAroundCenter(center, targetSize);
}

static void SRGAppearanceImageDrawPDFPageInRect(CGPDFPageRef pageRef, CGRect rect)
{
CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSaveGState(context);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

// Flip the context to Quartz space
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformScale(transform, 1.f, -1.f);
transform = CGAffineTransformTranslate(transform, 0.f, -image.size.height);
CGContextConcatCTM(context, transform);

// Flip the rect, which remains in UIKit space
CGRect d = CGRectApplyAffineTransform(rect, transform);

// Calculate a rectangle to draw to
CGRect pageRect = CGPDFPageGetBoxRect(pageRef, kCGPDFCropBox);
CGFloat drawingAspect = SRGAppearanceImageAspectScaleFit(pageRect.size, d);
CGRect drawingRect = SRGAppearanceImageRectByFittingRect(pageRect, d);

// Adjust the context
CGContextTranslateCTM(context, drawingRect.origin.x, drawingRect.origin.y);
CGContextScaleCTM(context, drawingAspect, drawingAspect);

// Draw the page
CGContextDrawPDFPage(context, pageRef);
CGContextRestoreGState(context);
}

static NSString *SRGAppearanceVectorImageCachesDirectory(void)
{
static dispatch_once_t s_onceToken;
static NSString *s_imageCachesDirectory;
dispatch_once(&s_onceToken, ^{
NSString *cachesDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
s_imageCachesDirectory = [cachesDirectory stringByAppendingPathComponent:@"SRGAppearance/VectorImages"];
});
return s_imageCachesDirectory;
}

@implementation UIImage (SRGAppearance)

// Implementation borrowed from https://github.com/erica/useful-things
+ (UIImage *)srg_vectorImageAtPath:(NSString *)filePath withSize:(CGSize)size
{
NSURL *fileURL = [self srg_URLForVectorImageAtPath:filePath withSize:size];
return fileURL ? [UIImage imageWithContentsOfFile:fileURL.path] : nil;
}

+ (NSURL *)srg_URLForVectorImageAtPath:(NSString *)filePath withSize:(CGSize)size
{
// Check cached image existence at the very beginning, and return it if available
NSString *cachedFileName = [NSString stringWithFormat:@"%@_%@_%@.png", @(filePath.hash), @(size.width), @(size.height)];
NSString *cachesDirectory = SRGAppearanceVectorImageCachesDirectory();
NSString *cachedFilePath = [cachesDirectory stringByAppendingPathComponent:cachedFileName];
if ([[NSFileManager defaultManager] fileExistsAtPath:cachedFilePath]) {
return [NSURL fileURLWithPath:cachedFilePath];
}

// Check cache directory existence, since the cache might be cleared anytime
if (! [[NSFileManager defaultManager] fileExistsAtPath:cachesDirectory]) {
if (! [[NSFileManager defaultManager] createDirectoryAtPath:cachesDirectory withIntermediateDirectories:YES attributes:nil error:NULL]) {
return nil;
}
}

NSURL *fileURL = [NSURL fileURLWithPath:filePath];
CGPDFDocumentRef pdfDocumentRef = CGPDFDocumentCreateWithURL((__bridge CFURLRef)fileURL);
if (! pdfDocumentRef) {
return nil;
}

UIGraphicsBeginImageContextWithOptions(size, NO, 1. /* Force scale to 1 (0 would use device scale) */);
CGPDFPageRef pageRef = CGPDFDocumentGetPage(pdfDocumentRef, 1);
SRGAppearanceImageDrawPDFPageInRect(pageRef, CGRectMake(0.f, 0.f, size.width, size.height));
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

CGPDFDocumentRelease(pdfDocumentRef);

NSData *imageData = UIImagePNGRepresentation(image);
if (! imageData) {
return nil;
}

if (! [[NSFileManager defaultManager] createFileAtPath:cachedFilePath contents:imageData attributes:nil]) {
return nil;
}

return [NSURL fileURLWithPath:cachedFilePath];
}

+ (void)srg_clearVectorImageCache
{
NSString *cachesDirectory = SRGAppearanceVectorImageCachesDirectory();
if ([[NSFileManager defaultManager] fileExistsAtPath:cachesDirectory]) {
[[NSFileManager defaultManager] removeItemAtPath:cachesDirectory error:NULL];
}
}

- (UIImage *)srg_imageTintedWithColor:(UIColor *)color
{
if (! color) {
return self;
}

CGRect rect = CGRectMake(0.f, 0.f, self.size.width, self.size.height);
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.f);
CGContextRef context = UIGraphicsGetCurrentContext();

CGContextTranslateCTM(context, 0.f, self.size.height);
CGContextScaleCTM(context, 1.0f, -1.0f);

CGContextDrawImage(context, rect, self.CGImage);
CGContextSetBlendMode(context, kCGBlendModeSourceIn);
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, rect);

UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return tintedImage;
}

@end
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ SRG Appearance is a lightweight library providing unified SRG SSR appearance to

* Official SRG SSR fonts, automatically registered with your application, and with standard point sizes for common text styles.
* Official SRG SSR colors.
* Image processing tools.

## Compatibility

Expand Down Expand Up @@ -68,6 +69,10 @@ A limited set of SRG SSR custom font styles is provided as well. SRG SSR fonts a

Standard colors are provided in `UIColor+SRGAppearance.h`.

## Image processing

Image processing tools are provided in `UIImage+SRGAppearance.h`.

## License

See the [LICENSE](LICENSE) file for more information.
Expand Down
24 changes: 20 additions & 4 deletions SRGAppearance.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
/* Begin PBXBuildFile section */
6F131A7A1E8C1E17000D6BEC /* Sketch.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6F131A781E8C1E17000D6BEC /* Sketch.ttf */; };
6F131A7C1E8C1EBC000D6BEC /* Venetian.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6F131A7B1E8C1EBC000D6BEC /* Venetian.otf */; };
6F5769AE1EBCB38A00F931CA /* UIImage+SRGAppearance.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F5769AC1EBCB38A00F931CA /* UIImage+SRGAppearance.h */; settings = {ATTRIBUTES = (Public, ); }; };
6F5769AF1EBCB38A00F931CA /* UIImage+SRGAppearance.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F5769AD1EBCB38A00F931CA /* UIImage+SRGAppearance.m */; };
6F5769B11EBCBE6100F931CA /* ImageTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F5769B01EBCBE6100F931CA /* ImageTestCase.m */; };
6FA09D471D9E6FDA00EDCA64 /* SRGAppearance.h in Headers */ = {isa = PBXBuildFile; fileRef = 6FA09D441D9E6FDA00EDCA64 /* SRGAppearance.h */; settings = {ATTRIBUTES = (Public, ); }; };
6FA09D481D9E6FDA00EDCA64 /* SRGAppearance.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FA09D451D9E6FDA00EDCA64 /* SRGAppearance.m */; };
6FA09D4B1D9E70ED00EDCA64 /* NSBundle+SRGAppearance.h in Headers */ = {isa = PBXBuildFile; fileRef = 6FA09D491D9E70ED00EDCA64 /* NSBundle+SRGAppearance.h */; };
Expand All @@ -32,6 +35,7 @@
6FE08CDB1E8AA8E300EC4716 /* BundleFix.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FE08CDA1E8AA8E300EC4716 /* BundleFix.m */; };
6FE08CDE1E8AA9D100EC4716 /* FontAwesome.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6FE08CDD1E8AA9D100EC4716 /* FontAwesome.otf */; };
6FE08CE01E8AAB8F00EC4716 /* Corrupt.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6FE08CDF1E8AAB8F00EC4716 /* Corrupt.otf */; };
6FE89DD21EBD02DE00CE3069 /* TestImage.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 6FE89DD01EBD024200CE3069 /* TestImage.pdf */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -68,6 +72,9 @@
/* Begin PBXFileReference section */
6F131A781E8C1E17000D6BEC /* Sketch.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Sketch.ttf; sourceTree = "<group>"; };
6F131A7B1E8C1EBC000D6BEC /* Venetian.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Venetian.otf; sourceTree = "<group>"; };
6F5769AC1EBCB38A00F931CA /* UIImage+SRGAppearance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+SRGAppearance.h"; sourceTree = "<group>"; };
6F5769AD1EBCB38A00F931CA /* UIImage+SRGAppearance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+SRGAppearance.m"; sourceTree = "<group>"; };
6F5769B01EBCBE6100F931CA /* ImageTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ImageTestCase.m; path = Tests/Sources/ImageTestCase.m; sourceTree = SOURCE_ROOT; };
6FA09D361D9E6F8900EDCA64 /* SRGAppearance.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SRGAppearance.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6FA09D421D9E6FDA00EDCA64 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6FA09D441D9E6FDA00EDCA64 /* SRGAppearance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRGAppearance.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -96,6 +103,7 @@
6FE08CDA1E8AA8E300EC4716 /* BundleFix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BundleFix.m; sourceTree = "<group>"; };
6FE08CDD1E8AA9D100EC4716 /* FontAwesome.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = FontAwesome.otf; sourceTree = "<group>"; };
6FE08CDF1E8AAB8F00EC4716 /* Corrupt.otf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Corrupt.otf; sourceTree = "<group>"; };
6FE89DD01EBD024200CE3069 /* TestImage.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = TestImage.pdf; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -168,6 +176,8 @@
6FE08C501E8A818C00EC4716 /* UIFont+SRGAppearance.m */,
6FE08C511E8A818C00EC4716 /* UIFontDescriptor+SRGAppearance.h */,
6FE08C521E8A818C00EC4716 /* UIFontDescriptor+SRGAppearance.m */,
6F5769AC1EBCB38A00F931CA /* UIImage+SRGAppearance.h */,
6F5769AD1EBCB38A00F931CA /* UIImage+SRGAppearance.m */,
);
path = Sources;
sourceTree = "<group>";
Expand All @@ -187,6 +197,7 @@
children = (
6FE08CD91E8AA8E300EC4716 /* Helpers */,
6FA09D741D9E861600EDCA64 /* FontsTestCase.m */,
6F5769B01EBCBE6100F931CA /* ImageTestCase.m */,
);
path = Sources;
sourceTree = "<group>";
Expand Down Expand Up @@ -257,10 +268,11 @@
6FE08CDC1E8AA8F500EC4716 /* Resources */ = {
isa = PBXGroup;
children = (
6F131A7B1E8C1EBC000D6BEC /* Venetian.otf */,
6F131A781E8C1E17000D6BEC /* Sketch.ttf */,
6FE08CDF1E8AAB8F00EC4716 /* Corrupt.otf */,
6FE08CDD1E8AA9D100EC4716 /* FontAwesome.otf */,
6FE89DD01EBD024200CE3069 /* TestImage.pdf */,
6F131A781E8C1E17000D6BEC /* Sketch.ttf */,
6F131A7B1E8C1EBC000D6BEC /* Venetian.otf */,
);
path = Resources;
sourceTree = "<group>";
Expand All @@ -273,6 +285,7 @@
buildActionMask = 2147483647;
files = (
6FA09D4B1D9E70ED00EDCA64 /* NSBundle+SRGAppearance.h in Headers */,
6F5769AE1EBCB38A00F931CA /* UIImage+SRGAppearance.h in Headers */,
6FA09D471D9E6FDA00EDCA64 /* SRGAppearance.h in Headers */,
6FE08CD51E8A8AA700EC4716 /* UIColor+SRGAppearance.h in Headers */,
6FE08C551E8A818C00EC4716 /* UIFontDescriptor+SRGAppearance.h in Headers */,
Expand Down Expand Up @@ -398,6 +411,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6FE89DD21EBD02DE00CE3069 /* TestImage.pdf in Resources */,
6F131A7A1E8C1E17000D6BEC /* Sketch.ttf in Resources */,
6F131A7C1E8C1EBC000D6BEC /* Venetian.otf in Resources */,
6FE08CDE1E8AA9D100EC4716 /* FontAwesome.otf in Resources */,
Expand Down Expand Up @@ -425,6 +439,7 @@
6FE08C541E8A818C00EC4716 /* UIFont+SRGAppearance.m in Sources */,
6FE08CD61E8A8AA700EC4716 /* UIColor+SRGAppearance.m in Sources */,
6FA09D481D9E6FDA00EDCA64 /* SRGAppearance.m in Sources */,
6F5769AF1EBCB38A00F931CA /* UIImage+SRGAppearance.m in Sources */,
6FA09D4C1D9E70ED00EDCA64 /* NSBundle+SRGAppearance.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -433,6 +448,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6F5769B11EBCBE6100F931CA /* ImageTestCase.m in Sources */,
6FA09D761D9E861600EDCA64 /* FontsTestCase.m in Sources */,
6FE08CDB1E8AA8E300EC4716 /* BundleFix.m in Sources */,
);
Expand Down Expand Up @@ -507,7 +523,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -554,7 +570,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.1;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
Expand Down
Binary file added Tests/Resources/TestImage.pdf
Binary file not shown.
Loading

0 comments on commit 54d9327

Please sign in to comment.