Files
IJSVG/source/IJSVGFontConverter.m
T
Curtis Hard a86e347b4c Added support for converting fonts to SVG's
Also added support for reading glyphs from icon fonts
2015-05-23 15:13:24 +01:00

182 lines
4.9 KiB
Objective-C

//
// IJSVGFontConverter.m
// IJSVGExample
//
// Created by Curtis Hard on 21/05/2015.
// Copyright (c) 2015 Curtis Hard. All rights reserved.
//
#import "IJSVGFontConverter.h"
#import "IJSVGBezierPathAdditions.h"
@implementation IJSVGFontConverter
- (void)dealloc
{
[_paths release], _paths = nil;
[_url release], _url = nil;
[_font release], _font = nil;
[super dealloc];
}
- (id)initWithFontAtFileURL:(NSURL *)url
{
if( ( self = [super init] ) != nil )
{
_url = [url copy];
// load the font
CGDataProviderRef dataProvider = CGDataProviderCreateWithURL((CFURLRef)_url);
CGFontRef fontRef = CGFontCreateWithDataProvider(dataProvider);
CTFontRef font = CTFontCreateWithGraphicsFont( fontRef, 30.f, NULL, NULL );
// toll free bridge between NSFont at CTFont :)
_font = [(NSFont *)font copy];
CGFontRelease(fontRef);
CGDataProviderRelease(dataProvider);
CFRelease(font);
}
return self;
}
- (NSFont *)font
{
return _font;
}
- (NSArray *)allCharacters
{
NSCharacterSet * charSet = _font.coveredCharacterSet;
NSMutableArray * array = [[[NSMutableArray alloc] init] autorelease];
for( int plane = 0; plane <= 16; plane++ )
{
if( [charSet hasMemberInPlane:plane] )
{
UTF32Char c;
for( c = plane << 16; c < (plane+1) << 16; c++ )
{
if( [charSet longCharacterIsMember:c] )
{
UTF32Char c1 = NSSwapHostIntToLittle(c);
// add it...
[array addObject:[[[NSString alloc] initWithBytes:&c1
length:4
encoding:NSUTF32LittleEndianStringEncoding] autorelease]];
}
}
}
}
return array;
}
- (void)generateMap
{
// we have already been made!
if( _paths.count != 0 )
return;
_paths = [[NSMutableDictionary alloc] init];
CTFontRef font = (CTFontRef)_font;
for( NSString * charString in [self allCharacters] )
{
// get the characters in each char
NSUInteger count = charString.length;
unichar characters[count];
[charString getCharacters:characters
range:NSMakeRange( 0, count )];
// get the glyphs
CGGlyph glyphs[count];
CTFontGetGlyphsForCharacters( font, characters, glyphs, count);
CGPathRef path = CTFontCreatePathForGlyph( font, glyphs[0], NULL );
if( path != NULL )
{
NSString * k = [NSString stringWithFormat:@"%04x",[charString characterAtIndex:0]];
[self parseCGPath:path
forCharacterString:k];
}
CGPathRelease(path);
}
}
- (void)parseCGPath:(CGPathRef)path
forCharacterString:(NSString *)string
{
[_paths setObject:[[self class] bezierpathFromCGPath:path]
forKey:string];
}
- (NSDictionary *)paths
{
[self generateMap];
return _paths;
}
static void IJSVGCGPathCallback(void * info, const CGPathElement * element)
{
NSBezierPath * path = (NSBezierPath *)info;
CGPoint * points = element->points;
switch( element->type )
{
// move to
case kCGPathElementMoveToPoint:
{
[path moveToPoint:NSMakePoint( points[0].x, points[0].y)];
break;
}
// line to
case kCGPathElementAddLineToPoint:
{
[path lineToPoint:NSMakePoint( points[0].x, points[0].y)];;
break;
}
// quad curve
case kCGPathElementAddQuadCurveToPoint:
{
[path addQuadCurveToPoint:points[1] controlPoint:points[0]];
break;
}
// curve to
case kCGPathElementAddCurveToPoint:
{
[path curveToPoint:NSMakePoint(points[2].x, points[2].y)
controlPoint1:NSMakePoint( points[0].x, points[0].y)
controlPoint2:NSMakePoint( points[1].x, points[1].y)];
break;
}
// close
case kCGPathElementCloseSubpath: {
[path closePath];
break;
}
}
}
+ (NSBezierPath *)bezierpathFromCGPath:(CGPathRef)path
{
NSBezierPath * bezPath = [NSBezierPath bezierPath];
// pass the path
CGPathApply( path, bezPath, IJSVGCGPathCallback );
// the glyph will be upside down, so we need to turn it up the right way!
NSAffineTransform * trans = [NSAffineTransform transform];
// scale -1 on the Y axis so its now correct but to far up
[trans scaleXBy:1.f
yBy:-1.f];
// move it back down by its height
[trans translateXBy:0.f
yBy:bezPath.controlPointBounds.size.height];
[bezPath transformUsingAffineTransform:trans];
return bezPath;
}
@end