Files
IJSVG/source/IJSVGParser.m
T
Curtis Hard 1840c4eaa1 Squashed commit of the following:
commit 912bdd5029e2afab7736b36b694fb77e9b82fe7d
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 14 12:00:34 2017 +0000

    Various formatting changes

commit 3de9a8a00df4391c1be592cdda63fcd7cc49511b
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 14 12:00:20 2017 +0000

    Updated to use the backing sclae helper

commit b06b74b2b5d22c1b7d4cfad789d97cc1319e6961
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 14 11:48:15 2017 +0000

    Formatting fixes

commit e3fbca0f3b44f6a5c282e1e43b711addefc9b3fc
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 14 00:20:40 2017 +0000

    Fixes for SVG sprite rendereing / exporting with correct color

commit 0cc40c6e923865dc93e7912fd5a61dba88b3647a
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 14 00:10:46 2017 +0000

    Unit swapping to much better object way

commit 7a3d41d071ab510704ea0803f93804794c330467
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Jan 13 14:58:11 2017 +0000

    Added more default values

commit 55b397b15deb0ffa39611f7ad2c840d9d1982adc
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Jan 12 23:23:15 2017 +0000

    Made constant strings for all parsed attributes

commit 3e0423ba0ed6b8c223c0684c9abf87ee9e8264c7
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Jan 12 21:47:00 2017 +0000

    More fixes to parsing attribuets

commit 2558f4e07feea1730df191f08cc110ecf1dbd963
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Jan 12 21:42:42 2017 +0000

    Various changes

commit 64a5d8362209ae1ae53bae3ff65c48dca2c08324
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Jan 12 16:36:45 2017 +0000

    Refactor of attribute parsing

commit 3b1274baa744aadb8b14a79f759dc11edd32f8fc
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 11 21:11:37 2017 +0000

    Fixes memory leaks

    - For the more interested, threading… wrap the dealloc method of any custom CALayers in a transaction lock or the sublayers get leaked… -_-

commit 059da0ce1965fa39b1ccd6b69f14aee296c67ecb
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 11 08:27:25 2017 +0000

    Changed backing scale implementation

commit 421a4afff25a0cb31aa2ff1574d4303911a65448
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Jan 10 22:09:51 2017 +0000

    Fixes for coordinate and percent workings

commit cdbae16d634a4f510ea700925bfa4ba225eac6f6
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Jan 10 21:57:25 2017 +0000

    Implemented a backingScaleHelper callback to help the sublayers that have custom content have the correct scale

commit d05bf062be5b8c5bc0577438d58e38f55f4c060e
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Jan 10 15:25:59 2017 +0000

    Updated the colour space

commit c96571a11bd3ae30c004ab53c36a853a01d1784d
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Jan 10 09:05:51 2017 +0000

    Fixes image not pulling through

commit 92f60485a0ecf48f26957daff0a528dac4601cc8
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Jan 10 08:36:26 2017 +0000

    Varous fixes and improvements to masking

commit abc7573a52789033095cd86680fac480e91272d9
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 9 23:27:21 2017 +0000

    Much better pattern parsing and usage

    - Actually almost conforms to the spec….almost…

commit 4635de5af72fd0a4e94f9278eb37fd7b0d8dd02a
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 9 18:25:38 2017 +0000

    Added ability to init an SVG via raw IJSVGLayers (obviously need to know how the structure works)

    - Also added ability to initialize with an image (creates the group on the fly)

commit 94ab4be7a350accad53d653bad8f7a892a4791ce
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 9 14:27:18 2017 +0000

    Fixes object bounding box on masking

commit 618aba584de37d316c16ace12be768936529cc4d
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 9 13:08:50 2017 +0000

    Added collpasing of groups that are siblings and are the same

commit 027617129a39606600e595444c3aee057849ca17
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 8 23:04:48 2017 +0000

    Formatting and refactor

commit b2cf4ef67249346635aa8bba91894d622c137df7
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 8 22:55:59 2017 +0000

    Relative path transformations

commit 8d7cca01b9289079a5f0c5881cbb2403c74728eb
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 8 19:40:48 2017 +0000

    Added collapsing of groups

commit 9b742e641983ea6f33f0c7abd5cc17e78478e7b0
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 8 18:41:53 2017 +0000

    More things!

commit 42b8d8eb0e96155717c606ebbf856c9c5ae790ae
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 8 17:16:32 2017 +0000

    Various unneeded stuff removed

commit b1fd970f81a491e4d153ecb0d9beb6394701d2cd
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 8 14:42:08 2017 +0000

    Added data method and extra options

commit 198f28a63cbe8cf40faf2261a2875c31ff59fcad
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 8 14:27:33 2017 +0000

    More export stuff, man im tired….

commit b352fb3fb7cb0b8512105cffc50d6f54632193a5
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Jan 7 22:53:20 2017 +0000

    Absolutely stupid insane task…but somehow worked…

commit d826387279cf4280d88f740b09ba9099b4dc2e64
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Jan 6 23:34:06 2017 +0000

    Initial steps for exporting a layer tree back to SVG

commit ffe296fa709cd73023a213cfd97d978cc642e85e
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 4 16:13:46 2017 +0000

    Multi thread issue fixes… seems to do the trick…

commit b7550f41b7fb6dd5738f43434089f0b98299300e
Author: Curtis Hard <curtishard@me.com>
Date:   Wed Jan 4 12:29:11 2017 +0000

    Various performance increases

commit f4bea2ff3c146b51ef5383a1ffa34d1690ec8e8f
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Jan 3 23:41:48 2017 +0000

    Added mask and clip-path to be parsed from styles

commit 159d6bbb846ada5f8684697b0ea38bbc98180a8d
Author: Curtis Hard <curtishard@me.com>
Date:   Tue Jan 3 22:59:43 2017 +0000

    Added fillColor and strokeColor to IJSVG

commit a298d8ef71bfa50ca01eb7b895abc91a9b525817
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 2 22:11:34 2017 +0000

    Check notes….

    - Added discardDOM to IJSVG, this allows you to use the cache more effectly but clear out the DOM behind it, what this will do is actually generate the layer tree and then release the DOM from memory, once its gone, its gone. Have to clear the cache object the alloc init another SVG object with same URL to regenerate.

    - Added setEvictItemsAfter method to cache, allows you to tell the cache how many items you want to be able to cache

commit c2fe30945c1d238b88d6145461a1d4bc037fbe26
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 2 21:37:50 2017 +0000

    Fixes bug...

commit 58aa9c5db76f6d2b72aa4bc051ed82d8d6ce98fb
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 2 21:22:27 2017 +0000

    Fixes to sub SVG sprite sheets not needing to be corrected

commit 05d010ad9622090a17932b6f089603c8c99199c7
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 2 21:11:25 2017 +0000

    Addes drawsAsynchronously

    - Check the header of IJSVG to see what it actually does

commit ff4229fbe27b5e431676a1e2516cfa1488de39a7
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 2 21:10:49 2017 +0000

    Fixes memory leak with sprite sheets

commit 87fabd6475c5cf4d0bde73ec5c7f64df11b16cd8
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 2 20:57:34 2017 +0000

    Fixes retain cycle

commit ddce67f456d973e5d785e6daef482c5f50563f06
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 2 19:42:48 2017 +0000

    Various threading issues sorted

commit 79d7ac935e8e788609572d6580dda70385dacb2e
Author: Curtis Hard <curtishard@me.com>
Date:   Mon Jan 2 17:42:54 2017 +0000

    Few fixes and added beginnings of what will be text

commit ccc24ca92f989273dc8474788f22148415359a8a
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 1 14:20:37 2017 +0000

    Few tweaks

commit 21930ed7116f96db6dc08d8d71d55bea2d817e3b
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 1 11:52:42 2017 +0000

    Turned off caching by default

commit c1449c7edfc8d95219092d76495d0591c8f60599
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 1 11:49:28 2017 +0000

    Better frame manipulation

commit da19869b99808eb9ef1f4e42417267abdfd120b4
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 1 11:28:08 2017 +0000

    Back to basics

commit d05176fdc1f4913c195dd32c0ef8beb18f64122f
Author: Curtis Hard <curtishard@me.com>
Date:   Sun Jan 1 11:12:40 2017 +0000

    Fixes issue with closing of paths

    Basically we dont want to close if the original bezier path parsed from the file didnt too!

commit 79622ef1bacbe382c848b1359044a126cea7ec08
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Dec 31 19:29:19 2016 +0000

    more fixes

commit 6a918f481b2257372c46eb0db81e50f8ddb220fa
Author: Curtis Hard <curtishard@me.com>
Date:   Sat Dec 31 18:14:38 2016 +0000

    Added skewX and skewY transforms

commit f9fb22d5cb072ce0b00df7c7748c3faf17ab3ab5
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Dec 30 23:55:34 2016 +0000

    fixes memory leak for paths

commit 5ddd8dca37d59a3bc31ce43a2c97c7cbffcd5670
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Dec 30 23:47:59 2016 +0000

    Sub SVG rendering

commit adcbb5f2ce9466a55711448c80a0963967c43e23
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Dec 30 23:25:48 2016 +0000

    Thread fixes

commit 92a71e31c6e450fa4b75f110601e8d7fb21bda2c
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Dec 30 22:17:54 2016 +0000

    More fixes to renderer

commit 44670b546218f07a343440fa5418444433149c08
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Dec 30 15:42:35 2016 +0000

    testing

commit 0b6de6278eff4d9906d016e8a88af31319410db3
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Dec 30 12:44:03 2016 +0000

    More fixes to rendering

commit 1273960ab247fd9219c2fb2aa24289b10fab1767
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Dec 30 09:01:02 2016 +0000

    Fixes gradient drawing issues within CALayers

commit 84e884e7a1e7a2ee2258f7bd9ca3d376b17edbd7
Author: Curtis Hard <curtishard@me.com>
Date:   Fri Dec 30 08:28:01 2016 +0000

    Fixes to image rendering

commit 782c936f1562c71f3c37fe5ffb9c5f835d3f61a4
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Dec 29 23:39:58 2016 +0000

    More things towards patterns

commit 58d3a1ca405d55c6ea78f6d914e96dbd3358404c
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Dec 29 16:44:38 2016 +0000

    More calayer rendering stuff

commit d3ff49d6cdcc73cb8b80c4a3cd3c852d8c8931d2
Author: Curtis Hard <curtishard@me.com>
Date:   Thu Dec 29 15:10:04 2016 +0000

    Intro to calayer based rendering
2017-01-15 12:02:21 +00:00

1296 lines
46 KiB
Objective-C

//
// IJSVGParser.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGParser.h"
#import "IJSVG.h"
@implementation IJSVGParser
@synthesize viewBox;
@synthesize proposedViewSize;
+ (IJSVGParser *)groupForFileURL:(NSURL *)aURL
{
return [[self class] groupForFileURL:aURL
error:nil
delegate:nil];
}
+ (IJSVGParser *)groupForFileURL:(NSURL *)aURL
delegate:(id<IJSVGParserDelegate>)delegate
{
return [[self class] groupForFileURL:aURL
error:nil
delegate:delegate];
}
+ (IJSVGParser *)groupForFileURL:(NSURL *)aURL
error:(NSError **)error
delegate:(id<IJSVGParserDelegate>)delegate
{
return [[[[self class] alloc] initWithFileURL:aURL
error:error
delegate:delegate] autorelease];
}
- (void)dealloc
{
[_glyphs release], _glyphs = nil;
[_styleSheet release], _styleSheet = nil;
[_parsedNodes release], _parsedNodes = nil;
[_defNodes release], _defNodes = nil;
[_svgs release], _svgs = nil;
[super dealloc];
}
- (id)initWithSVGString:(NSString *)string
error:(NSError **)error
delegate:(id<IJSVGParserDelegate>)delegate
{
if( ( self = [super init] ) != nil )
{
_delegate = delegate;
_respondsTo.handleForeignObject = [_delegate respondsToSelector:@selector(svgParser:handleForeignObject:document:)];
_respondsTo.shouldHandleForeignObject = [_delegate respondsToSelector:@selector(svgParser:shouldHandleForeignObject:)];
_respondsTo.handleSubSVG = [_delegate respondsToSelector:@selector(svgParser:foundSubSVG:withSVGString:)];
_glyphs = [[NSMutableArray alloc] init];
_parsedNodes = [[NSMutableArray alloc] init];
_defNodes = [[NSMutableDictionary alloc] init];
_svgs = [[NSMutableArray alloc] init];
// load the document / file, assume its UTF8
// use NSXMLDocument as its the easiest thing to do on OSX
NSError * anError = nil;
@try {
_document = [[NSXMLDocument alloc] initWithXMLString:string
options:0
error:&anError];
}
@catch (NSException *exception) {
}
// error parsing the XML document
if( anError != nil ) {
return [self _handleErrorWithCode:IJSVGErrorParsingFile
error:error];
}
// attempt to parse the file
anError = nil;
@try {
[self _parse];
}
@catch (NSException *exception) {
return [self _handleErrorWithCode:IJSVGErrorParsingSVG
error:error];
}
// check the actual parsed SVG
anError = nil;
if( ![self _validateParse:&anError] ) {
*error = anError;
[_document release], _document = nil;
[self release], self = nil;
return nil;
}
// we have actually finished with the document at this point
// so just get rid of it
[_document release], _document = nil;
}
return self;
}
- (id)initWithFileURL:(NSURL *)aURL
error:(NSError **)error
delegate:(id<IJSVGParserDelegate>)delegate
{
NSError * anError = nil;
NSStringEncoding encoding;
NSString * str = [NSString stringWithContentsOfFile:aURL.path
usedEncoding:&encoding
error:&anError];
// error reading file
if(str == nil) {
return [self _handleErrorWithCode:IJSVGErrorReadingFile
error:error];
}
return [self initWithSVGString:str
error:error
delegate:delegate];
}
- (void *)_handleErrorWithCode:(NSUInteger)code
error:(NSError **)error
{
if( error )
*error = [[[NSError alloc] initWithDomain:IJSVGErrorDomain
code:code
userInfo:nil] autorelease];
[_document release], _document = nil;
[self release], self = nil;
return nil;
}
- (BOOL)_validateParse:(NSError **)error
{
// check is font
if( self.isFont )
return YES;
// check the viewbox
if( NSEqualRects( self.viewBox, NSZeroRect ) ||
self.size.width == 0 || self.size.height == 0 )
{
if( error != NULL )
*error = [[[NSError alloc] initWithDomain:IJSVGErrorDomain
code:IJSVGErrorInvalidViewBox
userInfo:nil] autorelease];
return NO;
}
return YES;
}
- (NSSize)size
{
return viewBox.size;
}
- (void)_parse
{
NSXMLElement * svgElement = [_document rootElement];
// parse common attributes on the SVG element
[self _parseElementForCommonAttributes:svgElement
node:self];
// find the sizebox!
NSXMLNode * attribute = nil;
if( ( attribute = [svgElement attributeForName:(NSString *)IJSVGAttributeViewBox] ) != nil )
{
// we have a viewbox...
CGFloat * box = [IJSVGUtils parseViewBox:[attribute stringValue]];
viewBox = NSMakeRect( box[0], box[1], box[2], box[3]);
free(box);
} else {
// there is no view box so find the width and height
CGFloat w = [[[svgElement attributeForName:(NSString *)IJSVGAttributeWidth] stringValue] floatValue];
CGFloat h = [[[svgElement attributeForName:(NSString *)IJSVGAttributeHeight] stringValue] floatValue];
if( h == 0.f && w != 0.f )
h = w;
else if( w == 0.f && h != 0.f )
w = h;
viewBox = NSMakeRect( 0.f, 0.f, w, h );
}
// find any stylesheets
NSArray * styles = [svgElement nodesForXPath:@"//style"
error:nil];
if(styles.count != 0)
{
_styleSheet = [[IJSVGStyleSheet alloc] init];
for(NSXMLElement * styleElement in styles)
{
[_styleSheet parseStyleBlock:[styleElement stringValue]];
}
}
// parse the width and height....
CGFloat w = [[[svgElement attributeForName:(NSString *)IJSVGAttributeWidth] stringValue] floatValue];
CGFloat h = [[[svgElement attributeForName:(NSString *)IJSVGAttributeHeight] stringValue] floatValue];
if( w == 0.f && h == 0.f )
{
w = viewBox.size.width;
h = viewBox.size.height;
} else if( w == 0 && h != 0.f ) {
w = viewBox.size.width;
} else if( h == 0 && w != 0.f ) {
h = viewBox.size.height;
}
proposedViewSize = NSMakeSize( w, h );
// find foreign objects...
NSXMLElement * switchElement = nil;
NSArray * switchElements = [svgElement nodesForXPath:@"switch"
error:nil];
if( [switchElements count] != 0 )
{
// for performance reasons, ask for this once!
BOOL handlesShouldHandle = _respondsTo.shouldHandleForeignObject == 1;
BOOL handlesHandle = _respondsTo.handleForeignObject == 1;
// we have a switch, work out what the objects are...
switchElement = switchElements[0];
NSXMLElement * child = nil;
if( _delegate != nil ) {
for( child in [switchElement children] ) {
if( [[child name] isEqualToString:@"foreignObject"] )
{
// create the temp foreign object
IJSVGForeignObject * foreignObject = [[[IJSVGForeignObject alloc] init] autorelease];
// grab the common attributes
[self _parseElementForCommonAttributes:child
node:foreignObject];
foreignObject.requiredExtension = [[child attributeForName:@"requiredExtensions"] stringValue];
// ask the delegate
if( handlesShouldHandle && [_delegate svgParser:self
shouldHandleForeignObject:foreignObject] && handlesHandle )
{
[_delegate svgParser:self
handleForeignObject:foreignObject
document:_document];
break;
}
}
}
}
// set the main element to the switch
svgElement = switchElement;
}
// the root element is SVG, so iterate over its children
// recursively
// are there any defaults?
NSArray * defaults = [svgElement nodesForXPath:@"//defs"
error:nil];
if(defaults.count != 0) {
// we have default, we need to store these per ID and remove them from the array
for(NSXMLElement * defs in defaults) {
// store each object
for(NSXMLElement * childDef in defs.children) {
NSString * defID = [[childDef attributeForName:@"id"] stringValue];
if(defID != nil) {
_defNodes[defID] = childDef;
}
}
}
}
self.name = svgElement.name;
[self _parseBlock:svgElement
intoGroup:self
def:NO];
// now everything has been done we need to compute the style tree
for(NSDictionary * dict in _parsedNodes) {
[self _postParseElementForCommonAttributes:dict[@"element"]
node:dict[@"node"]];
}
// dont need the style sheet or the parsed nodes as this point
[_styleSheet release], _styleSheet = nil;
[_parsedNodes release], _parsedNodes = nil;
[_defNodes release], _defNodes = nil;
}
- (void)_postParseElementForCommonAttributes:(NSXMLElement *)element
node:(IJSVGNode *)node
{
// first of all, compute a style sheet
IJSVGStyle * sheetStyle = nil;
__block IJSVGStyle * style = nil;
// attribute helpers
typedef void (^cp)(NSString *);
void (^attr)(const NSString *, cp) = ^(NSString * key, cp block) {
NSString * v = [element attributeForName:key].stringValue
?: [style property:key];
if(v != nil && v.length != 0) {
block(v);
}
};
typedef id (^cap)(NSString *);
void (^atts)(NSDictionary<NSString *, NSString *> *, cap) =
^(NSDictionary<NSString *, NSString *>* kv, cap block) {
for(NSString * key in kv.allKeys) {
attr(key, ^(NSString * value) {
[node setValue:block(value)
forKey:kv[key]];
});
}
};
// id, this must be here for the style sheet to actually
// render and parse, basically it relies on ID/CSS selectors so these
// must be in place before its computed
attr(IJSVGAttributeID, ^(NSString * value) {
node.identifier = value;
_defNodes[node.identifier] = element;
});
//
attr(IJSVGAttributeClass, ^(NSString * value) {
node.className = value;
node.classNameList = [value componentsSeparatedByString:@" "];
});
// work out the style sheet
if(_styleSheet != nil) {
sheetStyle = [_styleSheet styleForNode:node];
}
// is there a
attr(IJSVGAttributeStyle, ^(NSString * value) {
style = [IJSVGStyle parseStyleString:value];
});
// merge to two together
if(sheetStyle != nil) {
style = [sheetStyle mergedStyle:style];
}
// floats
atts(@{IJSVGAttributeX:@"x",
IJSVGAttributeY:@"y",
IJSVGAttributeWidth:@"width",
IJSVGAttributeHeight:@"height",
IJSVGAttributeOpacity:@"opacity",
IJSVGAttributeStrokeOpacity:@"strokeOpacity",
IJSVGAttributeStrokeWidth:@"strokeWidth",
IJSVGAttributeStrokeDashArray:@"strokeDashOffset",
IJSVGAttributeFillOpacity:@"fillOpacity"}, ^id (NSString * value) {
return [IJSVGUnitLength unitWithString:value];
});
// nodes
atts(@{IJSVGAttributeClipPath:@"clipPath",
IJSVGAttributeMask:@"mask"}, ^id (NSString * value) {
NSString * url = [IJSVGUtils defURL:value];
if(url != nil) {
return [self definedObjectForID:url
node:node
fromGroup:nil];
}
return nil;
});
// units
atts(@{IJSVGAttributeGradientUnits:@"units",
IJSVGAttributeMaskUnits:@"units",
IJSVGAttributeMaskContentUnits:@"contentUnits"}, ^id (NSString * value) {
return @([IJSVGUtils unitTypeForString:value]);
});
// transforms
atts(@{IJSVGAttributeTransform:@"transforms",
IJSVGAttributeGradientTransform:@"transforms"}, ^(NSString * value) {
NSMutableArray * tempTransforms = [[[NSMutableArray alloc] init] autorelease];
[tempTransforms addObjectsFromArray:[IJSVGTransform transformsForString:value]];
if(node.transforms != nil) {
[tempTransforms addObjectsFromArray:node.transforms];
}
return tempTransforms;
});
#pragma mark attributes that require custom rules
// unicode
attr(IJSVGAttributeUnicode, ^(NSString * value) {
node.unicode = [NSString stringWithFormat:@"%04x",[value characterAtIndex:0]];
});
// linecap
attr(IJSVGAttributeStrokeLineCap, ^(NSString * value) {
node.lineCapStyle = [IJSVGUtils lineCapStyleForString:value];
});
// line join
attr(IJSVGAttributeLineJoin, ^(NSString * value) {
node.lineJoinStyle = [IJSVGUtils lineJoinStyleForString:value];
});
// stroke color
attr(IJSVGAttributeStroke, ^(NSString * value) {
node.strokeColor = [IJSVGColor colorFromString:value];
});
// stroke dash array
attr(IJSVGAttributeStrokeDashArray, ^(NSString * value) {
NSInteger paramCount = 0;
CGFloat * params = [IJSVGUtils commandParameters:value
count:&paramCount];
node.strokeDashArray = params;
node.strokeDashArrayCount = paramCount;
});
// fill - seems kinda complicated for what it actually is
attr(IJSVGAttributeFill, ^(NSString * value) {
NSString * fillDefID = [IJSVGUtils defURL:value];
if(fillDefID != nil) {
// find the object
id obj = [self definedObjectForID:fillDefID
node:node
fromGroup:nil];
// what type is it?
if([obj isKindOfClass:[IJSVGGradient class]]) {
node.fillGradient = (IJSVGGradient *)obj;
} else if([obj isKindOfClass:[IJSVGPattern class]]) {
node.fillPattern = (IJSVGPattern *)obj;
}
} else {
// its a color
node.fillColor = [IJSVGColor colorFromString:value];
if(node.fillOpacity.value != 1.f) {
node.fillColor = [IJSVGColor changeAlphaOnColor:node.fillColor
to:node.fillOpacity.value];
}
}
});
// fill rule
attr(IJSVGAttributeFillRule, ^(NSString * value) {
node.windingRule = [IJSVGUtils windingRuleForString:value];
});
// display
attr(IJSVGAttributeDisplay, ^(NSString * value) {
if([value.lowercaseString isEqualToString:@"none"]) {
node.shouldRender = NO;
}
});
}
- (id)definedObjectForID:(NSString *)anID
node:(IJSVGNode *)node
fromGroup:(IJSVGGroup *)group
{
NSXMLElement * parseElement = _defNodes[anID];
if(parseElement != nil) {
// parse the element
if(group == nil) {
group = [[[IJSVGGroup alloc] init] autorelease];
}
// parse the block
[self _parseBaseBlock:parseElement
intoGroup:group
def:NO
node:node];
return [group defForID:anID];
}
return nil;
}
- (BOOL)isFont
{
return [_glyphs count] != 0;
}
- (NSArray *)glyphs
{
return _glyphs;
}
- (void)addSubSVG:(IJSVG *)anSVG
{
[_svgs addObject:anSVG];
}
- (NSArray<IJSVG *> *)subSVGs:(BOOL)recursive
{
if(recursive == NO) {
return _svgs;
}
NSMutableArray * svgs = [[[NSMutableArray alloc] init] autorelease];
for(IJSVG * anSVG in svgs) {
[svgs addObject:anSVG];
[svgs addObjectsFromArray:[anSVG subSVGs:recursive]];
}
return svgs;
}
- (void)addGlyph:(IJSVGNode *)glyph
{
[_glyphs addObject:glyph];
}
- (void)_parseElementForCommonAttributes:(NSXMLElement *)element
node:(IJSVGNode *)node
{
[self _postParseElementForCommonAttributes:element
node:node];
}
- (void)_setupDefaultsForNode:(IJSVGNode *)node
{
switch(node.type) {
// mask
case IJSVGNodeTypeMask :{
node.units = IJSVGUnitObjectBoundingBox;
break;
}
// gradient
case IJSVGNodeTypeRadialGradient:
case IJSVGNodeTypeLinearGradient: {
node.units = IJSVGUnitObjectBoundingBox;
break;
}
default: {
}
}
}
- (void)_parseBaseBlock:(NSXMLElement *)element
intoGroup:(IJSVGGroup *)parentGroup
def:(BOOL)flag
node:(IJSVGNode *)currentNode
{
NSString * subName = element.name;
NSXMLNodeKind nodeKind = element.kind;
IJSVGNodeType aType = [IJSVGNode typeForString:subName
kind:nodeKind];
switch( aType ) {
default:
case IJSVGNodeTypeDef:
case IJSVGNodeTypeNotFound:
break;
// sub SVG
case IJSVGNodeTypeSVG: {
IJSVGPath * path = [[[IJSVGPath alloc] init] autorelease];
path.type = aType;
path.name = subName;
path.parentNode = parentGroup;
// grab common attributes
[self _setupDefaultsForNode:path];
[self _parseElementForCommonAttributes:element
node:path];
// work out the SVG
NSError * error = nil;
NSString * SVGString = element.XMLString;
IJSVG * anSVG = [[[IJSVG alloc] initWithSVGString:SVGString
error:&error
delegate:nil] autorelease];
// handle sub SVG
if(error == nil && _respondsTo.handleSubSVG == 1) {
[_delegate svgParser:self
foundSubSVG:anSVG
withSVGString:SVGString];
}
// any error?
if(anSVG != nil && error == nil) {
path.svg = anSVG;
[parentGroup addChild:path];
[parentGroup addDef:path];
// make sure we add this
[self addSubSVG:anSVG];
}
break;
}
// glyph
case IJSVGNodeTypeGlyph: {
// no path data
if( [element attributeForName:(NSString *)IJSVGAttributeD] == nil ||
[[element attributeForName:(NSString *)IJSVGAttributeD] stringValue].length == 0 ) {
break;
}
IJSVGPath * path = [[[IJSVGPath alloc] init] autorelease];
path.type = aType;
path.name = subName;
path.parentNode = parentGroup;
// find common attributes
[self _setupDefaultsForNode:path];
[self _parseElementForCommonAttributes:element
node:path];
// pass the commands for it
[self _parsePathCommandData:[[element attributeForName:(NSString *)IJSVGAttributeD] stringValue]
intoPath:path];
// check the size...
if( NSIsEmptyRect([path path].controlPointBounds) ) {
break;
}
// add the glyph
[self addGlyph:path];
break;
}
// group
case IJSVGNodeTypeFont:
case IJSVGNodeTypeMask:
case IJSVGNodeTypeGroup: {
// create a new group
IJSVGGroup * group = [[[IJSVGGroup alloc] init] autorelease];
group.type = aType;
group.name = subName;
group.parentNode = parentGroup;
// only groups get added to parent, rest is added as a def
if(!flag && aType == IJSVGNodeTypeGroup) {
[parentGroup addChild:group];
}
// find common attributes
[self _setupDefaultsForNode:group];
[self _parseElementForCommonAttributes:element
node:group];
// recursively parse blocks
[self _parseBlock:element
intoGroup:group
def:NO];
[parentGroup addDef:group];
break;
}
// path
case IJSVGNodeTypePath: {
IJSVGPath * path = [[[IJSVGPath alloc] init] autorelease];
path.type = aType;
path.name = subName;
path.parentNode = parentGroup;
if( !flag ) {
[parentGroup addChild:path];
}
// find common attributes
[self _setupDefaultsForNode:path];
[self _parseElementForCommonAttributes:element
node:path];
[self _parsePathCommandData:[[element attributeForName:(NSString *)IJSVGAttributeD] stringValue]
intoPath:path];
[parentGroup addDef:path];
break;
}
// polygon
case IJSVGNodeTypePolygon: {
IJSVGPath * path = [[[IJSVGPath alloc] init] autorelease];
path.type = aType;
path.name = subName;
path.parentNode = parentGroup;
if( !flag ) {
[parentGroup addChild:path];
}
// find common attributes
[self _setupDefaultsForNode:path];
[self _parseElementForCommonAttributes:element
node:path];
[self _parsePolygon:element
intoPath:path];
[parentGroup addDef:path];
break;
}
// polyline
case IJSVGNodeTypePolyline: {
IJSVGPath * path = [[[IJSVGPath alloc] init] autorelease];
path.type = aType;
path.name = subName;
path.parentNode = parentGroup;
if( !flag ) {
[parentGroup addChild:path];
}
// find common attributes
[self _setupDefaultsForNode:path];
[self _parseElementForCommonAttributes:element
node:path];
[self _parsePolyline:element
intoPath:path];
[parentGroup addDef:path];
break;
}
// rect
case IJSVGNodeTypeRect: {
IJSVGPath * path = [[[IJSVGPath alloc] init] autorelease];
path.type = aType;
path.name = subName;
path.parentNode = parentGroup;
if( !flag ) {
[parentGroup addChild:path];
}
// find common attributes
[self _parseRect:element
intoPath:path];
[self _setupDefaultsForNode:path];
[self _parseElementForCommonAttributes:element
node:path];
[parentGroup addDef:path];
break;
}
// line
case IJSVGNodeTypeLine: {
IJSVGPath * path = [[[IJSVGPath alloc] init] autorelease];
path.type = aType;
path.name = subName;
path.parentNode = parentGroup;
[parentGroup addChild:path];
// find common attributes
[self _setupDefaultsForNode:path];
[self _parseElementForCommonAttributes:element
node:path];
[self _parseLine:element
intoPath:path];
[parentGroup addDef:path];
break;
}
// circle
case IJSVGNodeTypeCircle: {
IJSVGPath * path = [[[IJSVGPath alloc] init] autorelease];
path.type = aType;
path.name = subName;
path.parentNode = parentGroup;
if( !flag ) {
[parentGroup addChild:path];
}
// find common attributes
[self _setupDefaultsForNode:path];
[self _parseElementForCommonAttributes:element
node:path];
[self _parseCircle:element
intoPath:path];
[parentGroup addDef:path];
break;
}
// ellipse
case IJSVGNodeTypeEllipse: {
IJSVGPath * path = [[[IJSVGPath alloc] init] autorelease];
path.type = aType;
path.name = subName;
path.parentNode = parentGroup;
if( !flag ) {
[parentGroup addChild:path];
}
// find common attributes
[self _setupDefaultsForNode:path];
[self _parseElementForCommonAttributes:element
node:path];
[self _parseEllipse:element
intoPath:path];
[parentGroup addDef:path];
break;
}
// use
case IJSVGNodeTypeUse: {
NSString * xlink = [[element attributeForName:(NSString *)IJSVGAttributeXLink] stringValue];
NSString * xlinkID = [xlink substringFromIndex:1];
IJSVGNode * node = [self definedObjectForID:xlinkID
node:nil
fromGroup:parentGroup];
node.parentNode = parentGroup;
if(!flag) {
[parentGroup addChild:node];
}
[self _parseElementForCommonAttributes:element
node:node];
[parentGroup addDef:node];
break;
}
// linear gradient
case IJSVGNodeTypeLinearGradient: {
NSString * xlink = [[element attributeForName:(NSString *)IJSVGAttributeXLink] 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];
CGPoint startPoint, endPoint;
[IJSVGLinearGradient parseGradient:element
gradient:grad
startPoint:&startPoint
endPoint:&endPoint];
[self _setupDefaultsForNode:grad];
[self _parseElementForCommonAttributes:element
node:grad];
grad.startPoint = startPoint;
grad.endPoint = endPoint;
[parentGroup addDef:grad];
break;
}
IJSVGLinearGradient * gradient = [[[IJSVGLinearGradient alloc] init] autorelease];
gradient.type = aType;
CGPoint startPoint, endPoint;
gradient.gradient = [IJSVGLinearGradient parseGradient:element
gradient:gradient
startPoint:&startPoint
endPoint:&endPoint];
[self _setupDefaultsForNode:gradient];
[self _parseElementForCommonAttributes:element
node:gradient];
gradient.startPoint = startPoint;
gradient.endPoint = endPoint;
[parentGroup addDef:gradient];
break;
}
// radial gradient
case IJSVGNodeTypeRadialGradient: {
NSString * xlink = [[element attributeForName:(NSString *)IJSVGAttributeXLink] 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];
CGPoint startPoint, endPoint;
[IJSVGRadialGradient parseGradient:element
gradient:grad
startPoint:&startPoint
endPoint:&endPoint];
[self _setupDefaultsForNode:grad];
[self _parseElementForCommonAttributes:element
node:grad];
grad.startPoint = startPoint;
grad.endPoint = endPoint;
[parentGroup addDef:grad];
break;
}
IJSVGRadialGradient * gradient = [[[IJSVGRadialGradient alloc] init] autorelease];
gradient.type = aType;
CGPoint startPoint, endPoint;
gradient.gradient = [IJSVGRadialGradient parseGradient:element
gradient:gradient
startPoint:&startPoint
endPoint:&endPoint];
gradient.startPoint = startPoint;
gradient.endPoint = endPoint;
[self _setupDefaultsForNode:gradient];
[self _parseElementForCommonAttributes:element
node:gradient];
[parentGroup addDef:gradient];
break;
}
// clippath
case IJSVGNodeTypeClipPath: {
IJSVGGroup * group = [[[IJSVGGroup alloc] init] autorelease];
group.type = aType;
group.name = subName;
group.parentNode = parentGroup;
[self _setupDefaultsForNode:group];
// find common attributes
[self _parseElementForCommonAttributes:element
node:group];
// recursively parse blocks
[self _parseBlock:element
intoGroup:group
def:NO];
[parentGroup addDef:group];
break;
}
// pattern
case IJSVGNodeTypePattern: {
IJSVGPattern * pattern = [[[IJSVGPattern alloc] init] autorelease];
[self _setupDefaultsForNode:pattern];
// find common attributes
[self _parseElementForCommonAttributes:element
node:pattern];
// pattern has children
[self _parseBlock:element
intoGroup:pattern
def:NO];
[parentGroup addDef:pattern];
break;
}
// image
case IJSVGNodeTypeImage: {
IJSVGImage * image = [[[IJSVGImage alloc] init] autorelease];
[self _setupDefaultsForNode:image];
// find common attributes
[self _parseElementForCommonAttributes:element
node:image];
// from base64
[image loadFromBase64EncodedString:[[element attributeForName:(NSString *)IJSVGAttributeXLink] stringValue]];
// add to parent
[parentGroup addChild:image];
[parentGroup addDef:image];
break;
}
}
}
- (void)_parseBlock:(NSXMLElement *)anElement
intoGroup:(IJSVGGroup*)parentGroup
def:(BOOL)flag
{
for( NSXMLElement * element in [anElement children] ) {
[self _parseBaseBlock:element
intoGroup:parentGroup
def:flag
node:nil];
}
}
static NSCharacterSet * _commandCharSet = nil;
+ (NSCharacterSet *)_commandCharSet
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_commandCharSet = [[NSCharacterSet characterSetWithCharactersInString:@"MmZzLlHhVvCcSsQqTtAa"] retain];
});
return _commandCharSet;
}
#pragma mark Parser stuff!
- (void)_parsePathCommandData:(NSString *)command
intoPath:(IJSVGPath *)path
{
// invalid command
if( command == nil || command.length == 0 ) {
return;
}
NSCharacterSet * set = [[self class] _commandCharSet];
NSUInteger len = [command length];
// allocate memory for the string buffer for reading
unichar * buffer = (unichar *)calloc( len+1, sizeof(unichar));
[command getCharacters:buffer
range:NSMakeRange(0, len)];
int defaultBufferSize = 200;
int currentBufferSize = 0;
int currentSize = defaultBufferSize;
unichar * commandBuffer = NULL;
if( len != 0 ) {
commandBuffer = (unichar *)calloc(defaultBufferSize,sizeof(unichar));
}
IJSVGCommand * _currentCommand = nil;
for( int i = 0; i < len; i++ ) {
unichar currentChar = buffer[i];
unichar nextChar = buffer[i+1];
BOOL atEnd = i == len-1;
BOOL isStartCommand = [set characterIsMember:nextChar];
if( ( currentBufferSize + 1 ) == currentSize ) {
currentSize += defaultBufferSize;
commandBuffer = (unichar *)realloc( commandBuffer, sizeof(unichar)*currentSize);
}
commandBuffer[currentBufferSize++] = currentChar;
if( isStartCommand || atEnd ) {
NSString * commandString = [NSString stringWithCharacters:commandBuffer
length:currentBufferSize];
// previous command is actual subcommand
IJSVGCommand * previousCommand = [_currentCommand subCommands].lastObject;
IJSVGCommand * cCommand = [self _parseCommandString:commandString
previousCommand:previousCommand
intoPath:path];
// retain the current one
if(cCommand != nil) {
_currentCommand = cCommand;
}
free(commandBuffer);
commandBuffer = NULL;
if( !atEnd ) {
currentBufferSize = 0;
currentSize = defaultBufferSize;
commandBuffer = (unichar *)calloc(defaultBufferSize,sizeof(unichar));
}
}
}
// free the buffer
free(buffer);
}
- (IJSVGCommand *)_parseCommandString:(NSString *)string
previousCommand:(IJSVGCommand *)previousCommand
intoPath:(IJSVGPath *)path
{
// work out the last command - the reason this is so long is because the command
// could be a series of the same commands, so work it out by the number of parameters
// there is per command string
IJSVGCommand * preCommand = nil;
if( previousCommand ) {
preCommand = previousCommand;
}
// main commands
IJSVGCommand * command = [[[IJSVGCommand alloc] initWithCommandString:string] autorelease];
for( IJSVGCommand * subCommand in [command subCommands] ) {
[subCommand.commandClass runWithParams:subCommand.parameters
paramCount:subCommand.parameterCount
command:subCommand
previousCommand:preCommand
type:subCommand.type
path:path];
preCommand = subCommand;
}
return command;
}
- (void)_parseLine:(NSXMLElement *)element
intoPath:(IJSVGPath *)path
{
// convert a line into a command,
// basically MX1 Y1LX2 Y2
CGFloat x1 = [[[element attributeForName:(NSString *)IJSVGAttributeX1] stringValue] floatValue];
CGFloat y1 = [[[element attributeForName:(NSString *)IJSVGAttributeY1] stringValue] floatValue];
CGFloat x2 = [[[element attributeForName:(NSString *)IJSVGAttributeX2] stringValue] floatValue];
CGFloat y2 = [[[element attributeForName:(NSString *)IJSVGAttributeY2] stringValue] floatValue];
// use sprintf as its quicker then stringWithFormat...
char buffer[50];
sprintf( buffer, "M%.2f %.2fL%.2f %.2f",x1,y1,x2,y2);
NSString * command = [NSString stringWithCString:buffer
encoding:NSUTF8StringEncoding];
[self _parsePathCommandData:command
intoPath:path];
}
- (void)_parseCircle:(NSXMLElement *)element
intoPath:(IJSVGPath *)path
{
CGFloat cX = [[[element attributeForName:(NSString *)IJSVGAttributeCX] stringValue] floatValue];
CGFloat cY = [[[element attributeForName:(NSString *)IJSVGAttributeCY] stringValue] floatValue];
CGFloat r = [[[element attributeForName:(NSString *)IJSVGAttributeR] stringValue] floatValue];
NSRect rect = NSMakeRect( cX - r, cY - r, r*2, r*2);
[path overwritePath:[NSBezierPath bezierPathWithOvalInRect:rect]];
}
- (void)_parseEllipse:(NSXMLElement *)element
intoPath:(IJSVGPath *)path
{
CGFloat cX = [[[element attributeForName:(NSString *)IJSVGAttributeCX] stringValue] floatValue];
CGFloat cY = [[[element attributeForName:(NSString *)IJSVGAttributeCY] stringValue] floatValue];
CGFloat rX = [[[element attributeForName:(NSString *)IJSVGAttributeRX] stringValue] floatValue];
CGFloat rY = [[[element attributeForName:(NSString *)IJSVGAttributeRY] stringValue] floatValue];
NSRect rect = NSMakeRect( cX-rX, cY-rY, rX*2, rY*2);
[path overwritePath:[NSBezierPath bezierPathWithOvalInRect:rect]];
}
- (void)_parsePolyline:(NSXMLElement *)element
intoPath:(IJSVGPath *)path
{
[self _parsePoly:element
intoPath:path
closePath:NO];
}
- (void)_parsePolygon:(NSXMLElement *)element
intoPath:(IJSVGPath *)path
{
[self _parsePoly:element
intoPath:path
closePath:YES];
}
- (void)_parsePoly:(NSXMLElement *)element
intoPath:(IJSVGPath *)path
closePath:(BOOL)closePath
{
NSString * points = [[element attributeForName:(NSString *)IJSVGAttributePoints] stringValue];
NSInteger count = 0;
CGFloat * params = [IJSVGUtils commandParameters:points
count:&count];
if( (count % 2) != 0 )
{
// error occured, free the params
free(params);
return;
}
// construct a command
NSMutableString * str = [[[NSMutableString alloc] init] autorelease];
[str appendFormat:@"M%f,%f L",params[0],params[1]];
for( NSInteger i = 2; i < count; i+=2 )
{
[str appendFormat:@"%f,%f ",params[i],params[i+1]];
}
if( closePath )
[str appendString:@"z"];
[self _parsePathCommandData:str
intoPath:path];
free(params);
}
- (void)_parseRect:(NSXMLElement *)element
intoPath:(IJSVGPath *)path
{
CGFloat aX, aY, aWidth, aHeight;
if([self namespacedAttribute:(NSString *)IJSVGAttributeX
element:element] != nil) {
// already namespaced, find them
aX = [[self namespacedAttribute:(NSString *)IJSVGAttributeX
element:element] floatValue];
aY = [[self namespacedAttribute:(NSString *)IJSVGAttributeY
element:element] floatValue];
aWidth = [IJSVGUtils floatValue:[self namespacedAttribute:(NSString *)IJSVGAttributeWidth
element:element]
fallBackForPercent:self.viewBox.size.width];
aHeight = [IJSVGUtils floatValue:[self namespacedAttribute:(NSString *)IJSVGAttributeHeight
element:element]
fallBackForPercent:self.viewBox.size.height];
} else {
// reassign X
[self applyNamespacedAttribute:(NSString *)IJSVGAttributeX
value:[[element attributeForName:(NSString *)IJSVGAttributeX] stringValue]
element:element];
aX = [[[element attributeForName:(NSString *)IJSVGAttributeX] stringValue] floatValue];
// reassign Y
[self applyNamespacedAttribute:(NSString *)IJSVGAttributeY
value:[[element attributeForName:(NSString *)IJSVGAttributeY] stringValue]
element:element];
aY = [[[element attributeForName:(NSString *)IJSVGAttributeY] stringValue] floatValue];
// reassign width
[self applyNamespacedAttribute:(NSString *)IJSVGAttributeWidth
value:[[element attributeForName:(NSString *)IJSVGAttributeWidth] stringValue]
element:element];
aWidth = [IJSVGUtils floatValue:[[element attributeForName:(NSString *)IJSVGAttributeWidth] stringValue]
fallBackForPercent:self.viewBox.size.width];
// reassign height
[self applyNamespacedAttribute:(NSString *)IJSVGAttributeHeight
value:[[element attributeForName:(NSString *)IJSVGAttributeHeight] stringValue]
element:element];
aHeight = [IJSVGUtils floatValue:[[element attributeForName:(NSString *)IJSVGAttributeHeight] stringValue]
fallBackForPercent:self.viewBox.size.height];
// set the namespaced versions as we need to remove the attributes
[element removeAttributeForName:(NSString *)IJSVGAttributeX];
[element removeAttributeForName:(NSString *)IJSVGAttributeY];
[element removeAttributeForName:(NSString *)IJSVGAttributeWidth];
[element removeAttributeForName:(NSString *)IJSVGAttributeHeight];
}
CGFloat rX = [[[element attributeForName:(NSString *)IJSVGAttributeRX] stringValue] floatValue];
CGFloat rY = [[[element attributeForName:(NSString *)IJSVGAttributeRY] stringValue] floatValue];
if( [element attributeForName:(NSString *)IJSVGAttributeRY] == nil ) {
rY = rX;
}
[path overwritePath:[NSBezierPath bezierPathWithRoundedRect:NSMakeRect( aX, aY, aWidth, aHeight)
xRadius:rX
yRadius:rY]];
}
- (NSString *)namespacedAttribute:(NSString *)key
element:(NSXMLElement *)element
{
key = [NSString stringWithFormat:@"ij-svg:%@",key];
if([element attributeForName:key] != nil) {
return [[element attributeForName:key] stringValue];
}
return nil;
}
- (void)applyNamespacedAttribute:(NSString *)key
value:(NSString *)value
element:(NSXMLElement *)element
{
key = [NSString stringWithFormat:@"ij-svg:%@",key];
NSXMLNode * node = [[[NSXMLNode alloc] initWithKind:NSXMLAttributeKind] autorelease];
node.name = key;
node.stringValue= value;
[element addAttribute:node];
}
@end