SVGKParser.m 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950
  1. //
  2. // SVGKParser.m
  3. // SVGKit
  4. //
  5. // Copyright Matt Rajca 2010-2011. All rights reserved.
  6. //
  7. #import "SVGKParser.h"
  8. #import <libxml/parser.h>
  9. #import "SVGKParserSVG.h"
  10. @class SVGKParserGradient;
  11. #import "SVGKParserGradient.h"
  12. @class SVGKParserPatternsAndGradients;
  13. #import "SVGKParserPatternsAndGradients.h"
  14. @class SVGKParserStyles;
  15. #import "SVGKParserStyles.h"
  16. @class SVGKParserDefsAndUse;
  17. #import "SVGKParserDefsAndUse.h"
  18. @class SVGKParserDOM;
  19. #import "SVGKParserDOM.h"
  20. #import "SVGDocument_Mutable.h" // so we can modify the SVGDocuments we're parsing
  21. #import "Node.h"
  22. #import "SVGKSourceString.h"
  23. #import "SVGKSourceURL.h"
  24. #import "CSSStyleSheet.h"
  25. #import "StyleSheetList+Mutable.h"
  26. #import "NSData+NSInputStream.h"
  27. @interface SVGKParser()
  28. @property(nonatomic,strong, readwrite) SVGKSource* source;
  29. @property(nonatomic,strong, readwrite) NSMutableArray* externalStylesheets;
  30. @property(nonatomic,strong, readwrite) SVGKParseResult* currentParseRun;
  31. @property(nonatomic,strong) NSString* defaultXMLNamespaceForThisParseRun;
  32. @property(nonatomic) BOOL hasCancelBeenRequested;
  33. @end
  34. @implementation SVGKParser
  35. @synthesize source;
  36. @synthesize externalStylesheets;
  37. @synthesize currentParseRun;
  38. @synthesize defaultXMLNamespaceForThisParseRun;
  39. @synthesize parserExtensions;
  40. @synthesize parserKnownNamespaces;
  41. static xmlSAXHandler SAXHandler;
  42. 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);
  43. static void endElementSAX(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI);
  44. static void charactersFoundSAX(void * ctx, const xmlChar * ch, int len);
  45. static void errorEncounteredSAX(void * ctx, const char * msg, ...);
  46. static NSString *NSStringFromLibxmlString (const xmlChar *string);
  47. static NSMutableDictionary *NSDictionaryFromLibxmlNamespaces (const xmlChar **namespaces, int namespaces_ct);
  48. static NSMutableDictionary *NSDictionaryFromLibxmlAttributes (const xmlChar **attrs, int attr_ct);
  49. #define kThreadLocalCurrentlyActiveParser ( @"kThreadLocalCurrentlyActiveParser" )
  50. /** This is a workaround to the major, catastophic bugs in libxml that you cannot
  51. attach a "context" object to libxml parser - and without that, you can't actually
  52. parse, because you have no reference to the context of your original "parse" call. ARGH!
  53. */
  54. SVGKParser* getCurrentlyParsingParser()
  55. {
  56. /** Currently implemented NON THREAD SAFE using a static varailbe that only
  57. allows one parse in memory at a time:
  58. */
  59. return [[NSThread currentThread].threadDictionary objectForKey:kThreadLocalCurrentlyActiveParser];
  60. }
  61. +(void)cancelParser:(SVGKParser *)parserToCancel
  62. {
  63. parserToCancel.hasCancelBeenRequested = TRUE;
  64. }
  65. +(SVGKParser *) newParserWithDefaultSVGKParserExtensions:(SVGKSource *)source
  66. {
  67. SVGKParser *parser = [[SVGKParser alloc] initWithSource:source];
  68. [parser addDefaultSVGParserExtensions];
  69. return parser;
  70. }
  71. + (SVGKParseResult*) parseSourceUsingDefaultSVGKParser:(SVGKSource*) source;
  72. {
  73. SVGKParser* parser = [self newParserWithDefaultSVGKParserExtensions:source];
  74. SVGKParseResult* result = [parser parseSynchronously];
  75. return result;
  76. }
  77. #define READ_CHUNK_SZ 1024*10
  78. - (id)initWithSource:(SVGKSource *) s {
  79. self = [super init];
  80. if (self) {
  81. self.parserExtensions = [NSMutableArray array];
  82. self.source = s;
  83. self.externalStylesheets = nil;
  84. _storedChars = [NSMutableString new];
  85. _stackOfParserExtensions = [NSMutableArray new];
  86. }
  87. return self;
  88. }
  89. -(void) addDefaultSVGParserExtensions
  90. {
  91. SVGKParserSVG *subParserSVG = [[SVGKParserSVG alloc] init];
  92. SVGKParserGradient* subParserGradients = [[SVGKParserGradient alloc] init];
  93. SVGKParserPatternsAndGradients *subParserPatternsAndGradients = [[SVGKParserPatternsAndGradients alloc] init];
  94. SVGKParserStyles* subParserStyles = [[SVGKParserStyles alloc] init];
  95. SVGKParserDefsAndUse *subParserDefsAndUse = [[SVGKParserDefsAndUse alloc] init];
  96. SVGKParserDOM *subParserXMLDOM = [[SVGKParserDOM alloc] init];
  97. [self addParserExtension:subParserSVG];
  98. [self addParserExtension:subParserGradients];
  99. [self addParserExtension:subParserPatternsAndGradients]; // FIXME: this is a "not implemente yet" parser; now that we have gradients, it should be deleted / renamed!
  100. [self addParserExtension:subParserStyles];
  101. [self addParserExtension:subParserDefsAndUse];
  102. [self addParserExtension:subParserXMLDOM];
  103. }
  104. - (void) addParserExtension:(NSObject<SVGKParserExtension>*) extension
  105. {
  106. // TODO: Should check for conflicts between this parser-extension and our existing parser-extensions, and issue warnings for any we find
  107. if( self.parserExtensions == nil )
  108. {
  109. self.parserExtensions = [NSMutableArray array];
  110. }
  111. if( [self.parserExtensions containsObject:extension])
  112. {
  113. SVGKitLogVerbose(@"[%@] WARNING: attempted to add a ParserExtension that was already added = %@", [self class], extension);
  114. return;
  115. }
  116. [self.parserExtensions addObject:extension];
  117. if( self.parserKnownNamespaces == nil )
  118. {
  119. self.parserKnownNamespaces = [NSMutableDictionary dictionary];
  120. }
  121. for( NSString* parserNamespace in extension.supportedNamespaces )
  122. {
  123. NSMutableArray* extensionsForNamespace = [self.parserKnownNamespaces objectForKey:parserNamespace];
  124. if( extensionsForNamespace == nil )
  125. {
  126. extensionsForNamespace = [NSMutableArray array];
  127. [self.parserKnownNamespaces setObject:extensionsForNamespace forKey:parserNamespace];
  128. }
  129. [extensionsForNamespace addObject:extension];
  130. }
  131. }
  132. //static FILE *desc;
  133. //static size_t
  134. //readPacket(char *mem, int size) {
  135. // size_t res;
  136. //
  137. // res = fread(mem, 1, size, desc);
  138. // return(res);
  139. //}
  140. - (SVGKParseResult*) parseSynchronously
  141. {
  142. if( self.currentParseRun != nil )
  143. {
  144. SVGKitLogError(@"FATAL: attempting to run the parser twice in one thread; limxml is single-threaded only, so we are too (until someone wraps libxml to be multi-threaded)");
  145. }
  146. self.currentParseRun = [SVGKParseResult new];
  147. _parentOfCurrentNode = nil;
  148. [_stackOfParserExtensions removeAllObjects];
  149. [[NSThread currentThread].threadDictionary setObject:self forKey:kThreadLocalCurrentlyActiveParser];
  150. /*
  151. // 1. while (source has chunks of BYTES)
  152. // 2. read a chunk from source, send to libxml
  153. // 3. if libxml failed chunk, break
  154. // 4. return result
  155. */
  156. NSInputStream* stream = source.stream;
  157. if( stream == nil )
  158. {
  159. [currentParseRun addSourceError:[NSError errorWithDomain:@"SVGKit" code:2354 userInfo:@{NSLocalizedDescriptionKey : [NSString stringWithFormat:@"Source failed to create a valid NSInputStream; check your log files for why the SVGKSource failed (source = %@)",source]}]];
  160. }
  161. else
  162. {
  163. [stream open];
  164. NSStreamStatus status = [stream streamStatus];
  165. if (status != NSStreamStatusOpen)
  166. {
  167. if (status == NSStreamStatusError)
  168. {
  169. [currentParseRun addSourceError:[stream streamError]];
  170. }
  171. else
  172. {
  173. [currentParseRun addSourceError:[NSError errorWithDomain:@"SVGKit" code:2573 userInfo:@{NSLocalizedDescriptionKey: @"The stream wouldn't open; this can happen when Apple libraries incorrectly open slowly over the internet. Any other case is probably a threading bug inside SVGKit"}]];
  174. }
  175. [stream close];
  176. return currentParseRun;
  177. }
  178. char buff[READ_CHUNK_SZ];
  179. xmlParserCtxtPtr ctx;
  180. ctx = xmlCreatePushParserCtxt(&SAXHandler, NULL, NULL, 0, NULL); // NEVER pass anything except NULL in second arg - libxml has a massive bug internally
  181. /*
  182. SVGKitLogVerbose(@"[%@] WARNING: Substituting entities directly into document, c.f. http://www.xmlsoft.org/entities.html for why!", [self class]);
  183. xmlSubstituteEntitiesDefault(1);
  184. xmlCtxtUseOptions( ctx,
  185. XML_PARSE_DTDATTR // default DTD attributes
  186. | XML_PARSE_NOENT // substitute entities
  187. | XML_PARSE_DTDVALID // validate with the DTD
  188. );
  189. */
  190. if( ctx ) // if libxml init succeeds...
  191. {
  192. // 1. while (source has chunks of BYTES)
  193. // 2. Check asynch cancellation flag
  194. // 3. read a chunk from source, send to libxml
  195. uint64_t totalBytesRead = 0;
  196. NSInteger bytesRead = [stream read:(uint8_t*)&buff maxLength:READ_CHUNK_SZ];
  197. while( bytesRead > 0 )
  198. {
  199. totalBytesRead += bytesRead;
  200. if( self.hasCancelBeenRequested )
  201. {
  202. SVGKitLogInfo( @"SVGKParser: 'cancel parse' discovered; bailing on this XML parse" );
  203. break;
  204. }
  205. else
  206. {
  207. if( source.approximateLengthInBytesOr0 > 0 )
  208. {
  209. currentParseRun.parseProgressFractionApproximate = totalBytesRead / (double) source.approximateLengthInBytesOr0;
  210. }
  211. else
  212. currentParseRun.parseProgressFractionApproximate = 0;
  213. }
  214. NSInteger libXmlParserParseError;
  215. @try
  216. {
  217. libXmlParserParseError = xmlParseChunk(ctx, buff, (int)bytesRead, 0);
  218. }
  219. @catch( NSException* e )
  220. {
  221. SVGKitLogError( @"Exception while trying to parse SVG file, will store in parse results. Exception = %@", e);
  222. [currentParseRun addParseErrorFatal:[NSError errorWithDomain:@"SVGK Parsing" code:32523432 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Exception = %@", e]}]];
  223. }
  224. if( [currentParseRun.errorsFatal count] > 0 )
  225. {
  226. // 3. if libxml failed chunk, break
  227. if( libXmlParserParseError > 0 )
  228. {
  229. SVGKitLogVerbose(@"[%@] libXml reported internal parser error with magic libxml code = %li (look this up on http://xmlsoft.org/html/libxml-xmlerror.html#xmlParserErrors)", [self class], (long)libXmlParserParseError );
  230. currentParseRun.libXMLFailed = YES;
  231. }
  232. else
  233. {
  234. SVGKitLogWarn(@"[%@] SVG parser generated one or more FATAL errors (not the XML parser), errors follow:", [self class] );
  235. for( NSError* error in currentParseRun.errorsFatal )
  236. {
  237. SVGKitLogWarn(@"[%@] ... FATAL ERRRO in SVG parse: %@", [self class], error );
  238. }
  239. }
  240. break;
  241. }
  242. bytesRead = [stream read:(uint8_t*)&buff maxLength:READ_CHUNK_SZ];
  243. }
  244. }
  245. [stream close]; // close the handle NO MATTER WHAT
  246. if (!currentParseRun.libXMLFailed
  247. && currentParseRun.errorsFatal.count < 1 )
  248. xmlParseChunk(ctx, NULL, 0, 1); // EOF
  249. xmlFreeParserCtxt(ctx);
  250. }
  251. [[NSThread currentThread].threadDictionary removeObjectForKey:kThreadLocalCurrentlyActiveParser];
  252. // 4. return result
  253. return currentParseRun;
  254. }
  255. /** ADAM: use this for a higher-performance, *non-blocking* parse
  256. (when someone upgrades this class and the interface to support non-blocking parse)
  257. // Called when a chunk of data has been downloaded.
  258. - (void)connection:(NSURLConnection *)connection
  259. didReceiveData:(NSData *)data
  260. {
  261. // Process the downloaded chunk of data.
  262. xmlParseChunk(_xmlParserContext, (const char *)[data bytes], [data length], 0);//....Getting Exception at this line.
  263. }
  264. */
  265. - (SVGKSource *)loadCSSFrom:(NSString *)href
  266. {
  267. SVGKSource *cssSource = nil;
  268. if( [href hasPrefix:@"http"] )
  269. {
  270. NSURL *url = [NSURL URLWithString:href];
  271. cssSource = [SVGKSourceURL sourceFromURL:url];
  272. }
  273. else
  274. {
  275. cssSource = [self.source sourceFromRelativePath:href];
  276. }
  277. if( cssSource == nil )
  278. {
  279. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  280. NSString *documentsDirectory = [paths objectAtIndex:0];
  281. NSString *path = [documentsDirectory stringByAppendingPathComponent:href];
  282. NSString *cssText = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
  283. if( cssText == nil )
  284. {
  285. SVGKitLogWarn(@"[%@] Unable to find external CSS file '%@'", [self class], href );
  286. }
  287. else
  288. {
  289. cssSource = [SVGKSourceString sourceFromContentsOfString:cssText];
  290. }
  291. }
  292. return cssSource;
  293. }
  294. - (NSString *)stringFromSource:(SVGKSource *) src
  295. {
  296. [src.stream open]; // if we do this, we CANNOT parse from this source again in future
  297. NSData *data = [NSData dataWithContentsOfStream:src.stream initialCapacity:4096 error:nil];
  298. return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  299. }
  300. - (void)handleProcessingInstruction:(NSString *)target withData:(NSString *) data
  301. {
  302. if( [@"xml-stylesheet" isEqualToString:target] && ( [data rangeOfString:@"type=\"text/css\""].location != NSNotFound || [data rangeOfString:@"type="].location == NSNotFound ) )
  303. {
  304. NSRange startHref = [data rangeOfString:@"href=\""];
  305. if( startHref.location != NSNotFound )
  306. {
  307. NSUInteger startIndex = startHref.location + startHref.length;
  308. NSRange endHref = [data rangeOfString:@"\"" options:0 range:NSMakeRange(startIndex, data.length - startIndex)];
  309. if( startHref.location != NSNotFound )
  310. {
  311. NSString *href = [data substringWithRange:NSMakeRange(startIndex, endHref.location - startIndex)];
  312. SVGKSource* cssSource = [self loadCSSFrom:href];
  313. if( cssSource != nil )
  314. {
  315. NSString *cssText = [self stringFromSource:cssSource];
  316. CSSStyleSheet* parsedStylesheet = [[CSSStyleSheet alloc] initWithString:cssText];
  317. if( currentParseRun.parsedDocument.rootElement == nil )
  318. {
  319. if( self.externalStylesheets == nil )
  320. self.externalStylesheets = [[NSMutableArray alloc] init];
  321. [self.externalStylesheets addObject:parsedStylesheet];
  322. }
  323. else
  324. {
  325. [currentParseRun.parsedDocument.rootElement.styleSheets.internalArray addObject:parsedStylesheet];
  326. }
  327. }
  328. }
  329. }
  330. }
  331. }
  332. - (void)addExternalStylesheetsToRootElement {
  333. if( self.externalStylesheets != nil )
  334. {
  335. [currentParseRun.parsedDocument.rootElement.styleSheets.internalArray addObjectsFromArray:self.externalStylesheets];
  336. self.externalStylesheets = nil;
  337. }
  338. }
  339. static void processingInstructionSAX (void * ctx,
  340. const xmlChar * target,
  341. const xmlChar * data)
  342. {
  343. SVGKParser* self = getCurrentlyParsingParser();
  344. NSString *stringTarget = NSStringFromLibxmlString(target);
  345. NSString *stringData = NSStringFromLibxmlString(data);
  346. [self handleProcessingInstruction:stringTarget withData:stringData];
  347. }
  348. - (void)handleStartElement:(NSString *)name namePrefix:(NSString*)prefix namespaceURI:(NSString*) XMLNSURI attributeObjects:(NSMutableDictionary *) attributeObjects
  349. {
  350. BOOL parsingRootTag = FALSE;
  351. if( _parentOfCurrentNode == nil )
  352. parsingRootTag = TRUE;
  353. if( ! parsingRootTag && _storedChars.length > 0 )
  354. {
  355. /** Send any partially-parsed text data into the old node that is now the parent node,
  356. then change the "storing chars" flag to fit the new node */
  357. Text *tNode = [[Text alloc] initWithValue:_storedChars];
  358. [_parentOfCurrentNode appendChild:tNode];
  359. [_storedChars setString:@""];
  360. }
  361. /**
  362. Search for a Parser Extension to handle this XML tag ...
  363. (most tags are handled by the default SVGParserSVG - but if you have other XML embedded in your SVG, you'll
  364. have custom parser extentions too)
  365. */
  366. NSObject<SVGKParserExtension>* defaultParserForThisNamespace = nil;
  367. NSObject<SVGKParserExtension>* defaultParserForEverything = nil;
  368. for( NSObject<SVGKParserExtension>* subParser in self.parserExtensions )
  369. {
  370. // TODO: rather than checking for the default parser on every node, we should stick them in a Dictionar at the start and re-use them when needed
  371. /**
  372. First: check if this parser is a "default" / fallback parser. If so, skip it, and only use it
  373. AT THE VERY END after checking all other parsers
  374. */
  375. BOOL shouldBreakBecauseParserIsADefault = FALSE;
  376. if( [[subParser supportedNamespaces] count] == 0 )
  377. {
  378. defaultParserForEverything = subParser;
  379. shouldBreakBecauseParserIsADefault = TRUE;
  380. }
  381. if( [[subParser supportedNamespaces] containsObject:XMLNSURI]
  382. && [[subParser supportedTags] count] == 0 )
  383. {
  384. defaultParserForThisNamespace = subParser;
  385. shouldBreakBecauseParserIsADefault = TRUE;
  386. }
  387. if( shouldBreakBecauseParserIsADefault )
  388. continue;
  389. /**
  390. Now we know it's a specific parser, check if it handles this particular node
  391. */
  392. if( [[subParser supportedNamespaces] containsObject:XMLNSURI]
  393. && [[subParser supportedTags] containsObject:name] )
  394. {
  395. [_stackOfParserExtensions addObject:subParser];
  396. /** Parser Extenstion creates a node for us */
  397. Node* subParserResult = [subParser handleStartElement:name document:source namePrefix:prefix namespaceURI:XMLNSURI attributes:attributeObjects parseResult:self.currentParseRun parentNode:_parentOfCurrentNode];
  398. #if DEBUG_XML_PARSER
  399. SVGKitLogVerbose(@"[%@] tag: <%@:%@> id=%@ -- handled by subParser: %@", [self class], prefix, name, ([((Attr*)[attributeObjects objectForKey:@"id"]) value] != nil?[((Attr*)[attributeObjects objectForKey:@"id"]) value]:@"(none)"), subParser );
  400. #endif
  401. /** Add the new (partially parsed) node to the parent node in tree
  402. (need this for some of the parsing, later on, where we need to be able to read up
  403. the tree to make decisions about the data - this is REQUIRED by the SVG Spec)
  404. */
  405. [_parentOfCurrentNode appendChild:subParserResult]; // this is a DOM method: should NOT have side-effects
  406. _parentOfCurrentNode = subParserResult;
  407. if( parsingRootTag )
  408. {
  409. currentParseRun.parsedDocument.rootElement = (SVGSVGElement*) subParserResult;
  410. [self addExternalStylesheetsToRootElement];
  411. }
  412. return;
  413. }
  414. }
  415. /**
  416. IF we had a specific matching parser, we would have returned already.
  417. Since we haven't, it means we have to try the default parsers instead
  418. */
  419. NSObject<SVGKParserExtension>* eventualParser = defaultParserForThisNamespace != nil ? defaultParserForThisNamespace : defaultParserForEverything;
  420. NSAssert( eventualParser != nil, @"Found a tag (prefix:%@ name:%@) that was rejected by all the parsers available. Perhaps you forgot to include a default parser (usually: SVGKParserDOM, which will handle any / all XML tags)", prefix, name );
  421. SVGKitLogVerbose(@"[%@] WARN: found a tag with no namespace parser: (</%@>), using default parser(%@)", [self class], name, eventualParser );
  422. [_stackOfParserExtensions addObject:eventualParser];
  423. /** Parser Extenstion creates a node for us */
  424. Node* subParserResult = [eventualParser handleStartElement:name document:source namePrefix:prefix namespaceURI:XMLNSURI attributes:attributeObjects parseResult:self.currentParseRun parentNode:_parentOfCurrentNode];
  425. #if DEBUG_XML_PARSER
  426. SVGKitLogVerbose(@"[%@] tag: <%@:%@> id=%@ -- handled by subParser: %@", [self class], prefix, name, ([((Attr*)[attributeObjects objectForKey:@"id"]) value] != nil?[((Attr*)[attributeObjects objectForKey:@"id"]) value]:@"(none)"), eventualParser );
  427. #endif
  428. /** Add the new (partially parsed) node to the parent node in tree
  429. (need this for some of the parsing, later on, where we need to be able to read up
  430. the tree to make decisions about the data - this is REQUIRED by the SVG Spec)
  431. */
  432. [_parentOfCurrentNode appendChild:subParserResult]; // this is a DOM method: should NOT have side-effects
  433. _parentOfCurrentNode = subParserResult;
  434. if( parsingRootTag )
  435. {
  436. currentParseRun.parsedDocument.rootElement = (SVGSVGElement*) subParserResult;
  437. [self addExternalStylesheetsToRootElement];
  438. }
  439. return;
  440. }
  441. static void startElementSAX (void *ctx, const xmlChar *localname, const xmlChar *prefix,
  442. const xmlChar *URI, int nb_namespaces, const xmlChar **namespaces,
  443. int nb_attributes, int nb_defaulted, const xmlChar **attributes) {
  444. SVGKParser *self = getCurrentlyParsingParser();
  445. NSString *stringLocalName = NSStringFromLibxmlString(localname);
  446. NSString *stringPrefix = NSStringFromLibxmlString(prefix);
  447. NSMutableDictionary *namespacesByPrefix = NSDictionaryFromLibxmlNamespaces(namespaces, nb_namespaces); // TODO: need to do something with this; this is the ONLY point at which we discover the "xmlns:" definitions in the SVG doc! See below for a temp fix
  448. NSMutableDictionary *attributeObjects = NSDictionaryFromLibxmlAttributes(attributes, nb_attributes);
  449. NSString *stringURI = NSStringFromLibxmlString(URI);
  450. /** Set a default Namespace for rest of this document if one is included in the attributes */
  451. if( self.defaultXMLNamespaceForThisParseRun == nil )
  452. {
  453. NSString* newDefaultNamespace = [namespacesByPrefix valueForKey:@""];
  454. if( newDefaultNamespace != nil )
  455. {
  456. self.defaultXMLNamespaceForThisParseRun = newDefaultNamespace;
  457. }
  458. }
  459. if( stringURI == nil
  460. && self.defaultXMLNamespaceForThisParseRun != nil )
  461. {
  462. /** Apply the default XML NS to this tag as if it had been typed in.
  463. e.g. if somewhere in this doc the author put:
  464. <svg xmlns="blah">
  465. ...then any time we find a tag that HAS NO EXPLICIT NAMESPACE, we act as if it had that one.
  466. */
  467. stringURI = self.defaultXMLNamespaceForThisParseRun;
  468. }
  469. for( Attr* newAttribute in attributeObjects.allValues )
  470. {
  471. if( newAttribute.namespaceURI == nil )
  472. newAttribute.namespaceURI = self.defaultXMLNamespaceForThisParseRun;
  473. }
  474. /**
  475. This appears to be a major bug in libxml: "xmlns:blah="blah"" is treated as a namespace declaration - but libxml
  476. FAILS to report it as an attribute (according to the XML spec, it appears to be "both" of those things?)
  477. ...but I could be wrong. The XML definition of Namespaces is badly written and missing several key bits of info
  478. (I have inferred the "both" status from the definition of XML's Node class, which raises an error on setting
  479. Node.prefix "if the node is an attribute, and it's in the xmlns namespace ... and ... and" -- which implies to me
  480. that attributes can be xmlns="blah" definitions)
  481. ... UPDATE: I have found confirming evidence in the "http://www.w3.org/2000/xmlns/" namespace itself. Visit that URL! It has docs...
  482. NB: this bug / issue was irrelevant until we started trying to export SVG documents from memory back to XML strings,
  483. at which point: we need this info! Or else we end up substantially changing the incoming SVG :(.
  484. So:
  485. Add any namespace declarations to the attributes dictionary:
  486. */
  487. for( NSString* prefix in namespacesByPrefix )
  488. {
  489. NSString* namespace = [namespacesByPrefix objectForKey:prefix];
  490. /** NB this happens *AFTER* setting default namespaces for all attributes - the xmlns: attributes are required by the XML
  491. spec to all live in a special magical namespace AND to all use the same prefix of "xmlns" - no other is allowed!
  492. */
  493. Attr* newAttributeFromNamespaceDeclaration = [[Attr alloc] initWithNamespace:@"http://www.w3.org/2000/xmlns/" qualifiedName:[NSString stringWithFormat:@"xmlns:%@", prefix] value:namespace];
  494. [attributeObjects setObject:newAttributeFromNamespaceDeclaration forKey:newAttributeFromNamespaceDeclaration.nodeName];
  495. }
  496. /**
  497. TODO: temporary workaround to PRETEND that all namespaces are always defined;
  498. this is INCORRECT: namespaces should be UNdefined once you close the parent tag that defined them (I think?)
  499. */
  500. for( NSString* prefix in namespacesByPrefix )
  501. {
  502. NSString* uri = [namespacesByPrefix objectForKey:prefix];
  503. if( [prefix isEqualToString:@""] ) // special string we put in earlier to indicate zero-length / "default" prefix
  504. [self.currentParseRun.namespacesEncountered setObject:uri forKey:[NSNull null]];
  505. else
  506. [self.currentParseRun.namespacesEncountered setObject:uri forKey:prefix];
  507. }
  508. #if DEBUG_XML_PARSER
  509. #if DEBUG_VERBOSE_LOG_EVERY_TAG
  510. SVGKitLogWarn(@"[%@] DEBUG_VERBOSE: <%@%@> (namespace URL:%@), attributes: %i", [self class], [NSString stringWithFormat:@"%@:",stringPrefix], name, stringURI, nb_attributes );
  511. #endif
  512. #endif
  513. #if DEBUG_VERBOSE_LOG_EVERY_TAG
  514. if( prefix2 == nil )
  515. {
  516. /* The XML library allows this, although it's very unhelpful when writing application code */
  517. /* Let's find out what namespaces DO exist... */
  518. /*
  519. TODO / DEVELOPER WARNING: the library says nb_namespaces is the number of elements in the array,
  520. but it keeps returning nil pointer (not always, but often). WTF? Not sure what we're doing wrong
  521. here, but commenting it out for now...
  522. if( nb_namespaces > 0 )
  523. {
  524. for( int i=0; i<nb_namespaces; i++ )
  525. {
  526. SVGKitLogWarn(@"[%@] DEBUG: found namespace [%i] : %@", [self class], i, namespaces[i] );
  527. }
  528. }
  529. else
  530. SVGKitLogWarn(@"[%@] DEBUG: there are ZERO namespaces!", [self class] );
  531. */
  532. }
  533. #endif
  534. if( stringURI == nil && stringPrefix == nil )
  535. {
  536. SVGKitLogWarn(@"[%@] WARNING: Your input SVG contains tags that have no namespace, and your document doesn't define a default namespace. This is always incorrect - it means some of your SVG data will be ignored, and usually means you have a typo in there somewhere. Tag with no namespace: <%@>", [self class], stringLocalName );
  537. }
  538. [self handleStartElement:stringLocalName namePrefix:stringPrefix namespaceURI:stringURI attributeObjects:attributeObjects];
  539. }
  540. - (void)handleEndElement:(NSString *)name {
  541. //DELETE DEBUG SVGKitLogVerbose(@"ending element, name = %@", name);
  542. NSObject* lastobject = [_stackOfParserExtensions lastObject];
  543. [_stackOfParserExtensions removeLastObject];
  544. NSObject<SVGKParserExtension>* parser = (NSObject<SVGKParserExtension>*)lastobject;
  545. // NSObject<SVGKParserExtension>* parentParser = [_stackOfParserExtensions lastObject];
  546. #if DEBUG_XML_PARSER
  547. #if DEBUG_VERBOSE_LOG_EVERY_TAG
  548. SVGKitLogVerbose(@"[%@] DEBUG-PARSER: ended tag (</%@>), handled by parser (%@) with parent parsed by %@", [self class], name, parser, parentParser );
  549. #endif
  550. #endif
  551. /**
  552. At this point, the "parent of current node" is still set to the node we're
  553. closing - because we haven't finished closing it yet
  554. */
  555. if( _storedChars.length > 0 )
  556. {
  557. /** Send any parsed text data into the node-we're-closing */
  558. Text *tNode = [[Text alloc] initWithValue:_storedChars];
  559. [_parentOfCurrentNode appendChild:tNode];
  560. [_storedChars setString:@""];
  561. }
  562. [parser handleEndElement:_parentOfCurrentNode document:source parseResult:self.currentParseRun];
  563. /** Update the _parentOfCurrentNode to point to the parent of the node we just closed...
  564. */
  565. _parentOfCurrentNode = _parentOfCurrentNode.parentNode;
  566. }
  567. static void endElementSAX (void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI) {
  568. SVGKParser* self = getCurrentlyParsingParser();
  569. [self handleEndElement:NSStringFromLibxmlString(localname)];
  570. }
  571. - (void)handleFoundCharacters:(const xmlChar *)chars length:(int)len {
  572. [_storedChars appendString:[[NSString alloc] initWithBytes:chars length:len encoding:NSUTF8StringEncoding]];
  573. }
  574. static void cDataFoundSAX(void *ctx, const xmlChar *value, int len)
  575. {
  576. SVGKParser* self = getCurrentlyParsingParser();
  577. [self handleFoundCharacters:value length:len];
  578. }
  579. static void charactersFoundSAX (void *ctx, const xmlChar *chars, int len) {
  580. SVGKParser* self = getCurrentlyParsingParser();
  581. [self handleFoundCharacters:chars length:len];
  582. }
  583. static void errorEncounteredSAX (void *ctx, const char *msg, ...) {
  584. SVGKitLogWarn(@"Error encountered during parse: %s", msg);
  585. SVGKParser* self = getCurrentlyParsingParser();
  586. SVGKParseResult* parseResult = self.currentParseRun;
  587. [parseResult addSAXError:[NSError errorWithDomain:@"SVG-SAX" code:1 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
  588. [NSString stringWithCString:msg encoding:NSUTF8StringEncoding], NSLocalizedDescriptionKey,
  589. nil]]];
  590. }
  591. static void unparsedEntityDeclaration(void * ctx,
  592. const xmlChar * name,
  593. const xmlChar * publicId,
  594. const xmlChar * systemId,
  595. const xmlChar * notationName)
  596. {
  597. SVGKitLogWarn(@"Error: unparsed entity Decl");
  598. }
  599. static void structuredError (void * userData,
  600. xmlErrorPtr error)
  601. {
  602. /**
  603. XML_ERR_WARNING = 1 : A simple warning
  604. XML_ERR_ERROR = 2 : A recoverable error
  605. XML_ERR_FATAL = 3 : A fatal error
  606. */
  607. xmlErrorLevel errorLevel = error->level;
  608. NSMutableDictionary* details = [NSMutableDictionary dictionaryWithObjectsAndKeys:
  609. [NSString stringWithCString:error->message encoding:NSUTF8StringEncoding], NSLocalizedDescriptionKey,
  610. [NSNumber numberWithInt:error->line], @"lineNumber",
  611. [NSNumber numberWithInt:error->int2], @"columnNumber",
  612. nil];
  613. if( error->str1 )
  614. [details setValue:[NSString stringWithCString:error->str1 encoding:NSUTF8StringEncoding] forKey:@"bonusInfo1"];
  615. if( error->str2 )
  616. [details setValue:[NSString stringWithCString:error->str2 encoding:NSUTF8StringEncoding] forKey:@"bonusInfo2"];
  617. if( error->str3 )
  618. [details setValue:[NSString stringWithCString:error->str3 encoding:NSUTF8StringEncoding] forKey:@"bonusInfo3"];
  619. NSError* objcError = [NSError errorWithDomain:[[NSNumber numberWithInt:error->domain] stringValue] code:error->code userInfo:details];
  620. SVGKParser* self = getCurrentlyParsingParser();
  621. SVGKParseResult* parseResult = self.currentParseRun;
  622. switch( errorLevel )
  623. {
  624. case XML_ERR_WARNING:
  625. {
  626. [parseResult addParseWarning:objcError];
  627. }break;
  628. case XML_ERR_ERROR:
  629. {
  630. [parseResult addParseErrorRecoverable:objcError];
  631. }break;
  632. case XML_ERR_FATAL:
  633. {
  634. [parseResult addParseErrorFatal:objcError];
  635. }
  636. default:
  637. break;
  638. }
  639. }
  640. static xmlSAXHandler SAXHandler = {
  641. NULL, /* internalSubset */
  642. NULL, /* isStandalone */
  643. NULL, /* hasInternalSubset */
  644. NULL, /* hasExternalSubset */
  645. NULL, /* resolveEntity */
  646. NULL, /* getEntity */
  647. NULL, /* entityDecl */
  648. NULL, /* notationDecl */
  649. NULL, /* attributeDecl */
  650. NULL, /* elementDecl */
  651. unparsedEntityDeclaration, /* unparsedEntityDecl */
  652. NULL, /* setDocumentLocator */
  653. NULL, /* startDocument */
  654. NULL, /* endDocument */
  655. NULL, /* startElement*/
  656. NULL, /* endElement */
  657. NULL, /* reference */
  658. charactersFoundSAX, /* characters */
  659. NULL, /* ignorableWhitespace */
  660. processingInstructionSAX, /* processingInstruction */
  661. NULL, /* comment */
  662. NULL, /* warning */
  663. errorEncounteredSAX, /* error */
  664. NULL, /* fatalError //: unused error() get all the errors */
  665. NULL, /* getParameterEntity */
  666. cDataFoundSAX, /* cdataBlock */
  667. NULL, /* externalSubset */
  668. XML_SAX2_MAGIC,
  669. NULL,
  670. startElementSAX, /* startElementNs */
  671. endElementSAX, /* endElementNs */
  672. structuredError, /* serror */
  673. };
  674. #pragma mark -
  675. #pragma mark Utility
  676. static NSString *NSStringFromLibxmlString (const xmlChar *string) {
  677. if( string == NULL ) // Yes, Apple requires we do this check!
  678. return nil;
  679. else
  680. return [NSString stringWithUTF8String:(const char *) string];
  681. }
  682. static NSMutableDictionary *NSDictionaryFromLibxmlNamespaces (const xmlChar **namespaces, int namespaces_ct)
  683. {
  684. NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
  685. for (int i = 0; i < namespaces_ct * 2; i += 2)
  686. {
  687. NSString* prefix = NSStringFromLibxmlString(namespaces[i]);
  688. NSString* uri = NSStringFromLibxmlString(namespaces[i+1]);
  689. if( prefix == nil )
  690. prefix = @""; // Special case: Apple dictionaries can't handle null keys
  691. [dict setObject:uri
  692. forKey:prefix];
  693. }
  694. return dict;
  695. }
  696. static NSMutableDictionary *NSDictionaryFromLibxmlAttributes (const xmlChar **attrs, int attr_ct) {
  697. NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
  698. for (int i = 0; i < attr_ct * 5; i += 5) {
  699. const char *begin = (const char *) attrs[i + 3];
  700. const char *end = (const char *) attrs[i + 4];
  701. size_t len = end - begin;
  702. NSString* value = [[NSString alloc] initWithBytes:begin length:len encoding:NSUTF8StringEncoding];
  703. NSString* localName = NSStringFromLibxmlString(attrs[i]);
  704. NSString* prefix = NSStringFromLibxmlString(attrs[i+1]);
  705. NSString* uri = NSStringFromLibxmlString(attrs[i+2]);
  706. NSString* qname = (prefix == nil) ? localName : [NSString stringWithFormat:@"%@:%@", prefix, localName];
  707. Attr* newAttribute = [[Attr alloc] initWithNamespace:uri qualifiedName:qname value:value];
  708. [dict setObject:newAttribute
  709. forKey:qname];
  710. }
  711. return dict;
  712. }
  713. #define MAX_ACCUM 256
  714. #define MAX_NAME 256
  715. +(NSDictionary *) NSDictionaryFromCSSAttributes: (Attr*) styleAttribute {
  716. if( styleAttribute == nil )
  717. {
  718. SVGKitLogWarn(@"[%@] WARNING: asked to convert an empty CSS string into a CSS dictionary; returning empty dictionary", [self class] );
  719. return [NSDictionary dictionary];
  720. }
  721. NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
  722. const char *cstr = [styleAttribute.value UTF8String];
  723. size_t len = strlen(cstr);
  724. char name[MAX_NAME];
  725. bzero(name, MAX_NAME);
  726. char accum[MAX_ACCUM];
  727. bzero(accum, MAX_ACCUM);
  728. size_t accumIdx = 0;
  729. for (size_t n = 0; n <= len; n++) {
  730. char c = cstr[n];
  731. if (c == '\n' || c == '\t' || c == ' ') {
  732. continue;
  733. }
  734. if (c == ':') {
  735. strcpy(name, accum);
  736. name[accumIdx] = '\0';
  737. bzero(accum, MAX_ACCUM);
  738. accumIdx = 0;
  739. continue;
  740. }
  741. else if (c == ';' || c == '\0') {
  742. accum[accumIdx] = '\0';
  743. Attr* newAttribute = [[Attr alloc] initWithNamespace:styleAttribute.namespaceURI qualifiedName:[NSString stringWithUTF8String:name] value:[NSString stringWithUTF8String:accum]];
  744. [dict setObject:newAttribute
  745. forKey:newAttribute.localName];
  746. bzero(name, MAX_NAME);
  747. bzero(accum, MAX_ACCUM);
  748. accumIdx = 0;
  749. continue;
  750. }
  751. accum[accumIdx++] = c;
  752. }
  753. return dict;
  754. }
  755. @end