diff --git a/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate b/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate index 0b5c905..42d2389 100644 Binary files a/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate and b/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/source/IJSVG.h b/source/IJSVG.h index e9b90cf..6117b36 100644 --- a/source/IJSVG.h +++ b/source/IJSVG.h @@ -28,6 +28,7 @@ handleForeignObject:(IJSVGForeignObject *)foreignObject @private IJSVGParser * _group; CGFloat _scale; + CGFloat _clipScale; NSMutableArray * _colors; id _delegate; diff --git a/source/IJSVG.m b/source/IJSVG.m index a08d56e..45dbd38 100644 --- a/source/IJSVG.m +++ b/source/IJSVG.m @@ -211,20 +211,21 @@ static NSColor * _baseColor = nil; // to translate the context so its centered CGFloat tX = round(rect.size.width/2-(_group.size.width/2)*_scale); CGFloat tY = round(rect.size.height/2-(_group.size.height/2)*_scale); - tX -= _group.viewBox.origin.x*_scale; - tY -= _group.viewBox.origin.y*_scale; // we also need to calculate the viewport so we can clip // the drawing if needed NSRect viewPort = NSZeroRect; - viewPort.origin.x = tX; - viewPort.origin.y = tY; - viewPort.size.width = _group.size.width*_scale; - viewPort.size.height = _group.size.height*_scale; + viewPort.origin.x = round(rect.size.width/2-(_group.proposedViewSize.width/2)*_clipScale); + viewPort.origin.y = round(rect.size.height/2-(_group.proposedViewSize.height/2)*_clipScale);; + viewPort.size.width = _group.proposedViewSize.width*_clipScale; + viewPort.size.height = _group.proposedViewSize.height*_clipScale; // clip any drawing to the view port [[NSBezierPath bezierPathWithRect:viewPort] addClip]; + tX -= _group.viewBox.origin.x*_scale; + tY -= _group.viewBox.origin.y*_scale; + CGContextTranslateCTM( ref, tX, tY ); CGContextScaleCTM( ref, _scale, _scale ); @@ -281,7 +282,12 @@ static NSColor * _baseColor = nil; // to transform the paths into our viewbox NSSize dest = rect.size; NSSize source = _group.viewBox.size; - _scale = MIN(dest.width/source.width,dest.height/source.height); + _clipScale = MIN(dest.width/_group.proposedViewSize.width,dest.height/_group.proposedViewSize.height); + + // work out the actual scale based on the clip scale + CGFloat w = _group.proposedViewSize.width*_clipScale; + CGFloat h = _group.proposedViewSize.height*_clipScale; + _scale = MIN(w/source.width,h/source.height); } - (void)_prepClip:(IJSVGNode *)node @@ -412,20 +418,30 @@ static NSColor * _baseColor = nil; // fill the path if( path.fillGradient != nil ) { - if( [path.fillGradient isKindOfClass:[IJSVGLinearGradient class]] ) + CGContextSaveGState(ref); { - // linear gradient - NSGradient * gradient = [path.fillGradient gradient];; - [gradient drawInBezierPath:path.path - angle:path.fillGradient.angle]; - } else if( [path.fillGradient isKindOfClass:[IJSVGRadialGradient class]] ) - { - // radial gradient - // very rudimentary at the moment - IJSVGRadialGradient * radGrad = (IJSVGRadialGradient *)path.fillGradient; - [radGrad.gradient drawInBezierPath:path.path - relativeCenterPosition:NSZeroPoint]; - } + // apply any gradient fill transforms + for( IJSVGTransform * transform in path.gradientTransforms ) + { + [IJSVGTransform performTransform:transform + inContext:ref]; + } + + if( [path.fillGradient isKindOfClass:[IJSVGLinearGradient class]] ) + { + // linear gradient + NSGradient * gradient = [path.fillGradient gradient];; + [gradient drawInBezierPath:path.path + angle:path.fillGradient.angle]; + } else if( [path.fillGradient isKindOfClass:[IJSVGRadialGradient class]] ) + { + // radial gradient + // very rudimentary at the moment + IJSVGRadialGradient * radGrad = (IJSVGRadialGradient *)path.fillGradient; + [radGrad.gradient drawInBezierPath:path.path + relativeCenterPosition:NSZeroPoint]; + } + } CGContextRestoreGState(ref); } else { // no gradient specified // just use the color instead diff --git a/source/IJSVGCommand.h b/source/IJSVGCommand.h index ebc28a1..54fb30a 100644 --- a/source/IJSVGCommand.h +++ b/source/IJSVGCommand.h @@ -55,6 +55,9 @@ typedef NS_ENUM( NSInteger, IJSVGCommandType ) { - (id)initWithCommandString:(NSString *)commandString; ++ (NSPoint)readCoordinatePair:(CGFloat *)pairs + index:(NSInteger)index; + + (void)registerClass:(Class)aClass forCommand:(NSString *)command; + (NSDictionary *)registeredCommandClasses; diff --git a/source/IJSVGCommand.m b/source/IJSVGCommand.m index 2de487c..60eaafa 100644 --- a/source/IJSVGCommand.m +++ b/source/IJSVGCommand.m @@ -90,6 +90,12 @@ static NSMutableDictionary * _classes = nil; return self; } ++ (NSPoint)readCoordinatePair:(CGFloat *)pairs + index:(NSInteger)index +{ + return NSMakePoint( pairs[index*2], pairs[index*2+1]); +} + + (void)registerClass:(Class)aClass forCommand:(NSString *)command { diff --git a/source/IJSVGCommandArc.m b/source/IJSVGCommandArc.m index 9d01578..1ed1c9e 100644 --- a/source/IJSVGCommandArc.m +++ b/source/IJSVGCommandArc.m @@ -27,7 +27,27 @@ type:(IJSVGCommandType)type path:(IJSVGPath *)path { - +// NSPoint point = [path currentPoint]; +// +// CGFloat x1 = point.x; +// CGFloat y1 = point.y; +// +// NSPoint radii = [[self class] readCoordinatePair:params +// index:0]; +// +// CGFloat rX = fabs(radii.x); +// CGFloat rY = fabs(radii.y); +// +// CGFloat phi = params[2]; +// phi *= M_PI/180; +// +// BOOL largeArcFlag = params[3] != 0.f; +// BOOL sweepFlag = params[4] != 0.f; +// +// NSPoint endPoint = NSMakePoint( params[5], params[6]); +// +// CGFloat x2 = point.x + endPoint.x; +// CGFloat y2 = point.y + endPoint.y; } @end diff --git a/source/IJSVGGradient.m b/source/IJSVGGradient.m index fbda625..9f8f293 100644 --- a/source/IJSVGGradient.m +++ b/source/IJSVGGradient.m @@ -19,6 +19,13 @@ [super dealloc]; } +- (id)copyWithZone:(NSZone *)zone +{ + IJSVGGradient * clone = [super copyWithZone:zone]; + clone.gradient = [[self.gradient copy] autorelease]; + return clone; +} + + (CGFloat *)computeColorStopsFromString:(NSXMLElement *)element colors:(NSArray **)someColors { diff --git a/source/IJSVGLinearGradient.m b/source/IJSVGLinearGradient.m index e42b1dc..d7bc0a5 100644 --- a/source/IJSVGLinearGradient.m +++ b/source/IJSVGLinearGradient.m @@ -33,10 +33,14 @@ aGradient.angle = [IJSVGUtils angleBetweenPointA:NSMakePoint( x1, y1 ) pointb:NSMakePoint( x2, y2 )]; + if( aGradient.gradient != nil ) + return nil; + // compute the color stops and colours NSArray * colors = nil; CGFloat * stopsParams = [[self class] computeColorStopsFromString:element colors:&colors]; + // create the gradient with the colours NSGradient * grad = [[[NSGradient alloc] initWithColors:colors atLocations:stopsParams diff --git a/source/IJSVGNode.h b/source/IJSVGNode.h index 60e52f8..37614c6 100644 --- a/source/IJSVGNode.h +++ b/source/IJSVGNode.h @@ -83,6 +83,7 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991; IJSVGNode * parentNode; IJSVGGroup * clipPath; NSArray * transforms; + NSArray * gradientTransforms; IJSVGWindingRule windingRule; IJSVGLineCapStyle lineCapStyle; @@ -112,6 +113,7 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991; @property ( nonatomic, assign ) IJSVGLineCapStyle lineCapStyle; @property ( nonatomic, assign ) IJSVGLineJoinStyle lineJoinStyle; @property ( nonatomic, retain ) NSArray * transforms; +@property ( nonatomic, retain ) NSArray * gradientTransforms; @property ( nonatomic, retain ) IJSVGDef * def; @property ( nonatomic, retain ) IJSVGGradient * fillGradient; @property ( nonatomic, assign ) CGFloat * strokeDashArray; @@ -120,6 +122,7 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991; + (IJSVGNodeType)typeForString:(NSString *)string; +- (void)applyPropertiesFromNode:(IJSVGNode *)node; - (id)initWithDef:(BOOL)flag; - (void)addDef:(IJSVGNode *)aDef; - (IJSVGDef *)defForID:(NSString *)anID; diff --git a/source/IJSVGNode.m b/source/IJSVGNode.m index 48932d8..29959f0 100644 --- a/source/IJSVGNode.m +++ b/source/IJSVGNode.m @@ -26,6 +26,7 @@ @synthesize identifier; @synthesize parentNode; @synthesize transforms; +@synthesize gradientTransforms; @synthesize windingRule; @synthesize def; @synthesize fillGradient; @@ -42,6 +43,7 @@ free(strokeDashArray); [fillGradient release], fillGradient = nil; [transforms release], transforms = nil; + [gradientTransforms release], gradientTransforms = nil; [fillColor release], fillColor = nil; [strokeColor release], strokeColor = nil; [identifier release], identifier = nil; @@ -90,45 +92,50 @@ return self; } -- (id)copyWithZone:(NSZone *)zone +- (void)applyPropertiesFromNode:(IJSVGNode *)node { - IJSVGNode * node = [[self class] allocWithZone:zone]; - node.name = self.name; - node.type = self.type; + self.name = node.name; + self.type = node.type; - node.x = self.x; - node.y = self.y; - node.width = self.width; - node.height = self.height; + self.x = node.x; + self.y = node.y; + self.width = node.width; + self.height = node.height; - node.fillGradient = self.fillGradient; + self.fillGradient = node.fillGradient; - node.fillColor = self.fillColor; - node.strokeColor = self.strokeColor; - node.clipPath = self.clipPath; + self.fillColor = node.fillColor; + self.strokeColor = node.strokeColor; + self.clipPath = node.clipPath; - node.opacity = self.opacity; - node.strokeWidth = self.strokeWidth; - node.fillOpacity = self.fillOpacity; - node.strokeOpacity = self.strokeOpacity; + self.opacity = node.opacity; + self.strokeWidth = node.strokeWidth; + self.fillOpacity = node.fillOpacity; + self.strokeOpacity = node.strokeOpacity; - node.identifier = self.identifier; - node.usesDefaultFillColor = self.usesDefaultFillColor; + self.identifier = node.identifier; + self.usesDefaultFillColor = node.usesDefaultFillColor; - node.transforms = self.transforms; - node.def = self.def; - node.windingRule = self.windingRule; - node.lineCapStyle = self.lineCapStyle; - node.lineJoinStyle = self.lineJoinStyle; - node.parentNode = self.parentNode; + self.transforms = node.transforms; + self.gradientTransforms = node.gradientTransforms; + self.def = node.def; + self.windingRule = node.windingRule; + self.lineCapStyle = node.lineCapStyle; + self.lineJoinStyle = node.lineJoinStyle; + self.parentNode = node.parentNode; // dash array needs physical memory copied CGFloat * nStrokeDashArray = (CGFloat *)malloc(node.strokeDashArrayCount*sizeof(CGFloat)); memcpy( self.strokeDashArray, nStrokeDashArray, node.strokeDashArrayCount*sizeof(CGFloat)); - node.strokeDashArray = nStrokeDashArray; - node.strokeDashArrayCount = self.strokeDashArrayCount; - node.strokeDashOffset = self.strokeDashOffset; - + self.strokeDashArray = nStrokeDashArray; + self.strokeDashArrayCount = node.strokeDashArrayCount; + self.strokeDashOffset = node.strokeDashOffset; +} + +- (id)copyWithZone:(NSZone *)zone +{ + IJSVGNode * node = [[self class] allocWithZone:zone]; + [node applyPropertiesFromNode:self]; return node; } diff --git a/source/IJSVGParser.h b/source/IJSVGParser.h index a178c40..1b80ed8 100644 --- a/source/IJSVGParser.h +++ b/source/IJSVGParser.h @@ -34,6 +34,7 @@ handleForeignObject:(IJSVGForeignObject *)foreignObject @interface IJSVGParser : IJSVGGroup { NSRect viewBox; + NSSize proposedViewSize; @private id _delegate; @@ -42,6 +43,7 @@ handleForeignObject:(IJSVGForeignObject *)foreignObject } @property ( nonatomic, readonly ) NSRect viewBox; +@property ( nonatomic, readonly ) NSSize proposedViewSize; - (id)initWithFileURL:(NSURL *)aURL delegate:(id)delegate; diff --git a/source/IJSVGParser.m b/source/IJSVGParser.m index fae8e69..0e803aa 100644 --- a/source/IJSVGParser.m +++ b/source/IJSVGParser.m @@ -11,6 +11,7 @@ @implementation IJSVGParser @synthesize viewBox; +@synthesize proposedViewSize; + (IJSVGParser *)groupForFileURL:(NSURL *)aURL { @@ -98,6 +99,15 @@ viewBox = NSMakeRect( 0.f, 0.f, w, h ); } + CGFloat w = [[[svgElement attributeForName:@"width"] stringValue] floatValue]; + CGFloat h = [[[svgElement attributeForName:@"height"] stringValue] floatValue]; + if( w == 0.f && h == 0.f ) + { + w = viewBox.size.width; + h = viewBox.size.height; + } + proposedViewSize = NSMakeSize( w, h ); + // find foreign objects... NSXMLElement * switchElement = nil; NSArray * switchElements = [svgElement nodesForXPath:@"switch" @@ -260,6 +270,17 @@ node.transforms = tran; } + // gradient transforms + NSXMLNode * gradTransformAttribute = [element attributeForName:@"gradientTransform"]; + if( gradTransformAttribute != nil ) + { + NSMutableArray * tran = [[[NSMutableArray alloc] init] autorelease]; + [tran addObjectsFromArray:[IJSVGTransform transformsForString:[gradTransformAttribute stringValue]]]; + if( node.gradientTransforms != nil ) + [tran addObjectsFromArray:node.gradientTransforms]; + node.gradientTransforms = tran; + } + // winding rule NSXMLNode * windingRuleAttribute = [element attributeForName:@"fill-rule"]; if( windingRuleAttribute != nil ) @@ -606,6 +627,24 @@ // linear gradient case IJSVGNodeTypeLinearGradient: { + NSString * xlink = [[element attributeForName:@"xlink:href"] stringValue]; + NSString * xlinkID = [xlink substringFromIndex:1]; + IJSVGNode * node = [parentGroup defForID:xlinkID]; + if( node != nil ) + { + // we are a clone + IJSVGLinearGradient * grad = [[[IJSVGLinearGradient alloc] init] autorelease]; + grad.type = aType; + [grad applyPropertiesFromNode:node]; + grad.gradient = [[[(IJSVGGradient *)node gradient] copy] autorelease]; + [IJSVGLinearGradient parseGradient:element + gradient:grad]; + [self _parseElementForCommonAttributes:element + node:grad]; + [parentGroup addDef:grad]; + continue; + } + IJSVGLinearGradient * gradient = [[[IJSVGLinearGradient alloc] init] autorelease]; gradient.type = aType; gradient.gradient = [IJSVGLinearGradient parseGradient:element @@ -618,6 +657,25 @@ // radial gradient case IJSVGNodeTypeRadialGradient: { + + NSString * xlink = [[element attributeForName:@"xlink:href"] stringValue]; + NSString * xlinkID = [xlink substringFromIndex:1]; + IJSVGNode * node = [parentGroup defForID:xlinkID]; + if( node != nil ) + { + // we are a clone + IJSVGRadialGradient * grad = [[[IJSVGRadialGradient alloc] init] autorelease]; + grad.type = aType; + [grad applyPropertiesFromNode:node]; + grad.gradient = [[[(IJSVGGradient *)node gradient] copy] autorelease]; + [IJSVGRadialGradient parseGradient:element + gradient:grad]; + [self _parseElementForCommonAttributes:element + node:grad]; + [parentGroup addDef:grad]; + continue; + } + IJSVGRadialGradient * gradient = [[[IJSVGRadialGradient alloc] init] autorelease]; gradient.type = aType; gradient.gradient = [IJSVGRadialGradient parseGradient:element diff --git a/source/IJSVGRadialGradient.m b/source/IJSVGRadialGradient.m index 865ca21..a3e4635 100644 --- a/source/IJSVGRadialGradient.m +++ b/source/IJSVGRadialGradient.m @@ -16,6 +16,7 @@ @synthesize fy; @synthesize radius; + + (NSGradient *)parseGradient:(NSXMLElement *)element gradient:(IJSVGRadialGradient *)gradient { @@ -39,6 +40,9 @@ gradient.fy = .5; } + if( gradient.gradient != nil ) + return nil; + NSArray * colors = nil; CGFloat * colorStops = [[self class] computeColorStopsFromString:element colors:&colors]; NSGradient * ret = [[[NSGradient alloc] initWithColors:colors diff --git a/source/IJSVGUtils.m b/source/IJSVGUtils.m index 2f520b1..e82a908 100644 --- a/source/IJSVGUtils.m +++ b/source/IJSVGUtils.m @@ -174,7 +174,7 @@ } + (CGFloat)floatValue:(NSString *)string - fallBackForPercent:(CGFloat)fallBack + fallBackForPercent:(CGFloat)fallBack { CGFloat val = [string floatValue]; if( [string rangeOfString:@"%"].location != NSNotFound )