Files
IJSVG/source/IJSVGUtils.m
T
Curtis Hard 6d4eb76803 Squashed commit of the following:
commit ffb55b0f31
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Mar 2 13:28:40 2018 +0000

    Fixes viewBox origin translate

commit a4c032afa2
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Feb 28 21:29:11 2018 +0000

    Added clip to viewport

commit abe8f4cba5
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Feb 28 21:00:20 2018 +0000

    Fixes drawInRect not obeying origin

commit a16842271b
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Feb 26 13:46:41 2018 +0000

    Resolves namespaces correctly + added common HTML list to be parsed as groups

commit 58126e06e4
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 25 21:58:49 2018 +0000

    Fixes!

commit 5af5e054ab
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Feb 23 22:37:53 2018 +0000

    Fixed defNode being removed

commit edd3aa1f33
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Feb 23 16:29:54 2018 +0000

    excluded various elements from diff

commit 3366bc4fa5
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Feb 23 14:05:03 2018 +0000

    Better optimaztion

commit 104002183b
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Feb 22 22:17:50 2018 +0000

    Added rudimenatry inline styles -> stylesheet

commit 7010b7ea50
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Feb 22 13:33:32 2018 +0000

    Correct order of cleanup

commit 5266a8c07a
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Feb 22 08:22:17 2018 +0000

    corrent length of string

commit caf55e8bdf
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Feb 21 20:45:17 2018 +0000

    removes useless def if required

commit 8160d05eba
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Feb 20 14:12:02 2018 +0000

    Refactor of a few methods

commit a6d6a06521
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Feb 20 09:40:21 2018 +0000

    Added collpasing of gradients

commit 103a4d71f6
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Feb 19 19:07:30 2018 +0000

    Reduced floats even more

commit 98874b1d2c
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Feb 19 18:53:02 2018 +0000

    More compression goodness

commit e742db31e0
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Feb 19 14:02:15 2018 +0000

    Added intermediateParent

commit 198fd09f07
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Feb 19 11:51:37 2018 +0000

    Fixes! and performance increases

commit 3493194b1b
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Feb 19 08:20:02 2018 +0000

    Scale computation

commit 0016775eaf
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 18 22:33:26 2018 +0000

    More goodness

commit 304a04cc22
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 18 22:32:04 2018 +0000

    Vastly improved the exporter

commit e4fd0af582
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 18 15:20:28 2018 +0000

    This is insanely important!

commit 69a2a0c97e
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 4 22:02:19 2018 +0000

    Refactor

commit 5299bb0479
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 4 21:58:21 2018 +0000

    will continue to use CoreAnimation for the time being

commit 4f1943cad1
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 4 21:57:46 2018 +0000

    Fixes gradient strokes

commit f02d186293
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Feb 4 11:15:04 2018 +0000

    trying to get masks to work

commit 3291718cfb
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Feb 2 22:35:22 2018 +0000

    Beginning of quartz renderer

commit 6fbaaf5884
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 29 22:32:08 2018 +0000

    Removed useless log

commit abc65797ea
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 29 21:37:45 2018 +0000

    I think gradients work :D

commit af5a1c2718
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 28 22:18:02 2018 +0000

    Possible fx and fy things…

commit fb9a5282b9
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 28 19:22:40 2018 +0000

    Even more gradient fixes

commit d83933a103
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 28 14:21:50 2018 +0000

    Various improvements

commit bd7a0d5021
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 27 22:26:33 2018 +0000

    Start to linear

commit a9a038568c
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 27 21:14:25 2018 +0000

    This kind of actually works...

commit 77fbb38b6f
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 27 20:31:36 2018 +0000

    I guess this could be a good start?

    Posssible start of fixes?

commit 12c3191569
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 27 15:32:15 2018 +0000

    Added method for findind absolute position

commit e3e9626ef7
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 27 14:25:03 2018 +0000

    Fixes crash due to parentNode on temp groups being released

commit 1160d89f16
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Jan 26 22:29:22 2018 +0000

    Fixes use statements with transforms

commit 1575cbfde8
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Jan 26 18:16:03 2018 +0000

    Moved color tree over to modern syntax

commit 5c4c2eee91
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Jan 25 18:23:53 2018 +0000

    Added HSL/HSLA support

commit 087b13e58f
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 24 22:24:43 2018 +0000

    Various image loading issues resolved from base64 images

commit 1183e167aa
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 24 21:21:41 2018 +0000

    Rect issue fix

commit 4dbfc59437
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 24 21:19:24 2018 +0000

    Moved transforms over from being concatinated to seperate calls

commit 409bd509fa
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 24 20:30:38 2018 +0000

    Fixes color issue

commit 8ae1d1b4e0
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 24 18:41:08 2018 +0000

    Removed check as its not needed here

commit 7243fbe5ff
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 24 18:35:43 2018 +0000

    Added excludeAttributes list to parseCommonAttributes

    added x and y to that list for rect

commit 51b9a5e85f
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Jan 23 19:53:04 2018 +0000

    Added isSubcommand to IJSVGCommand

    - Fixes move command going awol when preceding move commands are not subcommands (woah)

commit 40098589de
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 22 22:04:48 2018 +0000

    removed reverseObjectEnumerator

    IJSVGTransform already deals with this at parse stage (was a test from earlier), spec states transforms are applied in reverse order (which parser already dealth with :-))

commit 7cb96b21f2
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 22 22:01:56 2018 +0000

    Removed use of origin here as its computed in apply defaults

commit 1bff7c6970
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 22 21:48:07 2018 +0000

    Various fixes… still going…
2018-03-02 13:51:49 +00:00

610 lines
18 KiB
Objective-C

//
// IJSVGUtils.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGUtils.h"
#import "IJSVGLayer.h"
#import "IJSVGShapeLayer.h"
@implementation IJSVGUtils
BOOL IJSVGIsCommonHTMLElementName(NSString * str)
{
str = str.lowercaseString;
return [IJSVGCommonHTMLElementNames() containsObject:str];
};
NSArray * IJSVGCommonHTMLElementNames()
{
static NSArray * names = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
names = [@[@"a",
@"abbr",
@"acronym",
@"abbr",
@"address",
@"applet",
@"embed",
@"object",
@"area",
@"article",
@"aside",
@"audio",
@"b",
@"base",
@"basefont",
@"bdi",
@"bdo",
@"big",
@"blockquote",
@"body",
@"br",
@"button",
@"canvas",
@"caption",
@"center",
@"cite",
@"code",
@"col",
@"colgroup",
@"colgroup",
@"datalist",
@"dd",
@"del",
@"details",
@"dfn",
@"dialog",
@"dir",
@"ul",
@"div",
@"dl",
@"dt",
@"em",
@"embed",
@"fieldset",
@"figcaption",
@"figure",
@"figure",
@"font",
@"footer",
@"form",
@"frame",
@"frameset",
@"h1",
@"h6",
@"head",
@"header",
@"hr",
@"html",
@"i",
@"iframe",
@"img",
@"input",
@"ins",
@"kbd",
@"label",
@"input",
@"legend",
@"fieldset",
@"li",
@"link",
@"main",
@"map",
@"mark",
@"menu",
@"menuitem",
@"meta",
@"meter",
@"nav",
@"noframes",
@"noscript",
@"object",
@"ol",
@"optgroup",
@"option",
@"output",
@"p",
@"param",
@"picture",
@"pre",
@"progress",
@"q",
@"rp",
@"rt",
@"ruby",
@"s",
@"samp",
@"script",
@"section",
@"select",
@"small",
@"source",
@"video",
@"audio",
@"span",
@"strike",
@"del",
@"s",
@"strong",
@"style",
@"sub",
@"summary",
@"details",
@"sup",
@"table",
@"tbody",
@"td",
@"template",
@"textarea",
@"tfoot",
@"th",
@"thead",
@"time",
@"title",
@"tr",
@"track",
@"video",
@"audio",
@"tt",
@"u",
@"ul",
@"var",
@"video",
@"wbr"] retain];
});
return names;
};
NSString * IJSVGShortFloatString(CGFloat f)
{
return [NSString stringWithFormat:@"%g",f];
};
NSString * IJSVGShortFloatStringWithPrecision(CGFloat f, NSInteger precision)
{
NSString * format = [NSString stringWithFormat:@"%@.%ld%@",@"%",precision,@"f"];
NSString * ret = [NSString stringWithFormat:format,f];
// can it be reduced even more?
if(ret.floatValue == (float)ret.integerValue) {
ret = [NSString stringWithFormat:@"%ld",ret.integerValue];
}
return ret;
};
BOOL IJSVGIsLegalCommandCharacter(unichar aChar)
{
char * validChars = "MmZzLlHhVvCcSsQqTtAa";
NSUInteger length = strlen(validChars);
for(NSUInteger i = 0; i < length; i++) {
if(aChar == validChars[i]) {
return YES;
}
}
return NO;
}
BOOL IJSVGIsSVGLayer(CALayer * layer)
{
return [layer isKindOfClass:IJSVGLayer.class] ||
[layer isKindOfClass:IJSVGShapeLayer.class];
}
CGFloat angle( CGPoint a, CGPoint b ) {
return [IJSVGUtils angleBetweenPointA:a
pointb:b];
}
CGFloat ratio( CGPoint a, CGPoint b ) {
return (a.x * b.x + a.y * b.y) / (magnitude(a) * magnitude(b));
}
CGFloat magnitude(CGPoint point)
{
return sqrtf(powf(point.x, 2) + powf(point.y, 2));
}
CGFloat radians_to_degrees(CGFloat radians)
{
return ((radians) * (180.0 / M_PI));
}
CGFloat degrees_to_radians( CGFloat degrees )
{
return ( ( degrees ) / 180.0 * M_PI );
}
+ (IJSVGCommandType)typeForCommandString:(NSString *)string
{
return [string isEqualToString:[string uppercaseString]] ? IJSVGCommandTypeAbsolute : IJSVGCommandTypeRelative;
}
+ (NSString *)defURL:(NSString *)string
{
static NSRegularExpression * _reg = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_reg = [[NSRegularExpression alloc] initWithPattern:@"url\\s?\\(\\s?#(.*?)\\)\\;?"
options:0
error:nil];
});
__block NSString * foundID = nil;
[_reg enumerateMatchesInString:string
options:0
range:NSMakeRange( 0, string.length )
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
{
if( ( foundID = [string substringWithRange:[result rangeAtIndex:1]] ) != nil )
*stop = YES;
}];
return foundID;
}
+ (IJSVGFontTraits)fontWeightTraitForString:(NSString *)string
weight:(CGFloat *)weight
{
*weight = string.floatValue;
if([string isEqualToString:@"bold"])
return IJSVGFontTraitBold;
return IJSVGFontTraitNone;
}
+ (IJSVGFontTraits)fontStyleStringForString:(NSString *)string
{
if([string isEqualToString:@"italic"])
return IJSVGFontTraitItalic;
return IJSVGFontTraitNone;
}
+ (IJSVGWindingRule)windingRuleForString:(NSString *)string
{
if( [string isEqualToString:@"evenodd"] )
return IJSVGWindingRuleEvenOdd;
if( [string isEqualToString:@"inherit"] )
return IJSVGWindingRuleInherit;
return IJSVGWindingRuleNonZero;
}
+ (IJSVGLineJoinStyle)lineJoinStyleForString:(NSString *)string
{
if( [string isEqualToString:@"mitre"] )
return IJSVGLineJoinStyleMiter;
if( [string isEqualToString:@"round"] )
return IJSVGLineJoinStyleRound;
if( [string isEqualToString:@"bevel"] )
return IJSVGLineJoinStyleBevel;
if( [string isEqualToString:@"inherit"] )
return IJSVGLineJoinStyleInherit;
return IJSVGLineJoinStyleMiter;
}
+ (IJSVGLineCapStyle)lineCapStyleForString:(NSString *)string
{
if( [string isEqualToString:@"butt"] )
return IJSVGLineCapStyleButt;
if( [string isEqualToString:@"square"] )
return IJSVGLineCapStyleSquare;
if( [string isEqualToString:@"round"] )
return IJSVGLineCapStyleRound;
if( [string isEqualToString:@"inherit"] )
return IJSVGLineCapStyleInherit;
return IJSVGLineCapStyleButt;
}
+ (IJSVGUnitType)unitTypeForString:(NSString *)string
{
if([string isEqualToString:@"userSpaceOnUse"]) {
return IJSVGUnitUserSpaceOnUse;
}
return IJSVGUnitObjectBoundingBox;
}
+ (IJSVGBlendMode)blendModeForString:(NSString *)string
{
string = string.lowercaseString;
if([string isEqualToString:@"normal"])
return IJSVGBlendModeNormal;
if([string isEqualToString:@"multiply"])
return IJSVGBlendModeMultiply;
if([string isEqualToString:@"screen"])
return IJSVGBlendModeScreen;
if([string isEqualToString:@"overlay"])
return IJSVGBlendModeOverlay;
if([string isEqualToString:@"darken"])
return IJSVGBlendModeDarken;
if([string isEqualToString:@"lighten"])
return IJSVGBlendModeLighten;
if([string isEqualToString:@"color-dodge"])
return IJSVGBlendModeColorDodge;
if([string isEqualToString:@"color-burn"])
return IJSVGBlendModeColorBurn;
if([string isEqualToString:@"hard-light"])
return IJSVGBlendModeHardLight;
if([string isEqualToString:@"soft-light"])
return IJSVGBlendModeSoftLight;
if([string isEqualToString:@"difference"])
return IJSVGBlendModeDifference;
if([string isEqualToString:@"exclusion"])
return IJSVGBlendModeExclusion;
if([string isEqualToString:@"hue"])
return IJSVGBlendModeHue;
if([string isEqualToString:@"saturation"])
return IJSVGBlendModeSaturation;
if([string isEqualToString:@"color"])
return IJSVGBlendModeColor;
if([string isEqualToString:@"luminosity"])
return IJSVGBlendModeLuminosity;
return IJSVGBlendModeNormal;
}
+ (NSString *)mixBlendingModeForBlendMode:(IJSVGBlendMode)blendMode
{
switch (blendMode) {
case IJSVGBlendModeMultiply: {
return @"multiple";
}
case IJSVGBlendModeScreen: {
return @"screen";
}
case IJSVGBlendModeOverlay: {
return @"overlay";
}
case IJSVGBlendModeDarken: {
return @"darken";
}
case IJSVGBlendModeLighten: {
return @"lighten";
}
case IJSVGBlendModeColorDodge: {
return @"color-dodge";
}
case IJSVGBlendModeColorBurn: {
return @"color-burn";
}
case IJSVGBlendModeHardLight: {
return @"hard-light";
}
case IJSVGBlendModeSoftLight: {
return @"soft-light";
}
case IJSVGBlendModeDifference: {
return @"difference";
}
case IJSVGBlendModeExclusion: {
return @"exclusion";
}
case IJSVGBlendModeHue: {
return @"hue";
}
case IJSVGBlendModeSaturation: {
return @"saturation";
}
case IJSVGBlendModeColor: {
return @"color";
}
case IJSVGBlendModeLuminosity: {
return @"luminosity";
}
case IJSVGBlendModeNormal:
default: {
return nil;
}
}
}
+ (CGFloat *)commandParameters:(NSString *)command
count:(NSInteger *)count
{
if( [command isKindOfClass:[NSNumber class]] )
{
CGFloat * ret = (CGFloat *)malloc(1*sizeof(CGFloat));
ret[0] = [(NSNumber *)command floatValue];
*count = 1;
return ret;
}
return [[self class] scanFloatsFromString:command
size:count];
}
+ (CGFloat *)scanFloatsFromString:(NSString *)string
size:(NSInteger *)length
{
// default sizes and memory
// sizes for the string buffer
NSInteger defSize = 50;
NSInteger size = defSize;
// default memory size for the floats
NSInteger defFloatSize = 100;
NSInteger floatSize = defFloatSize;
NSInteger i = 0;
NSInteger counter = 0;
const char * cString = [string cStringUsingEncoding:NSUTF8StringEncoding];
const char * validChars = "0123456789eE+-.";
NSInteger sLength = strlen(cString);
// buffer for the returned floats
CGFloat * floats = (CGFloat *)malloc(sizeof(CGFloat)*defFloatSize);
char * buffer = NULL;
bool isDecimal = false;
int bufferCount = 0;
while(i < sLength) {
char currentChar = cString[i];
// work out next char
char nextChar = (char)0;
if(i < (sLength-1)) {
nextChar = cString[i+1];
}
bool isValid = strchr(validChars, currentChar);
// in order to work out the split, its either because the next char is
// a hyphen or a plus, or next char is a decimal and the current number is a decimal
bool isE = currentChar == 'e' || currentChar == 'E';
bool wantsEnd = nextChar == '-' || nextChar == '+' ||
(nextChar == '.' && isDecimal);
// could be a float like 5.334e-5 so dont break on the hypen
if(wantsEnd && isE && (nextChar == '-' || nextChar == '+')) {
wantsEnd = false;
}
// make sure its a valid string
if(isValid) {
// alloc the buffer if needed
if(buffer == NULL) {
buffer = (char *)calloc(sizeof(char),size);
} else if((bufferCount+1) == size) {
// realloc the buffer, incase the string is overflowing the
// allocated memory
size += defSize;
buffer = (char *)realloc(buffer, sizeof(char)*size);
}
// set the actual char against it
if(currentChar == '.') {
isDecimal = true;
}
buffer[bufferCount++] = currentChar;
} else {
// if its an invalid char, just stop it
wantsEnd = true;
}
// is at end of string, or wants to be stopped
// buffer has to actually exist or its completly
// useless and will cause a crash
if(buffer != NULL && (wantsEnd || i == sLength-1)) {
// make sure there is enough room in the float pool
if((counter+1) == floatSize) {
floatSize += defFloatSize;
floats = (CGFloat *)realloc(floats, sizeof(CGFloat)*floatSize);
}
// add the float
floats[counter++] = atof(buffer);
// memory clean and counter resets
free(buffer);
size = defSize;
isDecimal = false;
bufferCount = 0;
buffer = NULL;
}
i++;
}
*length = counter;
return floats;
}
+ (CGFloat *)parseViewBox:(NSString *)string
{
NSInteger size = 0;
return [[self class] scanFloatsFromString:string
size:&size];
}
+ (CGFloat)floatValue:(NSString *)string
fallBackForPercent:(CGFloat)fallBack
{
CGFloat val = [string floatValue];
if( [string rangeOfString:@"%"].location != NSNotFound )
val = (fallBack * val)/100;
return val;
}
+ (void)logParameters:(CGFloat *)param
count:(NSInteger)count
{
NSMutableString * str = [[[NSMutableString alloc] init] autorelease];
for( NSInteger i = 0; i < count; i++ )
{
[str appendFormat:@"%f ",param[i]];
}
NSLog(@"%@",str);
}
+ (CGFloat)floatValue:(NSString *)string
{
if( [string isEqualToString:@"inherit"] )
return IJSVGInheritedFloatValue;
return [string floatValue];
}
+ (CGFloat)angleBetweenPointA:(NSPoint)point1
pointb:(NSPoint)point2
{
return (point1.x * point2.y < point1.y * point2.x ? -1 : 1) * acosf(ratio(point1, point2));
}
+ (CGPathRef)newFlippedCGPath:(CGPathRef)path
{
CGRect boundingBox = CGPathGetPathBoundingBox(path);
CGAffineTransform scale = CGAffineTransformMakeScale(1.f, -1.f);
CGAffineTransform translate = CGAffineTransformTranslate(scale, 0.f, boundingBox.size.height);
CGPathRef transformPath = CGPathCreateCopyByTransformingPath(path, &translate);
return transformPath;
}
+ (CGPathRef)newCGPathFromBezierPath:(NSBezierPath *)bezPath
{
CGPathRef immutablePath = NULL;
// Then draw the path elements.
NSInteger numElements = bezPath.elementCount;
if (numElements > 0) {
CGMutablePathRef path = CGPathCreateMutable();
NSPoint points[3];
BOOL didClosePath = YES;
for (NSInteger i = 0; i < numElements; i++) {
switch ([bezPath elementAtIndex:i associatedPoints:points]) {
case NSMoveToBezierPathElement: {
CGPathMoveToPoint(path, NULL, points[0].x, points[0].y);
break;
}
case NSLineToBezierPathElement: {
CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y);
didClosePath = NO;
break;
}
case NSCurveToBezierPathElement: {
CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y,
points[1].x, points[1].y,
points[2].x, points[2].y);
didClosePath = NO;
break;
}
case NSClosePathBezierPathElement: {
CGPathCloseSubpath(path);
didClosePath = YES;
break;
}
}
}
// Be sure the path is closed or Quartz may not do valid hit detection.
if (didClosePath == NO) {
CGPathCloseSubpath(path);
}
// memory clean
immutablePath = CGPathCreateCopy(path);
CGPathRelease(path);
}
return immutablePath;
}
@end