SVGKParser.m 34 KB

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