Created
October 2, 2013 12:11
-
-
Save paul-delange/6792749 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
@class SVGParser; | |
FOUNDATION_EXPORT NSString * const kSVGParserErrorDomain; | |
@protocol SVGParserDelegate <NSObject> | |
@optional | |
- (void) svgParser: (SVGParser*) parser scannedAttributes: (NSDictionary*) attributes inElement: (NSString*) elementName; | |
- (void) svgParser: (SVGParser*) parser didFailWithError: (NSError*) error; | |
@end | |
@interface SVGParser : NSOperation | |
+ (instancetype) parserForFile: (NSString*) filePath; //Handles absolute file paths or filenames inside the main bundle | |
@property (weak, nonatomic) id<SVGParserDelegate> delegate; //Must be set before this operation starts | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "SVGParser.h" | |
#import <libxml/parser.h> | |
NSString * const kSVGFileExtension = @"svg"; | |
NSString * const kSVGCompressedFileExtension = @"svgz"; | |
NSString * const kSVGParserErrorDomain = @"SVGParserError"; | |
static xmlSAXHandler svgSAXHandlerStruct; | |
static void errorEncounteredSAX(void * ctx, const char * msg, ...); | |
static void startElementSAX(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes); | |
@interface SVGParser () { | |
NSString* _fileName; | |
} | |
- (instancetype) initWithSVGNamed: (NSString*) svgFileName; | |
@property (readonly, nonatomic) BOOL isAbsoluteFilePath; | |
@end | |
@implementation SVGParser | |
+ (instancetype) parserForFile: (NSString*) svgFileName { | |
return [[self alloc] initWithSVGNamed: svgFileName]; | |
} | |
- (instancetype) initWithSVGNamed:(NSString *)svgFileName { | |
NSString* extension = [svgFileName pathExtension]; | |
NSAssert([extension isEqualToString: kSVGFileExtension] || | |
[extension isEqualToString: kSVGCompressedFileExtension], | |
@"'%@' is not a valid SVG file", | |
svgFileName); | |
self = [super init]; | |
if( self ) { | |
//fragile... | |
if( [svgFileName characterAtIndex: 0] == '/' ) { | |
_isAbsoluteFilePath = YES; | |
} | |
_fileName = svgFileName; | |
} | |
return self; | |
} | |
#pragma mark - NSOperation | |
- (void) main { | |
NSParameterAssert(_delegate); | |
NSString* filePath = _fileName; | |
if( !self.isAbsoluteFilePath ) { | |
NSBundle* bundle = [NSBundle mainBundle]; | |
filePath = [bundle pathForResource: _fileName ofType: nil]; | |
} | |
NSAssert([[NSFileManager defaultManager] fileExistsAtPath: filePath], @"Could not find file at path %@", filePath); | |
// http://xmlsoft.org/html/libxml-parser.html#xmlSAXUserParseFile | |
int result = xmlSAXUserParseFile(&svgSAXHandlerStruct, (__bridge void*)self, [filePath UTF8String]); | |
NSAssert(result == 0, @"Failed to parse svg file (%d) at path: %@", result, filePath); | |
if( result != 0 ) { | |
if( [self.delegate respondsToSelector: @selector(svgParser:didFailWithError:)] ) { | |
xmlErrorPtr xmlError = xmlGetLastError(); | |
NSString* errorMsg = [[NSString alloc] initWithCString: xmlError->message encoding: NSUTF8StringEncoding]; | |
NSError* error = [NSError errorWithDomain: kSVGParserErrorDomain code: xmlError->code userInfo: @{NSLocalizedDescriptionKey: errorMsg}]; | |
[self.delegate svgParser: self didFailWithError: error]; | |
} | |
} | |
xmlCleanupParser(); | |
} | |
@end | |
#pragma mark - libxml SAX Parser | |
// http://xmlsoft.org/html/libxml-tree.html#xmlSAXHandler | |
static xmlSAXHandler svgSAXHandlerStruct = { | |
NULL, /* internalSubset */ | |
NULL, /* isStandalone */ | |
NULL, /* hasInternalSubset */ | |
NULL, /* hasExternalSubset */ | |
NULL, /* resolveEntity */ | |
NULL, /* getEntity */ | |
NULL, /* entityDecl */ | |
NULL, /* notationDecl */ | |
NULL, /* attributeDecl */ | |
NULL, /* elementDecl */ | |
NULL, /* unparsedEntityDecl */ | |
NULL, /* setDocumentLocator */ | |
NULL, /* startDocument */ | |
NULL, /* endDocument */ | |
NULL, /* startElement*/ | |
NULL, /* endElement */ | |
NULL, /* reference */ | |
NULL, /* characters */ | |
NULL, /* ignorableWhitespace */ | |
NULL, /* processingInstruction */ | |
NULL, /* comment */ | |
NULL, /* warning */ | |
errorEncounteredSAX, /* error */ | |
NULL, /* fatalError //: unused error() get all the errors */ | |
NULL, /* getParameterEntity */ | |
NULL, /* cdataBlock */ | |
NULL, /* externalSubset */ | |
XML_SAX2_MAGIC, // | |
NULL, | |
startElementSAX, /* startElementNs */ | |
NULL, /* endElementNs */ | |
NULL, /* serror */ | |
}; | |
static NSMutableDictionary *NSDictionaryCreateFromLibxmlAttributes (const xmlChar **attrs, int attr_ct) { | |
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; | |
NSUInteger limit = attr_ct * 5; | |
for (int i = 0; i < limit/*attr_ct * 5*/; i += 5) { | |
const char *begin = (const char *) attrs[i + 3]; | |
const char *end = (const char *) attrs[i + 4]; | |
int vlen = strlen(begin) - strlen(end); | |
char val[vlen + 1]; | |
strncpy(val, begin, vlen); | |
val[vlen] = '\0'; | |
[dict setObject:[NSString stringWithUTF8String:val] | |
forKey:[NSString stringWithUTF8String:(const char*)attrs[i]]]; | |
} | |
return dict; | |
} | |
static void startElementSAX(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, | |
int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes) { | |
SVGParser* parser = (__bridge SVGParser*)ctx; | |
if( [parser.delegate respondsToSelector: @selector(svgParser:scannedAttributes:inElement:)] ) { | |
NSDictionary* attrs =NSDictionaryCreateFromLibxmlAttributes(attributes, nb_attributes); | |
NSString* element = [[NSString alloc] initWithCString: (const char*)localname encoding: NSUTF8StringEncoding]; | |
[parser.delegate svgParser: parser scannedAttributes: attrs inElement: element]; | |
} | |
} | |
static void errorEncounteredSAX(void *ctx, const char *msg, ...) { | |
SVGParser* parser = (__bridge SVGParser*)ctx; | |
if( [parser.delegate respondsToSelector: @selector(svgParser:didFailWithError:)] ) { | |
NSString* errorMsg = [[NSString alloc] initWithCString: msg encoding: NSUTF8StringEncoding]; | |
NSError* error = [NSError errorWithDomain: kSVGParserErrorDomain code: -1 userInfo: @{NSLocalizedDescriptionKey : errorMsg}]; | |
[parser.delegate svgParser: parser didFailWithError: error]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment