diff --git a/pxSVG-TestApp/ViewController.m b/pxSVG-TestApp/ViewController.m index b1da327..0c508ad 100644 --- a/pxSVG-TestApp/ViewController.m +++ b/pxSVG-TestApp/ViewController.m @@ -23,21 +23,23 @@ - (instancetype)initWithFrame:(CGRect)frame sv.svgDelegate = self; sv.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; [self.contentView addSubview:_svgView=sv]; + self.clipsToBounds = YES; return self; } - (void)svgViewDidLoadImage:(pxSVGView *)svgView { - NSLog(@"%@",self.SVGURL); + NSLog(@"load %@",self.SVGURL); } - (void)svgView:(pxSVGView *)svgLayer didFailedLoad:(NSError *)error { - NSLog(@"%@ %@",self.SVGURL,error); + NSLog(@"err %@ %@",self.SVGURL,error); } - (void)setSVGURL:(NSURL *)SVGURL { + _SVGURL = SVGURL; [self.svgView loadURL:SVGURL]; } diff --git a/pxSVG/pxSVGImage.h b/pxSVG/pxSVGImage.h index 4395767..555f48d 100644 --- a/pxSVG/pxSVGImage.h +++ b/pxSVG/pxSVGImage.h @@ -12,4 +12,5 @@ + (instancetype) svgImageWithXML:(NSString*)data; - (instancetype) initWithXML:(NSString*)data; @property (nonatomic,readonly) CGRect bounds; +- (CALayer*)makeLayer; @end diff --git a/pxSVG/pxSVGImage.m b/pxSVG/pxSVGImage.m index 9fb4ae1..fb91a7e 100644 --- a/pxSVG/pxSVGImage.m +++ b/pxSVG/pxSVGImage.m @@ -37,4 +37,8 @@ - (CGRect)bounds { return self.renderPath.bounds; } +- (CALayer *)makeLayer +{ + return [self.renderPath makeLayer]; +} @end diff --git a/pxSVG/pxSVGLayer.h b/pxSVG/pxSVGLayer.h index 61d0693..8288373 100644 --- a/pxSVG/pxSVGLayer.h +++ b/pxSVG/pxSVGLayer.h @@ -20,4 +20,5 @@ - (void) loadData:(NSData*)data; - (void) loadString:(NSString*)string; - (void) loadURL:(NSURL*)url; +@property (nonatomic,readonly) CGRect contentRect; @end diff --git a/pxSVG/pxSVGLayer.m b/pxSVG/pxSVGLayer.m index 45b56a0..cd347bc 100644 --- a/pxSVG/pxSVGLayer.m +++ b/pxSVG/pxSVGLayer.m @@ -58,6 +58,7 @@ - (void)loadURL:(NSURL *)url NSBlockOperation *sync = [NSBlockOperation blockOperationWithBlock:^{ if ([op isCancelled]) return; if (!weakself) return; + weakself.loadOperation = nil; NSError *error = err; if (!error && [resp isKindOfClass:[NSHTTPURLResponse class]] && (((NSHTTPURLResponse*)resp).statusCode != 200)) error = [NSError errorWithDomain:@"pxSVGLoader.httpStatus" code:((NSHTTPURLResponse*)resp).statusCode userInfo:nil]; @@ -87,8 +88,10 @@ - (void)loadString:(NSString *)string if ([op isCancelled]) return; NSBlockOperation *sync = [NSBlockOperation blockOperationWithBlock:^{ if ([op isCancelled]) return; + if (!weakself) return; + weakself.parseOperation = nil; if (!img) return [weakself loadError:[NSError errorWithDomain:@"pxSVGParser.parseError" code:0 userInfo:nil]]; - NSLog(@"%@ %@",img,[NSValue valueWithCGRect:img.bounds]); + [weakself loadImage:img]; }]; [[NSOperationQueue mainQueue] addOperations:@[sync] waitUntilFinished:YES]; }]; @@ -96,6 +99,14 @@ - (void)loadString:(NSString *)string [[self.class parseQueue] addOperation:self.parseOperation=op]; } +- (void)loadImage:(pxSVGImage*)image +{ + [self clean]; + [self addSublayer:[image makeLayer]]; + if ([self.svgDelegate respondsToSelector:@selector(svgLayerDidLoadImage:)]) + [self.svgDelegate svgLayerDidLoadImage:self]; +} + - (void)loadError:(NSError *)error { if ([self.svgDelegate respondsToSelector:@selector(svgLayer:didFailedLoad:)]) @@ -106,6 +117,16 @@ - (void)clean { if (self.loadOperation) [self.loadOperation cancel]; if (self.parseOperation) [self.parseOperation cancel]; + while (self.sublayers.count) [self.sublayers.firstObject removeFromSuperlayer]; +} + +- (CGRect)contentRect +{ + CGRect f = CGRectNull; + for (CALayer *l in self.sublayers) { + f = CGRectUnion(f, l.frame); + } + return f; } @end diff --git a/pxSVG/pxSVGObject.m b/pxSVG/pxSVGObject.m index a71dca6..039eebf 100644 --- a/pxSVG/pxSVGObject.m +++ b/pxSVG/pxSVGObject.m @@ -61,7 +61,7 @@ - (UIColor*) colorWithSVGColor:(NSString*)string { if (!string) return nil; string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - if ([string isEqualToString:@"none"]) return nil; + if ([string isEqualToString:@"none"]) return [UIColor clearColor]; if ([string isEqualToString:@"black"]) return [UIColor blackColor]; if ([string isEqualToString:@"white"]) return [UIColor whiteColor]; NSScanner *sc = [NSScanner scannerWithString:[string lowercaseString]]; @@ -98,9 +98,9 @@ - (void)loadAttributes:(NSDictionary *)attributes self.id = [attributes objectForKey:@"id"]; self.fillColor = [self colorWithSVGColor:[attributes objectForKey:@"fill"]]; self.strokeColor = [self colorWithSVGColor:[attributes objectForKey:@"stroke"]]; - self.strokeWidth = [[attributes objectForKey:@"stroke-width"] doubleValue]; + self.strokeWidth = [attributes objectForKey:@"stroke-width"]?[[attributes objectForKey:@"stroke-width"] doubleValue]:NAN; self.transform = [self.class transformFromString:[attributes objectForKey:@"transform"]]; - self.opacity = [[attributes objectForKey:@"opacity"] doubleValue]; + self.opacity = [attributes objectForKey:@"opacity"]?[[attributes objectForKey:@"opacity"] doubleValue]:1; } - (void)setSubnodes:(NSArray *)subnodes { } @end diff --git a/pxSVG/pxSVGPath.m b/pxSVG/pxSVGPath.m index a068697..1caa64e 100644 --- a/pxSVG/pxSVGPath.m +++ b/pxSVG/pxSVGPath.m @@ -171,7 +171,7 @@ - (void)loadAttributes:(NSDictionary *)attributes [attributes objectForKey:@"ry"]) { CGRect r; r.size.width = [[attributes objectForKey:@"rx"] doubleValue]; - r.size.height = [[attributes objectForKey:@"rx"] doubleValue]; + r.size.height = [[attributes objectForKey:@"ry"] doubleValue]; r.origin.x = [[attributes objectForKey:@"cx"] doubleValue] - r.size.width; r.origin.y = [[attributes objectForKey:@"cy"] doubleValue] - r.size.height; r.size.width *= 2; r.size.height *= 2; @@ -195,7 +195,6 @@ - (void)loadAttributes:(NSDictionary *)attributes r.size.height = [[attributes objectForKey:@"height"] doubleValue]; r.origin.x = [[attributes objectForKey:@"x"] doubleValue]; r.origin.y = [[attributes objectForKey:@"y"] doubleValue]; - r.size.width *= 2; r.size.height *= 2; self.d = [UIBezierPath bezierPathWithRect:r]; } else NSLog(@"%@",attributes); } diff --git a/pxSVG/pxSVGRenderPath.h b/pxSVG/pxSVGRenderPath.h index 9b1a87f..cda92b2 100644 --- a/pxSVG/pxSVGRenderPath.h +++ b/pxSVG/pxSVGRenderPath.h @@ -13,4 +13,5 @@ + (instancetype) pathWithXML:(pxXMLNode*)xmlNode; - (instancetype) initWithXML:(pxXMLNode*)xmlNode; @property (readonly) CGRect bounds; +- (CALayer*)makeLayer; @end diff --git a/pxSVG/pxSVGRenderPath.m b/pxSVG/pxSVGRenderPath.m index f4f21d2..d00ec59 100644 --- a/pxSVG/pxSVGRenderPath.m +++ b/pxSVG/pxSVGRenderPath.m @@ -24,7 +24,7 @@ + (instancetype)pathWithXML:(pxXMLNode *)xmlNode - (instancetype)initWithXML:(pxXMLNode *)xmlNode { self = [super init]; - self.root = [self parseObject:xmlNode]; + self.root = [self parseObject:xmlNode inheritAttributes:nil]; if ([xmlNode.attributes objectForKey:@"width"] && [xmlNode.attributes objectForKey:@"height"]) { CGPoint o = CGPointZero; @@ -72,7 +72,7 @@ - (CGRect) objBounds:(pxSVGObject*)obj } return CGRectNull; } -- (pxSVGObject*)parseObject:(pxXMLNode*)node +- (pxSVGObject*)parseObject:(pxXMLNode*)node inheritAttributes:(pxSVGObject*)inherit { if ([node.tagName rangeOfString:@":"].location != NSNotFound) return nil; if ([node.tagName isEqualToString:@"metadata"]) return nil; @@ -94,14 +94,55 @@ - (pxSVGObject*)parseObject:(pxXMLNode*)node else NSLog(@"Unknown tag: %@",node.tagName); pxSVGObject *obj = [objClass new]; [obj loadAttributes:node.attributes]; + if (!obj.fillColor) + obj.fillColor = inherit.fillColor; + if (obj.strokeWidth == NAN) + obj.strokeWidth = inherit.strokeWidth; + if (!obj.strokeColor) + obj.strokeColor = inherit.strokeColor; if (node.childNodes.count) { NSMutableArray *subnodes = [NSMutableArray new]; for (pxXMLNode *n in node.childNodes) { - pxSVGObject *o = [self parseObject:n]; - if (o) [subnodes addObject:o]; + pxSVGObject *o = [self parseObject:n inheritAttributes:obj]; + if (o) { + [subnodes addObject:o]; + } } [obj setSubnodes:[NSArray arrayWithArray:subnodes]]; } return obj; } +- (CALayer *)makeLayerWithNode:(pxSVGObject*)node withOffset:(CGPoint)off +{ + CGRect f = [self objBounds:node]; + if (CGRectIsNull(f)) return nil; + CALayer *l; + if ([node respondsToSelector:@selector(d)]) { + CAShapeLayer *sl = [CAShapeLayer new]; + CGAffineTransform t = CGAffineTransformMakeTranslation(-f.origin.x, -f.origin.y); + CGPathRef p = CGPathCreateCopyByTransformingPath([(id)node d].CGPath, &t); + sl.path = p; + CGPathRelease(p); + sl.fillColor = (node.fillColor?:[UIColor blackColor]).CGColor; + sl.strokeColor = node.strokeColor.CGColor; + sl.lineWidth = node.strokeWidth==NAN?0:node.strokeWidth; + l = sl; + } else { + l = [CALayer new]; + } + l.frame = CGRectOffset(f, -off.x, -off.y); + l.transform = node.transform; + l.opacity = node.opacity; + if ([node respondsToSelector:@selector(subnodes)]) { + for (pxSVGObject *n in [(id)node subnodes]) { + CALayer *sl = [self makeLayerWithNode:n withOffset:(CGPoint){f.origin.x+off.x,f.origin.y+off.y}]; + if (sl) [l addSublayer:sl]; + } + } + return l; +} +- (CALayer *)makeLayer +{ + return [self makeLayerWithNode:self.root withOffset:CGPointZero]; +} @end diff --git a/pxSVG/pxSVGView.m b/pxSVG/pxSVGView.m index 9a3ca1d..8a47f6b 100644 --- a/pxSVG/pxSVGView.m +++ b/pxSVG/pxSVGView.m @@ -18,6 +18,7 @@ @implementation pxSVGView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; + self.contentMode = UIViewContentModeScaleAspectFit; pxSVGLayer *sl = [pxSVGLayer new]; [self.layer addSublayer:sl]; self.svgLayer=sl; @@ -33,6 +34,21 @@ - (void)layoutSublayersOfLayer:(CALayer *)layer - (void)svgLayerDidLoadImage:(pxSVGLayer *)svgLayer { + CATransform3D tr = CATransform3DIdentity; + CGRect c = svgLayer.contentRect; + switch (self.contentMode) { + case UIViewContentModeScaleAspectFit: { + CGFloat + scx = c.size.width/self.bounds.size.width, + scy = c.size.height/self.bounds.size.height, + sc = MAX(scx,scy); + tr = CATransform3DMakeScale(1/sc, 1/sc, 1); + tr = CATransform3DTranslate(tr, -c.origin.x/sc, -c.origin.y/sc, 0); + break; + } + default: break; + } + [svgLayer setTransform:tr]; if ([self.svgDelegate respondsToSelector:@selector(svgViewDidLoadImage:)]) [self.svgDelegate svgViewDidLoadImage:self]; }