diff --git a/IJSVG.m b/IJSVG.m index 320b94c..768beea 100644 --- a/IJSVG.m +++ b/IJSVG.m @@ -207,21 +207,86 @@ - (void)_drawGroup:(IJSVGGroup *)group [self _applyDefaults:context node:group]; - // it could be a group or a path - for( id child in [group children] ) + dispatch_block_t drawBlock = ^{ + // it could be a group or a path + for( id child in [group children] ) + { + if( [child isKindOfClass:[IJSVGPath class]] ) + { + IJSVGPath * p = (IJSVGPath *)child; + if( p.clipPath != nil ) + { + for( id clip in p.clipPath.children ) + { + if( [clip isKindOfClass:[IJSVGGroup class]] ) + [self _drawGroup:clip + rect:rect]; + else { + + // there is a clip path, save the context + CGContextSaveGState( context ); + + // add the clip + IJSVGPath * p = (IJSVGPath *)clip; + [p.path addClip]; + + // draw the path + [self _drawPath:child + rect:rect]; + + // restore the context + CGContextRestoreGState( context ); + } + } + } else { + // as its just a path, we can happily + // just draw it in the current context + [self _drawPath:child + rect:rect]; + } + } else if( [child isKindOfClass:[IJSVGGroup class]] ) { + + // if its a group, we recursively call this method + // to generate the paths required + [self _drawGroup:child + rect:rect]; + } + } + + }; + + // group clipping + if( group.clipPath != nil ) { - if( [child isKindOfClass:[IJSVGPath class]] ) - // as its just a path, we can happily - // just draw it in the current context - [self _drawPath:child - rect:rect]; - else if( [child isKindOfClass:[IJSVGGroup class]] ) - - // if its a group, we recursively call this method - // to generate the paths required - [self _drawGroup:child - rect:rect]; - } + + // find the clipped children + for( id child in group.clipPath.children ) + { + // if its a group, run this again + if( [child isKindOfClass:[IJSVGGroup class]] ) + [self _drawGroup:child + rect:rect]; + else { + + // save the context state + CGContextSaveGState( context ); + + // find the path + IJSVGPath * p = (IJSVGPath *)child; + + // clip the context + [p.path addClip]; + + // draw the paths + drawBlock(); + + // restore again + CGContextRestoreGState( context ); + } + } + } else + // just draw the block + drawBlock(); // restore the context CGContextRestoreGState(context); @@ -289,7 +354,7 @@ - (void)_drawPath:(IJSVGPath *)path [path.fillColor set]; [path.path fill]; } else if( _baseColor != nil ) { - + // is there a base color? // this is basically used whenever no color // is set, its also set via [IJSVG setBaseColor], @@ -297,6 +362,8 @@ - (void)_drawPath:(IJSVGPath *)path [_baseColor set]; [path.path fill]; + } else { + [path.path fill]; } } diff --git a/IJSVGExample/IJSVGExample.xcodeproj/project.pbxproj b/IJSVGExample/IJSVGExample.xcodeproj/project.pbxproj index 5587dd1..4e10abf 100644 --- a/IJSVGExample/IJSVGExample.xcodeproj/project.pbxproj +++ b/IJSVGExample/IJSVGExample.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 5910114B19B739B7009F8A35 /* IJSVGStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 5910114A19B739B7009F8A35 /* IJSVGStyle.m */; }; 5910114E19B75C9C009F8A35 /* IJSVGGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 5910114D19B75C9C009F8A35 /* IJSVGGradient.m */; }; 5910115119B75E44009F8A35 /* IJSVGRadialGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 5910115019B75E44009F8A35 /* IJSVGRadialGradient.m */; }; + 59459CEC19B906FE00CE493B /* clipped.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59459CEB19B906FE00CE493B /* clipped.svg */; }; + 59459CEF19B9074F00CE493B /* SVGExampleView4.m in Sources */ = {isa = PBXBuildFile; fileRef = 59459CEE19B9074F00CE493B /* SVGExampleView4.m */; }; 5956657D19B62F4600D805FF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956657C19B62F4600D805FF /* main.m */; }; 5956658019B62F4600D805FF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956657F19B62F4600D805FF /* AppDelegate.m */; }; 5956658219B62F4600D805FF /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5956658119B62F4600D805FF /* Images.xcassets */; }; @@ -67,6 +69,9 @@ 5910114D19B75C9C009F8A35 /* IJSVGGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IJSVGGradient.m; path = ../../IJSVGGradient.m; sourceTree = ""; }; 5910114F19B75E44009F8A35 /* IJSVGRadialGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJSVGRadialGradient.h; path = ../../IJSVGRadialGradient.h; sourceTree = ""; }; 5910115019B75E44009F8A35 /* IJSVGRadialGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IJSVGRadialGradient.m; path = ../../IJSVGRadialGradient.m; sourceTree = ""; }; + 59459CEB19B906FE00CE493B /* clipped.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = clipped.svg; sourceTree = ""; }; + 59459CED19B9074F00CE493B /* SVGExampleView4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVGExampleView4.h; sourceTree = ""; }; + 59459CEE19B9074F00CE493B /* SVGExampleView4.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SVGExampleView4.m; sourceTree = ""; }; 5956657719B62F4600D805FF /* IJSVGExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IJSVGExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 5956657B19B62F4600D805FF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 5956657C19B62F4600D805FF /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -194,6 +199,8 @@ 59A11E6E19B89D2000E44498 /* SVGExampleView2.m */, 59A11E7019B89D6600E44498 /* SVGExampleView3.h */, 59A11E7119B89D6600E44498 /* SVGExampleView3.m */, + 59459CED19B9074F00CE493B /* SVGExampleView4.h */, + 59459CEE19B9074F00CE493B /* SVGExampleView4.m */, ); path = IJSVGExample; sourceTree = ""; @@ -201,6 +208,7 @@ 5956657A19B62F4600D805FF /* Supporting Files */ = { isa = PBXGroup; children = ( + 59459CEB19B906FE00CE493B /* clipped.svg */, 59F799E119B880CE00096CB7 /* htc_one.svg */, 59B93C6E19B7D32C0063E823 /* products.svg */, 59B93C6C19B7D1840063E823 /* paperplane.svg */, @@ -377,6 +385,7 @@ 59F799E219B880CE00096CB7 /* htc_one.svg in Resources */, 59B93C6F19B7D32C0063E823 /* products.svg in Resources */, 5956658519B62F4600D805FF /* MainMenu.xib in Resources */, + 59459CEC19B906FE00CE493B /* clipped.svg in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -427,6 +436,7 @@ 5910114E19B75C9C009F8A35 /* IJSVGGradient.m in Sources */, 59A11E6F19B89D2000E44498 /* SVGExampleView2.m in Sources */, 595665CA19B6302600D805FF /* IJSVGColor.m in Sources */, + 59459CEF19B9074F00CE493B /* SVGExampleView4.m in Sources */, 595665DB19B6302600D805FF /* IJSVGUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; 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 9d2845a..1b43fe7 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/IJSVGExample/IJSVGExample/Base.lproj/MainMenu.xib b/IJSVGExample/IJSVGExample/Base.lproj/MainMenu.xib index 9e017cd..545ac1d 100644 --- a/IJSVGExample/IJSVGExample/Base.lproj/MainMenu.xib +++ b/IJSVGExample/IJSVGExample/Base.lproj/MainMenu.xib @@ -689,8 +689,12 @@ - - + + + + + + diff --git a/IJSVGExample/IJSVGExample/SVGExampleView4.h b/IJSVGExample/IJSVGExample/SVGExampleView4.h new file mode 100644 index 0000000..62f8632 --- /dev/null +++ b/IJSVGExample/IJSVGExample/SVGExampleView4.h @@ -0,0 +1,14 @@ +// +// SVGExampleView4.h +// IJSVGExample +// +// Created by Curtis Hard on 04/09/2014. +// Copyright (c) 2014 Curtis Hard. All rights reserved. +// + +#import +#import "SVGView.h" + +@interface SVGExampleView4 : SVGView + +@end diff --git a/IJSVGExample/IJSVGExample/SVGExampleView4.m b/IJSVGExample/IJSVGExample/SVGExampleView4.m new file mode 100644 index 0000000..bf971cd --- /dev/null +++ b/IJSVGExample/IJSVGExample/SVGExampleView4.m @@ -0,0 +1,18 @@ +// +// SVGExampleView4.m +// IJSVGExample +// +// Created by Curtis Hard on 04/09/2014. +// Copyright (c) 2014 Curtis Hard. All rights reserved. +// + +#import "SVGExampleView4.h" + +@implementation SVGExampleView4 + +- (IJSVG *)svg +{ + return [[IJSVG svgNamed:@"clipped"] retain]; +} + +@end diff --git a/IJSVGExample/IJSVGExample/clipped.svg b/IJSVGExample/IJSVGExample/clipped.svg new file mode 100644 index 0000000..a6a4ad6 --- /dev/null +++ b/IJSVGExample/IJSVGExample/clipped.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/IJSVGNode.h b/IJSVGNode.h index 83f38ef..3e29ddf 100644 --- a/IJSVGNode.h +++ b/IJSVGNode.h @@ -11,6 +11,7 @@ @class IJSVGDef; @class IJSVGGradient; +@class IJSVGGroup; typedef NS_OPTIONS( NSInteger, IJSVGNodeType ) { IJSVGNodeTypeGroup, @@ -25,6 +26,7 @@ typedef NS_OPTIONS( NSInteger, IJSVGNodeType ) { IJSVGNodeTypeUse, IJSVGNodeTypeLinearGradient, IJSVGNodeTypeRadialGradient, + IJSVGNodeTypeClipPath, IJSVGNodeTypeNotFound }; @@ -59,6 +61,7 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991; NSString * identifier; IJSVGNode * parentNode; + IJSVGGroup * clipPath; NSArray * transforms; IJSVGWindingRule windingRule; @@ -81,6 +84,7 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991; @property ( nonatomic, retain ) NSColor * strokeColor; @property ( nonatomic, copy ) NSString * identifier; @property ( nonatomic, assign ) IJSVGNode * parentNode; +@property ( nonatomic, assign ) IJSVGGroup * clipPath; @property ( nonatomic, assign ) IJSVGWindingRule windingRule; @property ( nonatomic, retain ) NSArray * transforms; @property ( nonatomic, retain ) IJSVGDef * def; diff --git a/IJSVGNode.m b/IJSVGNode.m index 9987f11..1d28e53 100644 --- a/IJSVGNode.m +++ b/IJSVGNode.m @@ -29,6 +29,7 @@ @implementation IJSVGNode @synthesize windingRule; @synthesize def; @synthesize fillGradient; +@synthesize clipPath; - (void)dealloc { @@ -69,6 +70,8 @@ + (IJSVGNodeType)typeForString:(NSString *)string return IJSVGNodeTypeLinearGradient; if( [string isEqualToString:@"radialgradient"] ) return IJSVGNodeTypeRadialGradient; + if( [string isEqualToString:@"clippath"] ) + return IJSVGNodeTypeClipPath; return IJSVGNodeTypeNotFound; } @@ -95,6 +98,7 @@ - (id)copyWithZone:(NSZone *)zone node.fillColor = self.fillColor; node.strokeColor = self.strokeColor; + node.clipPath = self.clipPath; node.opacity = self.opacity; node.strokeWidth = self.strokeWidth; diff --git a/IJSVGParser.m b/IJSVGParser.m index 02c4d7b..c732dac 100644 --- a/IJSVGParser.m +++ b/IJSVGParser.m @@ -153,6 +153,16 @@ - (void)_parse - (void)_parseElementForCommonAttributes:(NSXMLElement *)element node:(IJSVGNode *)node { + + // any clippath? + NSXMLNode * clipPathAttribute = [element attributeForName:@"clip-path"]; + if( clipPathAttribute != nil ) + { + NSString * clipID = [IJSVGUtils defURL:[clipPathAttribute stringValue]]; + if( clipID ) + node.clipPath = (IJSVGGroup *)[node defForID:clipID]; + } + // work out any extra attributes // opacity NSXMLNode * opacityAttribute = [element attributeForName:@"opacity"]; @@ -257,7 +267,9 @@ - (void)_parseBlock:(NSXMLElement *)anElement // find common attributes [self _parseElementForCommonAttributes:element node:group]; - [parentGroup addChild:group]; + + if( !flag ) + [parentGroup addChild:group]; // recursively parse blocks [self _parseBlock:element @@ -265,7 +277,7 @@ - (void)_parseBlock:(NSXMLElement *)anElement def:NO]; // could be defined - if( def ) + if( flag ) [parentGroup addDef:group]; continue; } @@ -282,10 +294,12 @@ - (void)_parseBlock:(NSXMLElement *)anElement node:path]; [self _parsePathCommandData:[[element attributeForName:@"d"] stringValue] intoPath:path]; - [parentGroup addChild:path]; + + if( !flag ) + [parentGroup addChild:path]; // could be defined - if( def ) + if( flag ) [parentGroup addDef:path]; continue; } @@ -302,10 +316,12 @@ - (void)_parseBlock:(NSXMLElement *)anElement node:path]; [self _parsePolygon:element intoPath:path]; - [parentGroup addChild:path]; + + if( !flag ) + [parentGroup addChild:path]; // could be defined - if( def ) + if( flag ) [parentGroup addDef:path]; continue; } @@ -322,10 +338,12 @@ - (void)_parseBlock:(NSXMLElement *)anElement node:path]; [self _parsePolyline:element intoPath:path]; - [parentGroup addChild:path]; + + if( !flag ) + [parentGroup addChild:path]; // could be defined - if( def ) + if( flag ) [parentGroup addDef:path]; continue; } @@ -342,10 +360,12 @@ - (void)_parseBlock:(NSXMLElement *)anElement node:path]; [self _parseRect:element intoPath:path]; - [parentGroup addChild:path]; + + if( !flag ) + [parentGroup addChild:path]; // could be defined - if( def ) + if( flag ) [parentGroup addDef:path]; continue; } @@ -365,7 +385,7 @@ - (void)_parseBlock:(NSXMLElement *)anElement [parentGroup addChild:path]; // could be defined - if( def ) + if( flag ) [parentGroup addDef:path]; continue; } @@ -382,10 +402,12 @@ - (void)_parseBlock:(NSXMLElement *)anElement node:path]; [self _parseCircle:element intoPath:path]; - [parentGroup addChild:path]; + + if( !flag ) + [parentGroup addChild:path]; // could be defined - if( def ) + if( flag ) [parentGroup addDef:path]; continue; } @@ -402,10 +424,12 @@ - (void)_parseBlock:(NSXMLElement *)anElement node:path]; [self _parseEllipse:element intoPath:path]; - [parentGroup addChild:path]; + + if( !flag ) + [parentGroup addChild:path]; // could be defined - if( def ) + if( flag ) [parentGroup addDef:path]; continue; } @@ -453,6 +477,26 @@ - (void)_parseBlock:(NSXMLElement *)anElement continue; } + // clippath + case IJSVGNodeTypeClipPath: { + IJSVGGroup * group = [[[IJSVGGroup alloc] init] autorelease]; + group.type = aType; + group.name = subName; + group.parentNode = parentGroup; + + // find common attributes + [self _parseElementForCommonAttributes:element + node:group]; + + // recursively parse blocks + [self _parseBlock:element + intoGroup:group + def:NO]; + + // add it as a def + [parentGroup addDef:group]; + } + } } }