123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877 |
- #import "SVGKImage.h"
- #import "SVGDefsElement.h"
- #import "SVGDescriptionElement.h"
- #import "SVGKParser.h"
- #import "SVGTitleElement.h"
- #import "SVGPathElement.h"
- #import "SVGUseElement.h"
- #import "SVGClipPathElement.h"
- #import "SVGSwitchElement.h"
- #import "NodeList+Mutable.h"
- #import "SVGSVGElement_Mutable.h"
- #import "SVGKParserSVG.h"
- #import "SVGKSourceLocalFile.h"
- #import "SVGKSourceURL.h"
- #import "SVGKSourceNSData.h"
- #import "CALayer+RecursiveClone.h"
- #if SVGKIT_MAC
- #import "SVGKExporterNSImage.h"
- #else
- #import "SVGKExporterUIImage.h"
- #endif
- #if ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED
- @interface SVGKImageCacheLine : NSObject
- @property(nonatomic) int numberOfInstances;
- @property(nonatomic,strong) SVGKImage* mainInstance;
- @end
- @implementation SVGKImageCacheLine
- @synthesize numberOfInstances;
- @synthesize mainInstance;
- @end
- #endif
- @interface SVGKImage ()
- @property(nonatomic) CGSize internalSizeThatWasSetExplicitlyByUser;
- @property (nonatomic, strong, readwrite) SVGKParseResult* parseErrorsAndWarnings;
- @property (nonatomic, strong, readwrite) SVGKSource* source;
- @property (nonatomic, strong, readwrite) SVGDocument* DOMDocument;
- @property (nonatomic, strong, readwrite) SVGSVGElement* DOMTree;
- @property (nonatomic, strong, readwrite) CALayer* CALayerTree;
- #if ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED
- @property (nonatomic, strong, readwrite) NSString* nameUsedToInstantiate;
- #endif
- #pragma mark - UIImage methods cloned and re-implemented as SVG intelligent methods
- @end
- #pragma mark - main class
- @implementation SVGKImage
- @synthesize DOMDocument, DOMTree, CALayerTree;
- @synthesize scale = _scale;
- @synthesize source;
- @synthesize parseErrorsAndWarnings;
- #if ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED
- @synthesize nameUsedToInstantiate = _nameUsedToInstantiate;
- static NSMutableDictionary* globalSVGKImageCache;
- #pragma mark - Respond to low-memory warnings by dumping the global static cache
- +(void) initialize
- {
- if( self == [SVGKImage class])
- {
- #if SVGKIT_UIKIT
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarningOrBackgroundNotification:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarningOrBackgroundNotification:) name:UIApplicationDidEnterBackgroundNotification object:nil];
- #endif
- }
- }
- +(void) didReceiveMemoryWarningOrBackgroundNotification:(NSNotification*) notification
- {
- if ([globalSVGKImageCache count] == 0) return;
-
- SVGKitLogWarn(@"[%@] Low-mem or background; purging cache of %lu SVGKImages...", self, (unsigned long)[globalSVGKImageCache count] );
-
- [globalSVGKImageCache removeAllObjects];
- }
- #endif
- #pragma mark - Convenience initializers
- + (SVGKImage *)imageNamed:(NSString *)name inBundle:(NSBundle *)bundle
- {
- #if ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED
- if( globalSVGKImageCache == nil )
- {
- globalSVGKImageCache = [NSMutableDictionary new];
- }
-
- SVGKImageCacheLine* cacheLine = [globalSVGKImageCache valueForKey:name];
- if( cacheLine != nil )
- {
- cacheLine.numberOfInstances ++;
- return cacheLine.mainInstance;
- }
- #endif
-
- SVGKSource *source = [SVGKSourceLocalFile internalSourceAnywhereInBundle:bundle usingName:name];
-
-
- SVGKImage* result = [self imageWithSource:source];
-
- #if ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED
- if( result != nil )
- {
- result->cameFromGlobalCache = TRUE;
- result.nameUsedToInstantiate = name;
-
- SVGKImageCacheLine* newCacheLine = [[SVGKImageCacheLine alloc] init];
- newCacheLine.mainInstance = result;
-
- [globalSVGKImageCache setValue:newCacheLine forKey:name];
- }
- else
- {
- NSLog(@"[%@] WARNING: not caching the output for new SVG image with name = %@, because it failed to load correctly", [self class], name );
- }
- #endif
-
- return result;
- }
- + (SVGKImage *)imageNamed:(NSString *)name
- {
- return [self imageNamed:name inBundle:[NSBundle mainBundle]];
- }
- +(SVGKParser *) imageAsynchronouslyNamed:(NSString *)name onCompletion:(SVGKImageAsynchronousLoadingDelegate)blockCompleted
- {
- return [self imageWithSource:[SVGKSourceLocalFile internalSourceAnywhereInBundleUsingName:name] onCompletion:blockCompleted];
- }
- +(SVGKParser *) imageWithSource:(SVGKSource *)source onCompletion:(SVGKImageAsynchronousLoadingDelegate)blockCompleted
- {
- #if ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED
- if( globalSVGKImageCache == nil )
- {
- globalSVGKImageCache = [NSMutableDictionary new];
- }
-
- SVGKImageCacheLine* cacheLine = [globalSVGKImageCache valueForKey:source.keyForAppleDictionaries];
- if( cacheLine != nil )
- {
- cacheLine.numberOfInstances ++;
-
- blockCompleted( cacheLine.mainInstance, cacheLine.mainInstance.parseErrorsAndWarnings );
- return nil;
- }
- #endif
-
-
-
- SVGKParser* parser = [SVGKParser newParserWithDefaultSVGKParserExtensions:source];
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
- ^{
- SVGKParseResult* parsedSVG = [parser parseSynchronously];
-
- SVGKImage* finalImage = [[SVGKImage alloc] initWithParsedSVG:parsedSVG fromSource:source];
-
- #if ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED
- if( finalImage != nil )
- {
- finalImage->cameFromGlobalCache = TRUE;
- finalImage.nameUsedToInstantiate = source.keyForAppleDictionaries;
-
- SVGKImageCacheLine* newCacheLine = [[SVGKImageCacheLine alloc] init];
- newCacheLine.mainInstance = finalImage;
-
- [globalSVGKImageCache setValue:newCacheLine forKey:source.keyForAppleDictionaries];
- }
- else
- {
- NSLog(@"[%@] WARNING: not caching the output for new SVG image with source = %@, because it failed to load correctly", [self class], source );
- }
- #endif
-
- blockCompleted( finalImage, parsedSVG );
- });
-
- return parser;
- }
- + (SVGKImage*) imageWithContentsOfURL:(NSURL *)url {
- NSParameterAssert(url != nil);
- @synchronized(self) {
- return [[[self class] alloc] initWithContentsOfURL:url];
- }
- }
- + (SVGKImage*) imageWithContentsOfFile:(NSString *)aPath {
- @synchronized(self) {
- return [[[self class] alloc] initWithContentsOfFile:aPath];
- }
- }
- + (SVGKParser*) imageParserWithContentsOfFileAsynchronously:(NSString *)aPath onCompletion:(SVGKImageAsynchronousLoadingDelegate)blockCompleted {
- return [self imageWithSource:[SVGKSourceLocalFile sourceFromFilename:aPath] onCompletion:blockCompleted];
- }
- + (SVGKImage*) imageWithContentsOfFileAsynchronously:(NSString *)aPath onCompletion:(SVGKImageAsynchronousLoadingDelegate)blockCompleted {
- __block SVGKImage *image;
- dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
- [self imageWithSource:[SVGKSourceLocalFile sourceFromFilename:aPath] onCompletion:^(SVGKImage *loadedImage, SVGKParseResult *parseResult) {
- image = loadedImage;
- dispatch_semaphore_signal(semaphore);
- }];
- dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
- return image;
- }
- + (SVGKImage*) imageWithSource:(SVGKSource *)newSource
- {
- NSParameterAssert(newSource != nil);
- @synchronized(self) {
- return [[[self class] alloc] initWithSource:newSource];
- }
- }
- + (SVGKImage*) imageWithData:(NSData *)newNSData
- {
- NSParameterAssert(newNSData != nil);
- @synchronized(self) {
- return [[[self class] alloc] initWithData:newNSData];
- }
- }
- + (SVGKParser*) imageParserWithDataAsynchronously:(NSData *)newNSData onCompletion:(SVGKImageAsynchronousLoadingDelegate)blockCompleted {
- NSParameterAssert(newNSData != nil);
- SVGKitLogWarn(@"Creating an SVG from raw data; this is not recommended: SVG requires knowledge of at least the URL where it came from (as it can contain relative file-links internally). You should use the method [SVGKImage initWithSource:] instead and specify an SVGKSource with more detail" );
- return [self imageWithSource:[SVGKSourceNSData sourceFromData:newNSData URLForRelativeLinks:nil] onCompletion:blockCompleted];
- }
- + (SVGKImage*) imageWithDataAsynchronously:(NSData *)newNSData onCompletion:(SVGKImageAsynchronousLoadingDelegate)blockCompleted
- {
- NSParameterAssert(newNSData != nil);
- SVGKitLogWarn(@"Creating an SVG from raw data; this is not recommended: SVG requires knowledge of at least the URL where it came from (as it can contain relative file-links internally). You should use the method [SVGKImage initWithSource:] instead and specify an SVGKSource with more detail" );
- __block SVGKImage *image;
- dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
- [self imageWithSource:[SVGKSourceNSData sourceFromData:newNSData URLForRelativeLinks:nil] onCompletion:^(SVGKImage *loadedImage, SVGKParseResult *parseResult) {
- image = loadedImage;
- dispatch_semaphore_signal(semaphore);
- }];
- dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
- return image;
- }
- -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
- {
-
- SVGKitLogVerbose(@"[%@] source data changed; de-caching cached data", [self class] );
- self.CALayerTree = nil;
- }
- - (id)initWithParsedSVG:(SVGKParseResult *)parseResult fromSource:(SVGKSource*) parseSource
- {
- self = [super init];
- if (self)
- {
- _internalSizeThatWasSetExplicitlyByUser = CGSizeZero;
- _scale = 0.0;
-
- self.parseErrorsAndWarnings = parseResult;
-
- if( parseErrorsAndWarnings.parsedDocument != nil )
- {
- self.DOMDocument = parseErrorsAndWarnings.parsedDocument;
- self.DOMTree = DOMDocument.rootElement;
- self.source = parseSource;
- }
- else
- {
- self.DOMDocument = nil;
- self.DOMTree = nil;
- }
-
- if ( self.DOMDocument == nil )
- {
- SVGKitLogError(@"[%@] ERROR: failed to init SVGKImage with source = %@, returning nil from init methods. Parser warnings and errors = %@", [self class], parseSource, parseErrorsAndWarnings );
- self = nil;
- }
-
- [self addObserver:self forKeyPath:@"DOMTree.viewport" options:NSKeyValueObservingOptionOld context:nil];
-
- }
- return self;
- }
- - (id)initWithSource:(SVGKSource *)newSource {
- NSAssert( newSource != nil, @"Attempted to init an SVGKImage using a nil SVGKSource");
-
- self = [self initWithParsedSVG:[SVGKParser parseSourceUsingDefaultSVGKParser:newSource] fromSource:newSource];
-
- return self;
- }
- - (id)initWithContentsOfFile:(NSString *)aPath {
- NSParameterAssert(aPath != nil);
-
- return [self initWithSource:[SVGKSourceLocalFile sourceFromFilename:aPath]];
- }
- - (id)initWithContentsOfURL:(NSURL *)url {
- NSParameterAssert(url != nil);
-
- return [self initWithSource:[SVGKSourceURL sourceFromURL:url]];
- }
- - (id)initWithData:(NSData *)data
- {
- NSParameterAssert(data != nil);
-
- SVGKitLogWarn(@"Creating an SVG from raw data; this is not recommended: SVG requires knowledge of at least the URL where it came from (as it can contain relative file-links internally). You should use the method [SVGKImage initWithSource:] instead and specify an SVGKSource with more detail" );
-
- return [self initWithSource:[SVGKSourceNSData sourceFromData:data URLForRelativeLinks:nil]];
- }
- - (void)dealloc
- {
- #if ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED
- if( self->cameFromGlobalCache )
- {
- SVGKImageCacheLine* cacheLine = [globalSVGKImageCache valueForKey:self.nameUsedToInstantiate];
- cacheLine.numberOfInstances --;
-
- if( cacheLine.numberOfInstances < 1 )
- {
- [globalSVGKImageCache removeObjectForKey:self.nameUsedToInstantiate];
- }
- }
- #endif
-
- @try {
- [self removeObserver:self forKeyPath:@"DOMTree.viewport"];
- }
- @catch (NSException *exception) {
- SVGKitLogError(@"Exception removing DOMTree.viewport observer");
- }
-
-
- #if ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED
- #endif
-
- }
- #pragma mark - UIImage methods we reproduce to make it act like a UIImage
- -(BOOL) hasSize
- {
- if( ! CGSizeEqualToSize(CGSizeZero, self.internalSizeThatWasSetExplicitlyByUser ) )
- return true;
-
- if( SVGRectIsInitialized( self.DOMTree.viewport ) )
- return true;
-
- if( SVGRectIsInitialized( self.DOMTree.viewBox ) )
- return true;
-
- return false;
- }
- -(CGSize)size
- {
-
-
-
- if( ! CGSizeEqualToSize(CGSizeZero, self.internalSizeThatWasSetExplicitlyByUser ) )
- {
- return self.internalSizeThatWasSetExplicitlyByUser;
- }
-
-
- if( SVGRectIsInitialized( self.DOMTree.viewport ) )
- {
- return CGSizeFromSVGRect( self.DOMTree.viewport );
- }
-
- SVGRect effectiveViewbox;
- if( ! SVGRectIsInitialized( self.DOMTree.viewBox ) )
- {
-
- NSAssert(FALSE, @"Your SVG file has no internal size, and you have failed to specify a desired size. Therefore, we cannot give you a value for the 'image.size' property - you MUST use an SVG file that has a viewbox property, OR use an SVG file that defines an explicit svg width, OR provide a size of your own choosing (by setting image.size to a value) ... before you call this method" );
- effectiveViewbox = SVGRectUninitialized();
- }
- else
- effectiveViewbox = self.DOMTree.viewBox;
-
-
- return CGSizeFromSVGRect( effectiveViewbox );
- }
- -(void)setSize:(CGSize)newSize
- {
- self.internalSizeThatWasSetExplicitlyByUser = newSize;
-
- if( ! SVGRectIsInitialized(self.DOMTree.viewBox) && !SVGRectIsInitialized( self.DOMTree.viewport ) )
- {
- NSLog(@"[%@] WARNING: you have set an explicit image size, but your SVG file has no explicit width or height AND no viewBox. This means the image will NOT BE SCALED - either add a viewBox to your SVG source file, or add an explicit svg width and height -- or: use the .scale method on this class (SVGKImage) instead to scale by desired amount", [self class]);
- }
-
-
- self.DOMTree.viewport = SVGRectMake(0,0,newSize.width,newSize.height);
-
-
- self.CALayerTree = nil;
- }
- -(void)setScale:(CGFloat)newScale
- {
- NSAssert( self.DOMTree != nil, @"Can't set a scale before you've parsed an SVG file; scale is sometimes illegal, depending on the SVG file itself");
-
- NSAssert( ! SVGRectIsInitialized( self.DOMTree.viewBox ), @"image.scale cannot be set because your SVG has an internal viewbox. To resize this SVG, you must instead call image.size = (a new size) to force the svg to scale itself up or down as appropriate");
-
- _scale = newScale;
-
-
- self.CALayerTree = nil;
- }
- -(UIImage *)UIImage
- {
- #if SVGKIT_MAC
- return [SVGKExporterNSImage exportAsNSImage:self antiAliased:TRUE curveFlatnessFactor:1.0f interpolationQuality:kCGInterpolationDefault];
- #else
- return [SVGKExporterUIImage exportAsUIImage:self antiAliased:TRUE curveFlatnessFactor:1.0f interpolationQuality:kCGInterpolationDefault];
- #endif
- }
- - (void)drawAtPoint:(CGPoint)point
- {
- NSAssert( FALSE, @"Method unsupported / not yet implemented by SVGKit" );
- }
- #pragma mark - unsupported / unimplemented UIImage methods (should add as a feature)
- - (void)drawAtPoint:(CGPoint)point blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha
- {
- NSAssert( FALSE, @"Method unsupported / not yet implemented by SVGKit" );
- }
- - (void)drawInRect:(CGRect)rect
- {
- NSAssert( FALSE, @"Method unsupported / not yet implemented by SVGKit" );
- }
- - (void)drawInRect:(CGRect)rect blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha
- {
- NSAssert( FALSE, @"Method unsupported / not yet implemented by SVGKit" );
- }
- - (void)drawAsPatternInRect:(CGRect)rect
- {
- NSAssert( FALSE, @"Method unsupported / not yet implemented by SVGKit" );
- }
- #if SVGKIT_UIKIT
- + (UIImage *)animatedImageNamed:(NSString *)name duration:(NSTimeInterval)duration
- {
- NSAssert( FALSE, @"Method unsupported / not yet implemented by SVGKit" );
- return nil;
- }
- + (UIImage *)animatedResizableImageNamed:(NSString *)name capInsets:(UIEdgeInsets)capInsets duration:(NSTimeInterval)duration
- {
- NSAssert( FALSE, @"Method unsupported / not yet implemented by SVGKit" );
- return nil;
- }
- + (UIImage *)animatedImageWithImages:(NSArray *)images duration:(NSTimeInterval)duration
- {
- NSAssert( FALSE, @"Method unsupported / not yet implemented by SVGKit" );
- return nil;
- }
- #endif
- #pragma mark - CALayer methods: generate the CALayerTree
- - (CALayer *)layerWithIdentifier:(NSString *)identifier
- {
- return [self layerWithIdentifier:identifier layer:self.CALayerTree];
- }
- - (CALayer *)layerWithIdentifier:(NSString *)identifier layer:(CALayer *)layer {
-
- if ([[layer valueForKey:kSVGElementIdentifier] isEqualToString:identifier]) {
- return layer;
- }
-
- for (CALayer *child in layer.sublayers) {
- CALayer *resultingLayer = [self layerWithIdentifier:identifier layer:child];
-
- if (resultingLayer)
- return resultingLayer;
- }
-
- return nil;
- }
- -(CALayer*) newCopyPositionedAbsoluteLayerWithIdentifier:(NSString *)identifier
- {
- NSAssert( identifier != nil, @"Requested the layer with NIL identifier - your calling method is broken and should check its arguments more carefully");
-
- CALayer* originalLayer = [self layerWithIdentifier:identifier];
-
- if( originalLayer == nil )
- {
- SVGKitLogError(@"[%@] ERROR: requested a clone of CALayer with id = %@, but there is no layer with that identifier in the parsed SVG layer stack", [self class], identifier );
- return nil;
- }
- else
- return [self newCopyPositionedAbsoluteOfLayer:originalLayer];
- }
- -(CALayer*) newCopyPositionedAbsoluteOfLayer:(CALayer *)originalLayer
- {
- return [self newCopyPositionedAbsoluteOfLayer:originalLayer withSubLayers:FALSE];
- }
- -(CALayer*) newCopyPositionedAbsoluteOfLayer:(CALayer *)originalLayer withSubLayers:(BOOL) recursive
- {
-
-
-
- CALayer* clonedLayer = recursive ? [originalLayer cloneRecursively] : [originalLayer cloneShallow];
-
- if( clonedLayer == nil )
- return nil;
- else
- {
-
- NSString* layerID = [originalLayer valueForKey:kSVGElementIdentifier];
- if( layerID != nil )
- [clonedLayer setValue:layerID forKey:kSVGElementIdentifier];
-
-
- CGRect lFrame = clonedLayer.frame;
- CGFloat xOffset = 0.0;
- CGFloat yOffset = 0.0;
- CALayer* currentLayer = originalLayer;
-
- if( currentLayer.superlayer == nil )
- {
- SVGKitLogWarn(@"AWOOGA: layer %@ has no superlayer!", originalLayer );
- }
-
- while( currentLayer.superlayer != nil )
- {
-
-
- currentLayer = currentLayer.superlayer;
-
- xOffset += currentLayer.frame.origin.x;
- yOffset += currentLayer.frame.origin.y;
- }
-
- lFrame.origin = CGPointMake( lFrame.origin.x + xOffset, lFrame.origin.y + yOffset );
- clonedLayer.frame = lFrame;
-
-
- return clonedLayer;
- }
- }
- - (CALayer *)newLayerWithElement:(SVGElement <ConverterSVGToCALayer> *)element
- {
- CALayer *layer = [element newLayer];
-
- layer.hidden = ![self isElementVisible:element];
-
-
-
- NodeList* childNodes = element.childNodes;
- Node* saveParentNode = nil;
-
- if( [element isKindOfClass:[SVGUseElement class]] )
- {
- SVGUseElement* useElement = (SVGUseElement*) element;
- element = (SVGElement <ConverterSVGToCALayer> *)useElement.instanceRoot.correspondingElement;
-
- saveParentNode = element.parentNode;
- element.parentNode = useElement;
- NodeList* nodeList = [[NodeList alloc] init];
- [nodeList.internalArray addObject:element];
- childNodes = nodeList;
- }
- else
- if ( [element isKindOfClass:[SVGSwitchElement class]] )
- {
- childNodes = [(SVGSwitchElement*) element visibleChildNodes];
- }
-
- NSString* clipPath = [element cascadedValueForStylableProperty:@"clip-path" inherit:NO];
- if ( [clipPath hasPrefix:@"url"] )
- {
- NSRange idKeyRange = NSMakeRange(5, clipPath.length - 6);
- NSString* _pathId = [clipPath substringWithRange:idKeyRange];
-
-
-
- NSAssert( element.rootOfCurrentDocumentFragment != nil, @"This SVG shape has a URL clip-path type; it needs to search for that URL (%@) inside its nearest-ancestor <SVG> node, but the rootOfCurrentDocumentFragment reference was nil (suggests the parser failed, or the SVG file is corrupt)", _pathId );
-
- SVGClipPathElement* clipPathElement = (SVGClipPathElement*) [element.rootOfCurrentDocumentFragment getElementById:_pathId];
- NSAssert( clipPathElement != nil, @"This SVG shape has a URL clip-path (%@), but could not find an XML Node with that ID inside the DOM tree (suggests the parser failed, or the SVG file is corrupt)", _pathId );
-
- CALayer *clipLayer = [clipPathElement newLayer];
- for (SVGElement *child in clipPathElement.childNodes )
- {
- if ([child conformsToProtocol:@protocol(ConverterSVGToCALayer)]) {
-
- CALayer *sublayer = [self newLayerWithElement:(SVGElement<ConverterSVGToCALayer> *)child];
-
- if (!sublayer) {
- continue;
- }
-
- [clipLayer addSublayer:sublayer];
- }
- }
-
- [clipPathElement layoutLayer:clipLayer toMaskLayer:layer];
-
- SVGKitLogWarn(@"DOESNT WORK, APPLE's API APPEARS BROKEN???? - About to mask layer frame (%@) with a mask of frame (%@)", NSStringFromCGRect(layer.frame), NSStringFromCGRect(clipLayer.frame));
- layer.mask = clipLayer;
-
- }
-
-
- NSUInteger sublayerCount = 0;
- for (SVGElement *child in childNodes )
- {
- if ([child conformsToProtocol:@protocol(ConverterSVGToCALayer)]) {
-
- CALayer *sublayer = [self newLayerWithElement:(SVGElement<ConverterSVGToCALayer> *)child];
- if (saveParentNode) {
- CGRect lFrame = sublayer.frame;
- lFrame.origin = CGPointMake( lFrame.origin.x + layer.frame.origin.x, lFrame.origin.y + layer.frame.origin.y );
- sublayer.frame = lFrame;
-
- }
- if (!sublayer) {
- continue;
- }
-
- sublayerCount++;
- [layer addSublayer:sublayer];
- }
- }
-
- if (saveParentNode)
- element.parentNode = saveParentNode;
-
- if ( sublayerCount < 1 ) {
- return layer;
- }
-
-
-
- [element layoutLayer:layer];
- [layer setNeedsDisplay];
-
- return layer;
- }
- -(CALayer *)newCALayerTree
- {
- if( self.DOMTree == nil )
- return nil;
- else
- {
- CALayer* newLayerTree = [self newLayerWithElement:self.DOMTree];
-
- if( 0.0f != self.scale )
- {
- NSLog(@"[%@] WARNING: because you specified an image.scale (you SHOULD be using SVG viewbox or <svg width> instead!), we are changing the .anchorPoint and the .affineTransform of the returned CALayerTree. Apple's own libraries are EXTREMELY BUGGY if you hand them layers that have these variables changed (some of Apple's libraries completely ignore them, this is a major Known Bug that Apple hasn't fixed in many years). Proceed at your own risk, and warned!", [self class] );
-
-
- newLayerTree.anchorPoint = CGPointApplyAffineTransform( newLayerTree.anchorPoint, CGAffineTransformMakeScale(1.0f/self.scale, 1.0f/self.scale));
- newLayerTree.affineTransform = CGAffineTransformMakeScale( self.scale, self.scale );
- }
-
- return newLayerTree;
- }
- }
- -(CALayer *)CALayerTree
- {
- if( CALayerTree == nil )
- {
- SVGKitLogInfo(@"[%@] WARNING: no CALayer tree found, creating a new one (will cache it once generated)", [self class] );
- NSDate* startTime = [NSDate date];
- self.CALayerTree = [self newCALayerTree];
-
- SVGKitLogInfo(@"[%@] ...time taken to convert from DOM to fresh CALayers: %2.3f seconds)", [self class], -1.0f * [startTime timeIntervalSinceNow] );
- }
- else
- SVGKitLogVerbose(@"[%@] fetching CALayerTree: re-using cached CALayers (FREE))", [self class] );
-
- return CALayerTree;
- }
- - (void) addSVGLayerTree:(CALayer*) layer withIdentifier:(NSString*) layerID toDictionary:(NSMutableDictionary*) layersByID
- {
-
- [layersByID setValue:layer forKey:layerID];
-
- if ( [layer.sublayers count] < 1 )
- {
- return;
- }
-
- for (CALayer *subLayer in layer.sublayers)
- {
- NSString* subLayerID = [subLayer valueForKey:kSVGElementIdentifier];
-
- if( subLayerID != nil )
- {
- SVGKitLogVerbose(@"[%@] element id: %@ => layer: %@", [self class], subLayerID, subLayer);
-
- [self addSVGLayerTree:subLayer withIdentifier:subLayerID toDictionary:layersByID];
-
- }
- }
- }
- - (NSDictionary*) dictionaryOfLayers
- {
-
- NSMutableDictionary* layersByElementId = [NSMutableDictionary dictionary];
-
- CALayer* rootLayer = self.CALayerTree;
-
- [self addSVGLayerTree:rootLayer withIdentifier:self.DOMTree.identifier toDictionary:layersByElementId];
-
- SVGKitLogVerbose(@"[%@] ROOT element id: %@ => layer: %@", [self class], self.DOMTree.identifier, rootLayer);
-
- return layersByElementId;
- }
- #pragma mark - Useful bonus methods, will probably move to a different class at some point
- -(void) scaleToFitInside:(CGSize) maxSize
- {
- NSAssert( [self hasSize], @"Cannot scale this image because the SVG file has infinite size. Either fix the SVG file, or set an explicit size you want it to be exported at (by calling .size = something on this SVGKImage instance");
-
- float wScale = maxSize.width / self.size.width;
- float hScale = maxSize.height / self.size.height;
-
- float smallestScaleUp = MIN( wScale, hScale );
-
- if( smallestScaleUp < 1.0f )
- smallestScaleUp = MAX( wScale, hScale );
-
- self.size = CGSizeApplyAffineTransform( self.size, CGAffineTransformMakeScale( smallestScaleUp, smallestScaleUp));
- }
- -(BOOL) isElementVisible:(SVGElement *) element
- {
- NSString *display = [element cascadedValueForStylableProperty:@"display" inherit:NO];
- if( [display isEqualToString:@"none"] )
- return NO;
- NSString *visibility = [element cascadedValueForStylableProperty:@"visibility" inherit:NO];
- if( [visibility isEqualToString:@"hidden"] )
- return NO;
- return YES;
- }
- @end
|