SVG to CoreGraphics Conversion

*UPDATE – August 2, 2014*

There’s been a LOT of different tools come out in the last several years since I posted this. I’m still seeing a fair amount of traffic showing up here from google, so I thought I’d stick in a little update here with some links to newer apps/tools/converters for generating CoreGraphics code from other file types or graphical editors. I haven’t tried all of these, and I have no connection to their creators, just providing some links.

http://www.paintcodeapp.com

http://drawscri.pt

http://likethought.com/opacity/

I’m sure there’s more, so let me know in the comments if you’ve got another one. Hope this helps, and if you’re still interested in going into the SVG standard a little deeper or in seeing what I did earlier, then read on!

***

I’ve got another tutorial type post for today, but it’s really equal parts: “here’s what I found that helped but didn’t quite work,” “here’s what I did,” and, “anybody have any better ideas?” If you already know something about Core Graphics and why/when to use it and just want the gist of what I did to convert SVG files to Core Graphics calls, go ahead and skip on down to “SVG to the Rescue.” Otherwise, read on to hear about my experience.

Why Core Graphics?

If you’ve spent any time programming for iOS or OSX, you’ve probably been exposed at some level to the Core Graphics frameworks, otherwise known as Quartz. (BTW, if you’re looking for documentation in Xcode, you have to search “Quartz” in Xcode to find the Programming Guide. Searching “Core Graphics” won’t get you anything helpful. A common occurrence with the Xcode documentation browser in my experience, but, that’s a rant for a different day.) The documentation is actually quite good at getting you up and running with the APIs. There’s also some great tutorials in the Graphics and Animation section here from Ray Wenderlich’s site. As a framework, Quartz is the name for the complete 2D drawing engine on iOS and OSX, and it covers everything from drawing to the screen, to working with images and PDFs, to color management, and also includes low-level support for drawing text. It’s resolution and device independent, meaning it’s great for generating interface elements for your iOS apps. No need to manually create multiple versions of each resource – iPhone, iPad, @2x for Retina displays (presumably @2x iPad Retina at some point in the future) – just render the interface at runtime, and as long as you do it right, the OS will handle all the scaling and render everything at the right resolution. It’s also perfect for those times when you need to draw dynamic interfaces that are based on some kind of data or input rather than a static “look” that you design and then display. Although it’s not exactly easy to reverse-engineer the Apple apps and see exactly what they’re doing, it’s safe to say that many of them are rendering directly in app with Core Graphics, rather than loading static images. The WWDC 2011 video, “Practical Drawing for iOS Developers” shows step by step how the Stocks app renders its views, entirely in Quartz. If you’re starting from scratch, the docs, WWDC videos, and tutorials will have you drawing lines, arcs, and basic shapes in no time, all with strokes, fills, and even gradients.

Complex Shapes

The problem I ran into was how to get past just the basics. The API’s for Quartz path drawing go something like this: move to this point, add a line to this point, add a bezier curve to this point with control points at these locations, etc. It’s relatively easy to think about and describe basic geometric shapes in these kind of terms, and Core Graphics even provides convenient methods for creating things like rounded rectangles and ellipses. Even complex views like the stocks app are still very much data/number driven types of views, and even though the drawing process itself is more complicated, it’s not hard to imagine how you would programmatically calculate and describe, say, points on a graph. But, what if you want to draw something a little more organic? What about more complex shapes with lots of curves?
Quarter RestTake this Quarter Rest symbol, for example. As a vector graphic in Illustrator, it contains 3 straight lines and 13 different Bezier curves, each with two control points – and even that is after trying to simplify it as much as possible without losing the desired shape. The problem quickly becomes apparent – it’s virtually impossible to establish a good mental connection between the graphic as conceived by the artist/designer, and the actual code used to produce it on screen. Bret Victor has a great write-up on this artistic disconnect when it comes to dynamic and interactive images/interfaces. It was immediately evident to me that trying to build this graphic in code, line by line – guesstimating and then tweaking the coordinates of the lines, curves and control points – could only end in one way: much swearing and me throwing my computer out the window in frustration.

The main reason I wanted to use Core Graphics rather than static images is to be able to display these musical symbols with dynamic coloring and shadows for some highlight/glow effects. Now, the shadow part of this is actually possible using pre-rendered images. You can set shadow properties like color, radius, distance, on any UIView (technically, on the CALayer of the UIView), including UIImageViews. Quartz will use the alpha values of the pixels to calculate where the edges are, and will generate a nice shadow behind the elements of the image. I say it’s possible, but it’s not actually that practical. Doing it this way requires an extra offscreen rendering pass, and the performance will very likely suffer. In my case, it completely tanked, from somewhere around 53-54 fps with normal content, to around 15 fps when adding a shadow to static images. In some situations, you could work around this by using the shouldRasterize feature of CALayer, but for dynamic content, this could actually make performance even worse. After my experiment, I knew there was no way around it but to keep working on some way to convert my vector images in Illustrator into something I could use in my app. Enter the .svg format.

SVG To the Rescue!

SVG – scalable vector graphics – is a widely used standard for storing, well, just what the name says. Most vector based graphics programs, including Adobe Illustrator, can open SVG, and will also export to SVG. Since SVG is a web standard, one option is to use a UIWebView to render the SVG to the iPhone screen, but that option doesn’t work for me. I googled far and wide for some kind of svg to Quartz converter, and had a bit of luck:

This site was a good start. It provides a resource where you can copy and paste the path data from an svg file, and it will export Quartz source code. I had some weird results from my files, though and a little difficulty figuring out the proper data to paste into the form.

Here’s an Objective-C class, under a Creative Commons Attribution license, which will also take the path data from an svg and output a UIBezier path (iOS) or NSBezier path (OSX).

I also found this library, but as of right now, the site appears to be down. Perhaps just a temporary issue. (UPDATE: Looks like it’s back up, and now that I can see it again, this page is mostly just a link to this on github. A library that takes SVG files and turns them into CAShapeLayers.)

I didn’t test any of these options extensively, but they appear to be good options, especially the second. If they work for you, then great! What they all have in common though is that you first need to extract the path data from the .svg file, meaning, I had to do some research on the standard anyway. Turns out, .svg is just an XML format that you can open in any text editor. And, even better, the svg path commands are very similar to the Core Graphics API’s. Each individual path is contained in a <path> tag, and the specific commands are in the “d” attribute. Here’s the file for that Quarter Rest symbol – open in the browser to see it rendered, or download and open in a text editor and you’ll see the path data clearly separated. The svg standard includes lots of fancy things like fills, strokes, gradients, patterns, masking, even animation, but all I’m using here is a simple, single path. Path commands in svg are single letters, with parameters following.

  • M (x, y) – move to point.
  • C (x1, y1, x2, y2, x, y) – Add cubic bezier curve to point (x, y) with control points (x1, y1 and x2, y2).
  • L (x, y) – add line to point.
  • Z is the command to close the current path.
  • There’s also H, and V for horizontal and vertical lines, and S for curves with an assumed first control point relative to the last command.

Once I got the file into this format, each move was easily converted to Quartz API calls to build a path:

  • CGPathMoveToPoint
  • CGPathAddCurveToPoint (where the point parameters are even in the same order as the SVG command)
  • CGPathAddLineToPoint
  • CGPathCloseSubpath
  • The “H,” “V,” and “S” commands don’t have a Quartz counterpart, so they need to be adapted.

And, here’s the end result of that Quarter Rest symbol, rendered in app with Core Graphics, complete with shadow/glow effect and maintaining nice, snappy frame rates.

Parsing Gotchas

Parsing the svg file by hand turned out to be a little challenging. For one thing, in the interest of keeping file size small, there are almost no separators between items. No whitespace, but also not many commas or other delimiters, wherever it can be omitted. For example, a “move” command, followed by an “add curve” command might look something like this: “M60.482,613.46c0,0-17.859,0.518-26.997,0” Each place there’s a negative number, the separating comma is eliminated as unnecessary, and each command runs right into the next one, so it’s important to know that the parameters for each command come after the letter. Also, when the same command is used multiple times in a row, it’s possible according to the standard to omit the command the second time. So, a “c” followed by 12 numbers is actually two separate curve commands. One other catch: each of these svg commands is using absolute coordinates, but most of them also have a corresponding command using relative coordinates. These use the same single letters, but in lower case. For example, M 10 10 m 5 5  means move to absolute point (10, 10) and then move to (5, 5) relative to this point – so (15, 15) absolute. Unfortunately, Illustrator exports svg files using mostly these relative commands, so I also needed to convert them to absolute point values for Quartz.

You’re Still Reading? Wow!

OK, this was a long post, so if you’ve read this far, that means you’ve got some actual interest in the subject and/or need for an efficient way to get path data into Core Graphics. So here’s the part where you’re hoping I have the link to some parsing code or maybe a script I wrote that will do this work for you. But, I have to confess, I did all mine by hand. I only had a handful of symbols I needed for this project, each of which is a pretty simple, single path, so I did the parsing and converting by hand along with some rounding and cleaning up along the way. Maybe next time I’ll get it automated, but for now, it was a good exercise in exploring a new file format and diving deeper into Core Graphics. But, if you’ve given some of this a try yourself, I’d love to hear what you came up with! Did any of these other resources work well for you? Got any nice scripts to share with everyone else? Or, perhaps even more likely, am I way off track? Is there an even better workflow for getting from design in a visual editor to Quartz source code? If so, I’d be grateful to hear your ideas.

BONUS: of course, just as I’m finishing this post, I stumbled across yet another resource. From the looks of it, it might be the best yet. It’s an Objective-C parser that will take a portion of an Illustrator eps file and convert it to CGPath. Guess I’ll have to try that out now too! Method for Interpreting Illustrator Art Assets as Cocoa CGPathRef

37 thoughts on “SVG to CoreGraphics Conversion

  1. Hi Ryan,

    This is Jeff (I wrote the EPS parser that was linked at the end of your post.)

    Before I wrote that, I looked far and wide for an easy peasy, straightforward way to deal with vector art assets in Cocoa. It seemed like most everyone was taking the SVG angle. And when I was looking around most of the solutions that used SVG either weren’t a good fit or I had trouble understanding how they worked.

    I used EPS since I have a background in print production (I’ve had to edit raw PostScript data by hand), and it’s pretty straightforward.

    When I get time (ha!) I’d like to improve the parser to include handling multiple path objects and maybe even appearance data.

    Anyway, interesting post and nice blog. I was reading about your being featured on the App Store. Well done!

    1. Hey Jeff,

      Thanks for sharing the EPS parser. If I ever get time :-), I may use your example as a starting place for doing an SVG parser! Also, glad to know I’m not who only one who has had trouble finding resources to do this. Like you said, even some of the ones I did find may have worked, but either weren’t a good fit or I just didn’t understand how to make them work. By the time I figured out enough about the SVG format, I decided to just roll my own conversions by hand this time. Thanks for the feedback, and good luck to you on the App Store!

  2. Hi, I have written an SVG to CoreGraphics parser a few years ago, it was part of an abstraction interface that could export to multiple graphics formats from a single API. It was used in several production systems by well known publishers. Feel free to contact me, perhaps there’s a way to turn all of this into a nice open source library.

  3. I tried the various parsers you linked to but none of them were willing to read the SVG files that Adobe Illustrator CS5 creates, even though I tried all the possible SVG format options I could think of, including “tiny,” for example.

    The closest I got was some kind of object that seems to have at least half of its points in the top left corner of the view.

    oh well. back to the drawing board I guess.

    1. Hmm, yeah I didn’t try all of them extensively before I linked them. Just wanted to collect some different resources I had come across. I know some of them do not actually parse the entire SVG file but some of them required you to first manually extract the path data from the file and paste/input that into the parser. So, that may have been part of the problem. Good luck!

  4. well, here’s an example bit of SVG code… could you see if you can get this to work with anything?

    Otherwise i’m sad that I’d have to write all the parsing code myself, which does seem crazy to have to do.

    -Dave

  5. oh well it didn’t paste the code snippet… let me know when you have a code snippet display plugin installed… or if you have one, what’s the escape command…?

    1. Yeah, I don’t have one installed. I’ve looked into it, but as far as I can tell, the basic WordPress hosted blogs don’t allow plugin installation. If I move to a different hosting setup, then I will definitely get a code snippet plugin installed.

      1. I was mostly just linking to these different resources as a way of collecting some different items in one place. I haven’t tried/downloaded all of them and am not familiar with exactly how they work. So, while I can’t say for sure whether any of the parsers will be able to handle your particular svg file, I did take a look at your example. I don’t know for sure if this is the problem, but I noticed a few things. First, your example file is made up of a few different paths – it may be that the parsers here are only designed to work correctly with files that have one path. The second thing is that your example also uses the svg “polygon” tag rather than just “path” tags and data. There’s no equivalent to this in Core Graphics, so maybe the parsers just aren’t set up to convert that. Again, I don’t know the exact way these parsers work, so you’ll have to keep experimenting to find out where your problem is, but those are just the first things I noticed when looking at your example.

      2. yeah i was kind of suspicious of the ‘polygon’ tag… i wish apple would work on supporting more standards like svg in core graphics… what’s the open gl es side look like? can things be more easily brought in from standard off the shelf software like blender?

      3. I’ve never used opengl before, so I really have no idea. I would imagine there are ways to get a Blender file turned into point data that can be used by/in opengl.

  6. Spot on with this write-up, I really feel this website needs a great deal more attention. I’ll probably be back again to see more, thanks for the info!

  7. I’m a bit late to the party, but this article put me on the right path to getting my SVG data converted into Quartz drawing commands.

    To improve on the “by hand” method a bit, I offer the following suggestion.

    1. Open up your SVG file in Inkscape (http://inkscape.org/).
    2. Open “File -> Inkscape Preferences… -> SVG output” and UNCHECK “Allow relative coordinates”.
    3. Save your SVG document as a new file (this is important, because Inkscape doesn’t always translate objects to absolute coordinates when saving an existing document).

    This will give you an SVG with all coordinates translated to absolute, making it extremely easy to then write the corresponding Quartz drawing commands and plug in the coordinates from the SVG.

    I realize there are now a couple apps available that can create images and save them as Quartz drawing commands (Opacity, Paint Code), but I’ve yet to see anything that will open an existing SVG and let you save parts of that as Quartz commands. I’m hoping my suggestion here is helpful to anyone who is trying to convert pre-existing SVG assets to Quartz.

    1. Brilliant idea! I looked around for some way to force Illustrator to output absolute coordinates, but couldn’t find anything. I hadn’t thought of trying something like Inkscape. Thanks for the tip!

  8. My solution for a coloring game I’m working on was to write a simplistic parser for a subset of the SVG standard and parse the path data using the code from PocketSVG. It required minimal modification to output CGPaths instead of UIBezierPaths. I wrote the XML parser using the TBXML library. I only feed it simple SVG files with nothing but groups of paths, but it works like a charm.

    1. Thanks for the tip no saw that recently but forgot to write down what it was. Maybe I should do a follow up post am summarize some of these other options that have come out since I wrote the original post.

  9. I’m amazed, I have to admit. Seldom do I encounter a blog that’s both equally educative and amusing, and let me tell you, you’ve hit the nail on the head. The issue is something not enough men and women are speaking intelligently about. I am very happy that I stumbled across this during my hunt for something regarding this.

  10. Hey there, I recently found your internet site by way of Google at the same time buying equivalent theme, your internet site got here way up, it looks excellent. I’ve book-marked so that you can favourites features|added onto my own book marking.

  11. There is a program on the mac app store called iDraw – if you open/create a vector image on there, select what you want and click edit>copy as>core graphics code…. bingo!

    1. Thanks for the tip. There’s been a lot of new products come out in the last couple years since this post. I should probably put an update at the top, since quite a few people keep ending up at this blog post!

  12. I’ve been browsing on-line more than three hours today, yet I
    never discovered any interesting article like yours.
    It is beautiful price enough for me. Personally, if
    all website owners and bloggers made excellpent content material as you did, the internet shall be much more useful
    than ever before.

    1. I’m just recently getting into writing some Swift, so I’m not sure actually. Sorry, but much help! If I come across something I’ll probably do another update to this post, or a new post.

  13. Hey Ryan, it’s me again.

    I’m currently drawing some SVGs to place on cells in a UICollectionView in my app, and I was wondering if you had any tips on sizing. Right now, I’m setting the SVG to be 50×50 mm (arbitrary, as long as it’s square), and using a tool called PocketSVG to render the SVG image on top of the cell. It works, kind of…the main issue is positioning of the image. Centering it in code does not actually center the image….fiddling with width/height of the image in my code gives weird, inconsistent results. All I want is for the image to fit inside the square cell, but it rarely actually works out that way. The image is always off to the side, stretched, squished, whatever…

    Did you do anything special when drawing your SVGs? I’d like to know if I have to do any centering/sizing/positioning up front in Inkscape before I import them into my app.

    Thanks,

    Kevin

Leave a reply to rdsquared Cancel reply