SVGKImage.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /*
  2. SVGKImage
  3. The main class in SVGKit - this is the one you'll normally interact with
  4. c.f. SVGKit.h for more info on using SVGKit
  5. What is an SVGKImage?
  6. An SVGKImage is as close to "the SVG version of a UIImage" as we could possibly get. We cannot
  7. subclass UIImage because Apple has defined UIImage as immutable - and SVG images actually change
  8. (each time you zoom in, we want to re-render the SVG as a higher-resolution set of pixels)
  9. We use the exact same method names as UIImage, and try to be literally as identical as possible.
  10. Creating an SVGKImage:
  11. - PREFERRED: use the "imageNamed:" method
  12. - CUSTOM SVGKSource class: use the "initWithSource:" method
  13. - CUSTOM PARSING: Parse using SVGKParser, then send the parse-result to "initWithParsedSVG:"
  14. Data:
  15. - UIImage: not supported yet: will be a cached UIImage that is re-generated on demand. Will enable us to implement an SVGKImageView
  16. that works as a drop-in replacement for UIImageView
  17. - DOMTree: the SVG DOM spec, the root element of a tree of SVGElement subclasses
  18. - CALayerTree: the root element of a tree of CALayer subclasses
  19. - size: as per the UIImage.size, returns a size in Apple Points (i.e. 320 == width of iPhone, irrespective of Retina)
  20. - scale: ??? unknown how we'll define this, but could be useful when doing auto-re-render-on-zoom
  21. - svgWidth: the internal SVGLength used to generate the correct .size
  22. - svgHeight: the internal SVGLength used to generate the correct .size
  23. - rootElement: the SVGSVGElement instance that is the root of the parse SVG tree. Use this to access the full SVG document
  24. */
  25. #import "SVGKDefine.h"
  26. #import "SVGKParser.h" // needed for asynchronous loading method-signature
  27. @class SVGDocument;
  28. @class SVGSVGElement;
  29. @class SVGKSource;
  30. @class SVGKParseResult;
  31. #define ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED 1 // if ENABLED, then ALL instances created with imageNamed: are shared, and are NEVER RELEASED
  32. @class SVGDefsElement;
  33. @class SVGKImage; // needed for typedef below
  34. typedef void (^SVGKImageAsynchronousLoadingDelegate)(SVGKImage* loadedImage, SVGKParseResult* parseResult );
  35. @interface SVGKImage : NSObject // doesn't extend UIImage because Apple made UIImage immutable
  36. {
  37. #if ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED
  38. BOOL cameFromGlobalCache;
  39. #endif
  40. }
  41. /** Generates an image on the fly
  42. NB you can get MUCH BETTER performance using the methods such as exportUIImageAntiAliased and exportNSDataAntiAliased
  43. */
  44. @property (weak, nonatomic, readonly) UIImage* UIImage;
  45. @property (nonatomic, strong, readonly) SVGKSource* source;
  46. @property (nonatomic, strong, readonly) SVGKParseResult* parseErrorsAndWarnings;
  47. @property (nonatomic, strong, readonly) SVGDocument* DOMDocument;
  48. @property (nonatomic, strong, readonly) SVGSVGElement* DOMTree; // needs renaming + (possibly) replacing by DOMDocument
  49. @property (nonatomic, strong, readonly) CALayer* CALayerTree;
  50. #if ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED
  51. @property (nonatomic, strong, readonly) NSString* nameUsedToInstantiate;
  52. #endif
  53. #pragma mark - methods to quick load an SVG as an image
  54. /**
  55. This is the preferred method for loading SVG files.
  56. Like Apple's [UIImage imageNamed:] method, it has a global cache of loaded SVG files to greatly
  57. increase performance. Unlike UIImage, SVGKImage's tend to be light in memory usage, but if needed,
  58. you can disable this at compile-time by setting ENABLE_GLOBAL_IMAGE_CACHE_FOR_SVGKIMAGE_IMAGE_NAMED to 0.
  59. As of SVGKit 1.2.0, this method:
  60. - Finds the SVG file (adding .svg extension if missing) in the App's sandboxed Documents folder
  61. - If that's missing, it finds the same file in the App's Bundle (i.e. the files stored at compile-time by Xcode, and shipped as the app)
  62. - Creates an SVGKSource so that you can later inspect exactly where it found the file
  63. */
  64. + (SVGKImage *)imageNamed:(NSString *)name;
  65. + (SVGKImage *)imageNamed:(NSString *)name withCacheKey:(NSString *)key;
  66. + (SVGKImage *)imageNamed:(NSString *)name inBundle:(NSBundle *)bundle;
  67. + (SVGKImage *)imageNamed:(NSString *)name inBundle:(NSBundle *)bundle withCacheKey:(NSString *)key;
  68. /**
  69. Almost identical to imageNamed: except that it performs the parse in a separate thread.
  70. Returns an SVGKParser object that you can cancel, or inspect for progress (using parser.currentParseRun)
  71. UNLESS the image was already loaded, and a cached version can be returned - in which case,
  72. returns nil and immmediately calls the completion block
  73. */
  74. +(SVGKParser *) imageAsynchronouslyNamed:(NSString *)name onCompletion:(SVGKImageAsynchronousLoadingDelegate) blockCompleted;
  75. + (SVGKImage *)imageWithContentsOfFile:(NSString *)path;
  76. + (SVGKImage *)imageWithContentsOfURL:(NSURL *)url;
  77. + (SVGKParser*) imageParserWithContentsOfFileAsynchronously:(NSString *)aPath onCompletion:(SVGKImageAsynchronousLoadingDelegate)blockCompleted;
  78. + (SVGKImage*) imageWithContentsOfFileAsynchronously:(NSString *)aPath onCompletion:(SVGKImageAsynchronousLoadingDelegate)blockCompleted;
  79. /**
  80. PREFERABLY: these are our only method, apart from the convenience "imageNamed"
  81. Creating an SVG from raw data; this is not recommended: SVG requires knowledge
  82. of at least the URL where it came from (as it can contain relative file-links internally).
  83. If you need to create an SVG e.g. directly from raw bytes, then you MUST use
  84. this method and ADDITIONALLY wrap your data into an SVGKSource.
  85. This is because SVG's cannot parse correctly without the metadata about where
  86. the file came from: e.g. they cannot process relative links, cross-references, etc.
  87. */
  88. +(SVGKImage*) imageWithData:(NSData *)newNSData; // if you have custom source's you want to use
  89. + (SVGKParser*) imageParserWithDataAsynchronously:(NSData *)newNSData onCompletion:(SVGKImageAsynchronousLoadingDelegate)blockCompleted;
  90. + (SVGKImage*) imageWithDataAsynchronously:(NSData *)newNSData onCompletion:(SVGKImageAsynchronousLoadingDelegate)blockCompleted;
  91. /**
  92. PREFERABLY: these are our only method, apart from the convenience "imageNamed"
  93. The first one is synchronous, the second is asynchronous.
  94. If you need to create an SVG e.g. directly from raw bytes, then you MUST use
  95. this method and ADDITIONALLY wrap your data into an SVGKSource.
  96. This is because SVG's cannot parse correctly without the metadata about where
  97. the file came from: e.g. they cannot process relative links, cross-references, etc.
  98. */
  99. +(SVGKImage*) imageWithSource:(SVGKSource *)newSource; // if you have custom source's you want to use
  100. /**
  101. This is the asynchronous version of imageWithSource:
  102. */
  103. +(SVGKParser *) imageWithSource:(SVGKSource *)source onCompletion:(SVGKImageAsynchronousLoadingDelegate)blockCompleted;
  104. - (id)initWithContentsOfFile:(NSString *)path;
  105. - (id)initWithData:(NSData *)data;
  106. #pragma mark - UIImage methods cloned and re-implemented as SVG intelligent methods
  107. /** NB: if an SVG defines no limits to itself - neither a viewbox, nor an <svg width=""> nor an <svg height=""> - and
  108. you have not explicitly given the SVGKImage instance a "user defined size" (by setting .size) ... then there is NO
  109. LEGAL SIZE VALUE for self.size to return, and it WILL ASSERT!
  110. Use this method to double-check, before calling .size, whether it's going to give you a legal value safely
  111. */
  112. -(BOOL) hasSize;
  113. /**
  114. NB: always call "hasSize" before calling this method; some SVG's may have NO DEFINED SIZE, and so
  115. the .size method could return an invalid value (c.f. the hasSize method for details on how to
  116. workaround that issue)
  117. SVG's are infinitely scalable, by definition - but authors can OPTIONALLY set a "preferred size".
  118. Also, we allow you to set an explicit "this is the size I'm going to render at, deal with it" size,
  119. which will OVERRIDE the author's own size (if they configured one), and force the SVG to resize itself
  120. to fit your dictated size.
  121. (NB: this is as per the spec, so it's OK)
  122. NOTE: if you change this property, it will invalidate any cached render-data, and all future
  123. renders will be done at this pixel-size/pixel-resolution
  124. NOTE: when you read the .UIImage property of this class, it generates a bitmap using the
  125. current value of this property (or x2 if retina display) -- and if you've never set the
  126. property, it will use the de-facto value obtained by reading the SVG file and looking for
  127. author-dictated size, etc
  128. */
  129. @property(nonatomic) CGSize size;
  130. /**
  131. TODO: From UIImage. Not needed, I think?
  132. @property(nonatomic,readonly) CIImage *CIImage __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0); // returns underlying CIImage or nil if CGImageRef based
  133. */
  134. // the these draw the image 'right side up' in the usual coordinate system with 'point' being the top-left.
  135. - (void)drawAtPoint:(CGPoint)point; // mode = kCGBlendModeNormal, alpha = 1.0
  136. #pragma mark - unsupported / unimplemented UIImage methods (should add as a feature)
  137. /**
  138. According to SVG Spec, default scale is "1.0", and the correct way to resize/scale an image is by:
  139. 1. setting an explicit "<svg width="..." height="...">"
  140. ...or, alternatively, you can do:
  141. 1. setting an explicit "<svg viewbox="..."
  142. (in which case, we'll use the viewbox width + height as stand-ins for your missing <svg width="" height="")
  143. Either way, you should also do:
  144. 2. set an explicit SVGKImage.size = "..."
  145. However, there are cases where none of those are possible. e.g. because your SVG file is badly written and missing
  146. both of those bits of data. So, to support these situations, we allow you to set a global "scale" that will be applied
  147. to your SVG file *if and only if* it has no explicit viewbox / width+height
  148. */
  149. @property(nonatomic) CGFloat scale;
  150. - (void)drawAtPoint:(CGPoint)point blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
  151. - (void)drawInRect:(CGRect)rect; // mode = kCGBlendModeNormal, alpha = 1.0
  152. - (void)drawInRect:(CGRect)rect blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
  153. - (void)drawAsPatternInRect:(CGRect)rect; // draws the image as a CGPattern
  154. // animated images. When set as UIImageView.image, animation will play in an infinite loop until removed. Drawing will render the first image
  155. #if SVGKIT_UIKIT
  156. + (UIImage *)animatedImageNamed:(NSString *)name duration:(NSTimeInterval)duration ;//__OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0); read sequnce of files with suffix starting at 0 or 1
  157. + (UIImage *)animatedResizableImageNamed:(NSString *)name capInsets:(UIEdgeInsets)capInsets duration:(NSTimeInterval)duration ;//__OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0); // squence of files
  158. + (UIImage *)animatedImageWithImages:(NSArray *)images duration:(NSTimeInterval)duration ;//__OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
  159. #endif
  160. /**
  161. TODO: From UIImage. Not needed, I think?
  162. @property(nonatomic,readonly) NSArray *images __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0); // default is nil for non-animated images
  163. @property(nonatomic,readonly) NSTimeInterval duration __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0); // total duration for all frames. default is 0 for non-animated images
  164. */
  165. #pragma mark ---------end of unsupported items
  166. #pragma mark - core methods for interacting with an SVG image usefully (not from UIImage)
  167. /*! If you want to provide a custom SVGKSource */
  168. - (id)initWithSource:(SVGKSource *)source;
  169. /*! If you already have a parsed SVG, and just want to upgrade it to an SVGKImage
  170. This is the designated initialiser used by all other init methods
  171. NB: this is frequently used if you have to add custom SVGKParserExtensions to parse an
  172. SVG which contains custom tags
  173. */
  174. - (id)initWithParsedSVG:(SVGKParseResult *)parseResult fromSource:(SVGKSource*) parseSource;
  175. /*! Creates a new instance each time you call it. This should ONLY be used if you specifically need to duplicate
  176. the CALayer's (e.g. because you want to render a temporary clone of the CALayers somewhere else on screen,
  177. and you're going to modify them).
  178. For all other use-cases, you should probably use the .CALayerTree property, which is automatically cached between
  179. calls - but MUST NOT be altered!
  180. */
  181. -(CALayer *)newCALayerTree;
  182. /*! uses the current .CALayerTree property to find the layer, recursing down the tree (or creates a new
  183. CALayerTree on demand, and caches it)
  184. i.e. this takes advantage of the cached CALayerTree instance, and also correctly uses the SVG.viewBox info
  185. that was used when generating the original CALayerTree
  186. */
  187. - (CALayer *)layerWithIdentifier:(NSString *)identifier;
  188. /*! uses the current .CALayerTree property to find the layer, recursing down the tree (or creates a new
  189. CALayerTree on demand, and caches it)
  190. i.e. this takes advantage of the cached CALayerTree instance, and also correctly uses the SVG.viewBox info
  191. that was used when generating the original CALayerTree
  192. */
  193. - (CALayer *)layerWithIdentifier:(NSString *)identifier layer:(CALayer *)layer;
  194. /*! As for layerWithIdentifier: but works out the absolute position of the layer,
  195. effectively pulling it out of the layer-tree (the newly created layer has NO SUPERLAYER,
  196. because it no longer needs one)
  197. Useful for extracting individual features from an SVG
  198. WARNING: will assert if you supply a nil identifier string
  199. WARNING: some SVG editors (e.g. Adobe Illustrator) don't bother creating an 'id' attribute for every node (the spec allows this,
  200. but strongly discourages it). Inkscape does the right thing and generates an automatic 'id' for every node. If you are loading
  201. docs that have many 'anonymous' nodes, you'll need to get actual pointer refs to the layers you need to work with, and use the
  202. alternate version of this method.
  203. */
  204. -(CALayer*) newCopyPositionedAbsoluteLayerWithIdentifier:(NSString *)identifier;
  205. /*! As for layerWithIdentifier: but works out the absolute position of the layer,
  206. effectively pulling it out of the layer-tree (the newly created layer has NO SUPERLAYER,
  207. because it no longer needs one)
  208. Useful for extracting individual features from an SVG
  209. Note that this ONLY clones the layer, does NOT include its sublayers. If you want to get a copy that includes
  210. the sublayers, use [self newCopyPositionedAbsoluteOfLayer:withSubLayers:TRUE]
  211. */
  212. -(CALayer*) newCopyPositionedAbsoluteOfLayer:(CALayer *)originalLayer;
  213. /**
  214. As for newCopyPositionedAbsoluteOfLayer:, but allows you to choose between 1 layer only (default)
  215. or a recursive copy which includes all sublayers.
  216. Only the root/parent layer will be positioned absolute - all the sublayers will still be relatively-positioned
  217. within their parents.
  218. */
  219. -(CALayer*) newCopyPositionedAbsoluteOfLayer:(CALayer *)originalLayer withSubLayers:(BOOL) recursive;
  220. /*! returns all the individual CALayer's in the full layer tree, indexed by the SVG identifier of the SVG node that created that layer */
  221. - (NSDictionary*) dictionaryOfLayers;
  222. #pragma mark - Useful bonus methods, will probably move to a different class at some point
  223. /** alters the SVG image's size directly (by changing the viewport) so that it will fit inside the specifed area without stretching or deforming */
  224. -(void) scaleToFitInside:(CGSize) maxSize;
  225. +(void) clearCache;
  226. @end