Categories
programming

SVGKit 2013 – Usage

SVG is an awesome image format thats widely used, works in all browsers. SVG graphics make better apps and better games – and automatically “upgrade” themselves for future devices.

This post explains how you can use SVGKit – the open-source SVG implementation for iOS/OS X in your own apps

(NB: basic installation is covered on the GitHub page – this is about how you *use* it)

SVGKit is like Apple’s UIKit

Originally, SVGKit was a bit tricky to use. I re-architected it to be an exact clone of Apple’s UIKit – same classes, same class structure, same method names.

So, we have:

UIKit SVGKit Notes
UI* SVGK* Apple’s standard: library names start with abbreviation of the library name
UIImage SVGKImage SVGKImage.h started as a copy/paste of UIImage.h
UIImageView SVGKImageView You cannot instantiate it directly, have to use a subclass
SVGKFastImageView Renders your SVG as a single layer, fast.
SVGKLayeredImageView Renders your SVG one CALayer per SVG element; you can hilight individual elements, tap them, animatet hem, etc

(a side note: We couldn’t use a classname prefix of “SVG” because the SVG spec reserves all classnames beginning “SVG”. Apple had a similar problem when they invented GLKit – the prefix “GL” was already used in the OpenGL library they were extending, so they named their classes “GLK” prefix. Hence … “SVGK”)

Loading an image

You already know how to load a UIImage, the “easy way”:
[objc]
UIImage* newImage = [UIImage imageNamed:@"myImage.png"];
[/objc]
Well, SVGKit is … the same:
[objc]
SVGKImage* newImage = [SVGKImage imageNamed:@"myImage.svg"];
[/objc]

Displaying an image: UIImageView

And how do you create a UIImageView?
[objc]
UIImageView* imageView = [[UIImageView alloc] initWithImage:newImage];
[/objc]
…so, for SVGKit:
[objc]
SVGKImageView* imageView = [[SVGKFastImageView alloc] initWithImage:newImage];
// ….. or:
SVGKImageView* imageView = [[SVGKLayeredImageView alloc] initWithImage:newImage];
[/objc]
Wait – why is this different?

The reference in both cases is an SVGKImageView – but you cannot alloc-init that class directly. Why?

Different people use the library in different ways. Some people need performance, others need detailed aniamtion. SVG’s contain a lot of bonus info, and it’s not possible to support every use case with a single class. So, you get to choose which one fits your needs

Advanced uses

So far, so easy. What about when you want more control? And how do you load an SVG over the internet (can you load an SVG directly from the web?)

Time to delve a little deeper…

Loading an SVG: in detail

There are four steps to loading an SVG (all of which are done automatically by SVGKImage above):

  1. SVGKSource: specify a location (a file, a filename – or an HTTP URL)
  2. SVGKParser: parse the file (all SVG’s are XML files, so your file can contain any custom XML you want)
  3. SVG classes: convert to SVG’s custom classes (from the SVG Spec); includes automatic support for CSS styling, cascades, etc
  4. SVGKImage: export the SVG to something Apple can render: e.g. a flat CALayer, or a hierarchy of CALayers, etc
  5. Experimental Exporter that saves a .svg file from your SVG file, including any changes you made to the in-memory DOM

Every stage is extensible, so you can either add features, or custommize it

Extend SVGKSource: adding new sources

Easy: create a new subclass of SVGKSource, and implement the two methods.

The method signatures are a little strange, because they have to support fast file-access (a C native library from Apple) as well as modern Objective-C methods.

One example of each: the supplied SVGKSource for loading from disk uses C methods, and the supplied one for loading from a URL uses Objective-C methods.

Extend SVGKParser: custom SVG files and formats

NOTE: do NOT subclass SVGKParser; parsing is very complex, and there’s a special mechanism that makes it easy to extend the parser (read on…)

Parsing is complex, so our parser is modular (based on ideas from a modular XML parser I wrote years ago). The SVGKParser class doesn’t parse SVG directly: instead, it parses raw XML, and converts it to a higher-level version that’s much faster to work with. It also manages error handling, loading bytes from an SVGKSource, etc.

You create a new parser instance with a helper method that includes all the modules you need by default:
[objc]
SVGKParser* parser = [SVGKParser parserWithDefaultParseExtensions];
[/objc]

SVGKParserExtension: adding your own custom parsing

Our SVG implementation is already split into sub-modules. This makes the code easier to maintain – and it makes it much easier to add support for SVG features one-by-one, in parallel with other developers.

Example classes:

  • SVGKParserSVG: parses “Basic” SVG – obvious things that require no special handling. NB: this is the most complex parser extension we have. Most are much simpler
  • SVGKParserGradients: parses SVG Gradients – because these were added later by a different developer (Stich) … likewise, if you’re adding a msising SVG feature, feel free to make a new parser-extension for it, it’s easier!
  • SVGKParserDefsAndUse: the SVG <defs> and <use> tags are much harder to parse than 99% of SVG, since they require complex cross-referencing and instancing. So, we use a separate parser extension to isolate this code.

For one of my games, I wanted to store gameplay data inside the SVG – attach info to particular SVG tags (e.g. give each SVG path a “bonusPoints” attribute).

To do this in a spec-compliant way, you should:

  1. Create a new XML namespace (requires no code: you simply invent a URL on a domain that you own)
  2. Put the namespace in your SVG file, using an <xmlns> tag, and give it a “prefix”, e.g. “my-game”
  3. Everywhere you want your custom attributes, or custom tags, prefix them, e.g.: “Write an SVGKParserExtension, and in the “supportedTags” and “supportedNamespaces” methods, return ONLY your namespace, and either nil, or the set of tags you’ve invented

Once the file is parsed, use the DOM to fetch your data (by definition, SVG parsers are required to be DOM-compliant parsers too)

e.g.:
[objc]
NodeList* myCustomNodes = [svgImage.DOMDocument getElementsByTagNameNS:@"http://my.custom.namespace" localName:@"my-custom-tag"];
[/objc]
(NB: NodeList is defined by the DOM, and is just a wrapper for an NSArray. If you #import “NodeList+Mutable.h”, you can access the NSArray directly, and use ObjectiveC fast enumeration. This is cheating, it’s not in the core SVG spec, which is why it’s hidden inside a separate header file)

Extend SVG Classes: the core SVG Spec

Don’t.

(the only valid reason for doing this is if you find features in the spec that are missing or broken in SVGKit. All other usages, you should be able to do in some other way, more easily, with less risk of your changes being broken when we upgrade SVGKit)

Extend SVGKImage export: export your SVG into custom rendering or to a new file on disk

All the parsed data for the SVGKImage is available to you directly, for exporting:

  • (SVGKImage* image).DOMTree : this property contains the entire parsed SVG-DOM (all the SVG* classes)
  • (SVGKImage* image).CALayerTree : this property contains the SVG converted into Apple’s CALayer classes, renderable directly in OS X and iOS
  • Additionally: “SVGKImage+CGContext.h” has methods to render the SVG into any CGContextRef you supply (or will autocreate one for you)
  • SVGKExporterNSData : creates NSData raw bytes, efficient and fast (good for OpenGL)
  • SVGKExporterUIImage : creates a UIImage instance, which you can use anywhere in UIKit

To save memory and CPU, the “CALayerTree” property isn’t created until you request it; if you don’t want the CALayer’s, they’ll never get created.

Improving SVGKit, fixing bugs, and ultra-advanced development

But SVGKit isn’t perfect: there are still missing features, bugs, etc.\

If you find a bug but can’t understand / fix it, please create a simple-as-possible SVG file and send it to us (create an Issue on the GitHub page, and include a link to the file). If you give us permission, we’ll add it to the suite of “test” images we use to verify each version of SVGKit, and that way it will always work, even with future versions.

If you fix bugs, or have bits you want to fix, please have a look at the SVGKit Development guide.

If you want support or help with SVGKit, the very best thing to do is create an Issue on the SVGKit page. That way, any of the contributors can see your problem and help you out. If you email me directly for help, you’re less likely to get a response (I’m busy, and I work on SVGKit entirely unpaid).

32 replies on “SVGKit 2013 – Usage”

I imagine you can use this from inside Cocos2d yes – but you will have to ask the cocos2d authors how to do that, I’m afraid. None of us use Cocos2d.

@Rock

Google “svg trace” if you want to convert images into SVG.

SVGKit is for drawing SVG’s, not for image-conversion, sorry.

If you could work out how to integrate that with SVGKit, that would be awesome. I didn’t write the parser myself, and I don’t know how much needs to be replaced. All I know is that it’s self-contained inside one file right now, with an interface that’s called by something like 3-4 external files.

i.e. it might be very easy to replace?

Also, bearing in mind SVGKit has to parse *all* variants of *all* svg paths. Different kinds of shape, and different modes (relative versus absolute) etc. The parsing code at the moment is hundreds of lines long to cope with all the different SVG definitions and make sure we don’t lose the data (since we need to know if it was a line, a polygon, closed or open, etc etc).

Sadly it’s not enough to simply return a CGPath object – since Apple’s CGPath API is missing a lot of the data (Apple throws it all away :( ).

@ben – yes, I tried to keep to exact same method names as Apple UIimage, but initwith.. is referring to a class name, so it had to be SVGKImage to be correct.

(Similary, cant be SVGImage because thats reserved by the SVG spec.

Hi guys, i just downloaded the branch version (1.1.0) and added a SVG file into the demo project. The SVG file was created by saving an AI file with a lot of layers as SVG. The problem is that it takes around 10-13 seconds to display it, and when zooming in/out, the app gets blocked for a couple of seconds. Same thing happens for “Location_European_nations” map inside the demo project. Any idea why? Is performance a known issue for maps with many layers?

Thank you for your time!

Impossible to answer without more info. All I can say is: the demo works fine on normal hardware.

@Adam thanks for your answer.

I am using an ipad3 (ios 7), and the time to load “Location_European_nation_states” map for example is around 10-12 seconds…do you also get this time for it? I think it is a little bit too much. Also, if I set the zoom to maximum value 2, the app blocks for about 5 seconds after zooming in/out(I am talking about the same map, european nation states). I also tested on an ipad mini(ios 6), and I have the same issues.

Strange is that on an older version of the SVGKit (https://github.com/reklis/SVGKit) the map loads much faster (about 2 seconds to load the entire map). I

I am not sure what details I should give you more. Can you tell me for this map mentioned above what times do you get?

Thanks,
Adina

Sounds to me like a bug in iOS 7.

iOS 7 is EDIT: “just as buggy as any new OS release” – Apple shipped two emergency updates last week, one of which changed two apps of mine from “hard crashing” into “working perfectly”.

So … I suggest you check with iOS 6 (which is stable) and/or use Xcode’s profiling tools to find out where the time is going. I’m not doing iOS 7 development yet (I’m waiting for Apple to fix the worst bugs), so I can’t check myself, sorry.

Hi, I’m now developing graphic service which needs SVG. There seems not to have a way to export or save the edited svg images back to svg images or pdf in iOS, as I know. Do you know any ways to do it?

Oh, Thanks!!!…But I didn’t have it from you If you sent me about it by email, please send me again….Maybe I deleted it uncounsciously..

Almost every SVGKit class that begins “SVG” (instead of SVGK) is named exactly the same as the XML tag you have to output for that class. SVGKit implements the DOM, so you might be able to write an exporter that does what you need very quickly – perhaps 20 minutes or less.

Google “export DOM to XML” if you’re stuck.

Hello, I have just downloaded the 2013 version from here: https://github.com/SVGKit/SVGKit/tree/2013base

I have opened “Demo-iOS.xcodeproj” in Xcode 5. I then attempt to ‘Run’, and I get the following error:

“/Downloads/SVGKit-2013base/Source/DOM classes/Unported or Partial DOM/SVGRectElement.m:9:6: Conflicting types for ‘CGPathAddRoundedRect'”

@Tom, I got the very same conflicting problem: Conflicting types for ‘CGPathAddRoundedRect’” with the latest version of SVGKit and really appreciate it that you’ve pasted the solution here.

I had another old project that was using an older version of SVGKit and I wasn’t able to upgrade it to use SVGKit 2013, so I had to change the method name from CGPathAddRoundedRect to CGPathAddRoundedRectx and it continued to work after that…. I’ve got that from the following stackoverflow post: http://stackoverflow.com/a/4705929/500484

This was patched in the main branch a few weeks ago – if you get that error, you’re not using the current version.

Hello, is there a way to somehow export somehow CALayer’s path so that I can just reuse them later without adding SVGKit to the app? I really only need to display one complex SVG image, so loading it once would be enough..

I hope the question makes sense :)

@Steve that’s a question about Apple’s CoreAnimation, not SVGKit. You can serialize CGPath objects, but it takes a lot of work – Apple doesn’t make it easy.

I don’t see the problem with including SVGKit – it’s just a library?

@adam, thank you for your answer! The concern about including SVGKIT when it is just to show one image looks like an overkill.. However now after playing with it and seeing how awesome it is, I am more concerned about switching the whole image-rendering part to SVGKIT and ditching the .png-based UIIMages whatsoever ))

SVGZ is a GZIP’d SVG file. UnGZIP it, and open it with SVGKit. Should work perfectly.

(Gzip is built-in to every mainstream programming language/platform today, AFAIAA)

Hi Adam,
I want draw directly from file extension “.svg” from memory to display through SVGKImageView, but no use SVGKImage to improve performance.
But I can’t make it. Can you give me solution about above questions?.
Thank you very much.

Hi Adam,

I’m a bit ashamed of asking that, but I’ve been unable to make SVGKit work in my code. Everythings build and links correctly, but following the simple steps of creating a SVGKImage and then feeding it to an SVGKFastImageView that I set up in a XIB file just doesn’t work. I get nothing, as if the image was empty. I’ve tried multiple things such as grabbing the UIImage and feed it to an UIImageView, or trying to use an SVGKLayeredImageView, but to no avail.

I’d like to ask more precisely a few things:
– Is there any problem with using SVGKImageView (any flavor) in a XIB? I guess not as I saw the example precisely do that, but could there be precise steps/configuration needed to make this work? ()
– Also, I’m trying to display an SVG image in a toolbar which is a UICollectionView. Could there be issues with the “reusable cells” mechanism? I’m thinking more precisely about repeated setImage on a SVGKImageView, as I saw in the demo that you allocated a new SVGKImageView each time the image changed.

Sorry for the wall’o text, hope you can help, and thank you for the lib and your time!

Use the demo project – that’s what it’s there for!

Insert your image, check it renders. If not, narrow it down.

If it does, then yes it’s something odd about your context where you’re rendering.

I want to use SVGKit to show a SVG file and access all the individual layers (images) for example to enlarge a layer, rotate or rearrange it withing the SVG. Can we do this ? please let me know.

Thank you in advance !

All the info is there in the project and documentation on the project page.

Comments are closed.