{"id":2127,"date":"2012-12-31T18:03:59","date_gmt":"2012-12-31T17:03:59","guid":{"rendered":"http:\/\/t-machine.org\/?p=2127"},"modified":"2013-12-23T15:59:08","modified_gmt":"2013-12-23T14:59:08","slug":"svgkit-2013-development","status":"publish","type":"post","link":"http:\/\/new.t-machine.org\/index.php\/2012\/12\/31\/svgkit-2013-development\/","title":{"rendered":"SVGKit 2013 &#8211; Development"},"content":{"rendered":"<p>SVG is an awesome image format thats widely used, works in all browsers. SVG graphics make better apps and better games &#8211; and automatically &#8220;upgrade&#8221; themselves for future devices.<\/p>\n<p>This post explains the underlying code architecture of <a href=\"https:\/\/github.com\/SVGKit\/SVGKit\/\">SVGKit &#8211; the open-source SVG implementation for iOS\/OS X<\/a>; the target audience is developers who want to help improve SVGKit (adding missing features, fixing bugs, or making it more compliant with the SVG Specification)<\/p>\n<h3>Goals<\/h3>\n<p>Primary goals of the SVGKit project:<\/p>\n<ol>\n<li>100% compliance with the SVG Specification\n<li>Seamless integration with iOS (iPad\/iPhone) and OS X\n<li>Performance <em>better than<\/em> PNG\/JPG\/bitmap graphics\n<li>&#8230;a library good enough that Apple would have liked to have included it in iOS\n<\/ol>\n<p>NB: the license terms for SVGKit are, without prejudice: &#8220;you can do anything you want with this, so long as you give credit to the SVGKit authors for their work&#8221;. Many of us are using it in commercial projects.<\/p>\n<h3>Core structure<\/h3>\n<p>The SVG Specification forces us to split the library into two parts, from the very start:<\/p>\n<ol>\n<li>SVG Spec &#8211; 100% defined by the W3 Consortium\n<li>Native rendering &#8211; approx 10% defined by the W3 Consortium\n<\/ol>\n<p>The SVG Spec does have *some* requirements on the native rendering, and it has a lot of &#8220;guidelines&#8221; &#8211; but on the whole, it&#8217;s undefined, so that we can provide an implementation that makes sense on our platform (iOS\/OS X).<\/p>\n<p>I&#8217;ve divided this up into independent sections:<\/p>\n<ol>\n<li>SVG Spec\n<ol>\n<li>Locating an input stream (e.g. a file, or an HTTP URL)\n<li>XML parsing (low-level)\n<li>DOM parsing from XML\n<li>SVG parsing from DOM\n<\/ol>\n<li>Native rendering &#8211; approx 10% defined by the W3 Consortium\n<ol>\n<li>Conversion from SVG + DOM to SVG data (including: cascading, as per CSS (required by SVG Spec!))\n<li>Dynamic changes to render data, to support Vector Graphics (Apple&#8217;s runtime support for vectors is &#8211; ironically &#8211; weak)\n<li>Export to disk, using the latest copy of your modified DOM\n<li>Export from SVG data to OpenGL (via raw bytes), to Apple&#8217;s (CAlayer\/UIView), and to arbitrary CGContextRef instances\n<\/ol>\n<\/ol>\n<h4>&#8220;Apple&#8217;s support for vector graphics is weak&#8221;<\/h4>\n<p>This was the biggest surprise to me: Apple has spent a decade marketing their OS (Mac \/ OS X) as &#8220;vector based&#8221;, etc.<\/p>\n<p>In practice &#8230; OS X libraries were usually sparsely documented by Apple, and until iPhone came along, they were messy, buggy, poorly designed, and full of &#8220;out of date&#8221; methods. With iPhone OS (now renamed &#8220;iOS&#8221;), Apple cleaned their house out, and made some very lean, clear, logical APIs (with many fewer bugs!). They also &#8211; finally &#8211; documented it all.<\/p>\n<p>That&#8217;s an amazing achievement, it&#8217;s very impressive. But along the way (probably to save time) they ignored some parts. The original iPhone&#8217;s CPU and GPU were very weak (compared to today), so it&#8217;s no surprise that Apple didn&#8217;t update their vector graphics libraries. iOS (as of 2013) is still using the under-documented and flawed OS X classes.<\/p>\n<p>(NB: the lack of documentation also means that very few people know how to use Core Animation\/Quartz\/CALayer for high performance &#8211; you have to &#8220;experiment&#8221; and deduce what Apple *might* be doing, and test extensively. [Incidentally, there&#8217;s a lot of misinformation around &#8211; rumour and theory, in the absence of official docs from Apple])<\/p>\n<p>Find the link for <a href=\"https:\/\/developer.apple.com\/library\/mac\/#documentation\/graphicsimaging\/reference\/CALayer_class\/Introduction\/Introduction.html\">CALayer<\/a> and bookmark it. This core class is where Apple&#8217;s vector libraries and main rendering intersect. It&#8217;s powerful &#8211; but it&#8217;s ugly and bloated too.<\/p>\n<h3>Simple bits, see elsewhere<\/h3>\n<h4>&#8220;Locating an input stream (e.g. a file, or an HTTP URL)&#8221;<\/h4>\n<p>c.f. the SVGKit Usage post. This stuff is very simple, but it&#8217;s lacking features. Would be great for you to add some new SVGKSource subclases, with better features.<\/p>\n<h4>&#8220;XML parsing (low-level)&#8221;<\/h4>\n<p>Currently uses libxml (because that is built-in to iOS, OS X, and Xcode).<\/p>\n<p>This wraps libxml, and adds three features:<\/p>\n<ol>\n<li>Captures every parse-error, and provides a list + line numbers when parsing is finished (libxml doesn&#8217;t have this feature by default)\n<li>Converts low-level libxml C library to high-level ObjectiveC calls\n<li>Provides a &#8220;modular&#8221; parsing system, where parsing code is very simple to write\n<\/ol>\n<p>On the whole, we have NO INTENTION of changing the parser &#8211; it works, and its intended to be as simple as possible. It&#8217;s really just an upgrade to libxml.<\/p>\n<p>But there&#8217;s one thing it&#8217;s missing that we&#8217;d love to add:<\/p>\n<ul>\n<li>Streaming \/ interrupt-based parsing\n<\/ul>\n<p>This is potentially more efficient in CPU and memory usage (not much, since we HAVE to use DOM &#8211; it&#8217;s required by the SVG Spec), but requires making the SVGKParser.m class a bit cleverer.<\/p>\n<h4>&#8220;Export from SVG data to OpenGL\/NSData\/CALayer\/CGContextRef\/etc&#8221;<\/h4>\n<p>Check out the &#8220;Exporters&#8221; sub-folder. It contains simple example classes &#8211; one per exporter &#8211; showing how to efficiently use SVGKImage to help you export stuff.<\/p>\n<p>Note that <strong>approximately half of all SVG files have NO SIZE!<\/strong> &#8211; they are &#8220;infinite&#8221; &#8211; and you want to re-use SVGKImage&#8217;s code for calculating &#8220;correct&#8221;, or &#8220;best guess&#8221; sizes.<\/p>\n<p>Since UIView uses CALayer&#8217;s internally, you can take any CALayer and add it to a UIView ([UIView.layer addSublayer:(CALayer*)myCALayer]).<\/p>\n<h3>Complex parts<\/h3>\n<h4>&#8220;DOM parsing from XML&#8221;<\/h4>\n<p>The way this works is <em>very rigidly defined by the SVG Spec<\/em>, and you absolutely must stick to the Spec.<\/p>\n<p>DOM is a major web standard, and the SVG authors thought it would save everyone a lot of time to re-use it.<\/p>\n<p>Unfortunately &#8211; tragically! &#8211; iOS has no DOM implementation:<\/p>\n<ol>\n<li>Apple has a private implementation available in Safari. It&#8217;s not entirely private (we had to rename one of our classes because of a careless name from Apple), but Apples policy is &#8220;if it&#8217;s not explicitly public, we can reject your app for using it&#8221;. In theory, we could get access to this via WebKit source, or via an embedded WebView. But it would probably be much slower, and use a lot more memory, than our current native implementation\n<li>There are a couple of open-source implementations, most of which have sadly been abandoned by their authors. Also, most of those I looked at are incomplete, and non-compliant; we can&#8217;t afford to rely on them.\n<\/ol>\n<p>The process for adding \/ modifying DOM classes goes like this:<\/p>\n<ol>\n<li>Copy\/paste the DOM official class name (including the capitalization)\n<li>In the header, paste the HTTP link to the *paragraph* of the DOM specification that defines that DOM class\n<li>&#8230;then copy\/paste the DOM&#8217;s interface\/class declaration (usually 5-10 lines of code beginning &#8220;interface&#8221;, and blockquoted)\n<li>Copy\/paste that a second time, this time as the ObjectiveC Interface\n<li>Convert every &#8220;variable&#8221; to an ObjectiveC @property\n<ul>\n<li>Note: by definition, you are supposed to replace DOMString with NSString*\n<\/ul>\n<li>Convert every &#8220;method&#8221; to an ObjectiveC &#8220;-(something) methodSomething:(something);&#8221; method &#8211; NB: do *not* implement as C-methods\n<li>Fill the .m file with @synthesize directives\n<li>Create a blank method in the .m file for each method, and put an &#8220;NSAssert( FALSE, &#8220;Not implemented yet&#8221; );&#8221; in there (or implement it yourself)\n<li>Any other DOM classes that are used as variables or method parameters &#8230; do all the above again\n<\/ol>\n<h5>Hiding NSArray and NSDictionary behind SVG Spec methods<\/h5>\n<p>The SVG Spec is designed to work in ANY programming language &#8211; so it doesn&#8217;t support some core features of ObjectiveC, such as fast enumeration (i.e. the &#8220;for( NSObject* o in array)&#8221; syntax).<\/p>\n<p>A much bigger problem for you is that you can&#8217;t include &#8220;init&#8221; methods, which are necessary for good ObjectiveC code.<\/p>\n<p>Our DOM and SVG classes *must* be spec compliant, so we cannot expose the raw array &#8211; and we can&#8217;t add methods to provide fast enumeration, nor custom init methods.<\/p>\n<p>Instead &#8230; when you have a situation like that, and you want users to be able to (optionally) access them &#8230; go ahead and do it, but put the &#8220;bonus&#8221; methods into a separate header file.<\/p>\n<p>In Xcode, this is called a &#8220;class extension&#8221;, and it&#8217;s a special feature of ObjectiveC. Select &#8220;Class Extension&#8221; when creating the new file.<\/p>\n<p>e.g. look at the source for Nodelist.h and NodeList.m &#8211; and notice that some of the methods are missing from the header file, but appear in NodeList+Mutable.h<\/p>\n<p>In general, you should use the following naming strategies:<\/p>\n<ul>\n<li>If the bonus features are needed to modify properties that SVG Spec says are &#8220;read only&#8221;, name the extension &#8220;Mutable&#8221; to make it clear that&#8217;s what it&#8217;s for\n<li>If the bonus features are ONLY a convenience, e.g. to enable fast enumeration, name the extension &#8220;NotInSpec&#8221; or similar\n<\/ul>\n<h4>&#8220;SVG parsing from DOM&#8221;<\/h4>\n<p>Again, the SVG spec <em>rigorously defines<\/em> the name of every &#8220;SVG&#8221; class, and its methods, and its variables. You must follow these exactly.<\/p>\n<p>The process is identical as for the DOM Spec notes above.<\/p>\n<p><storng>NB: SVG was <em>designed and intended<\/em> to be implemented on-top-of DOM; many of the SVG Spec methods are trivial to implement if you use the DOM methods that already exist. You are not supposed to re-invent the wheel!<\/strong><\/p>\n<p>For instance, have a look at DOMDocument, and Node, and Element &#8211; they have some very useful methods built-in to them.<\/p>\n<p>Remmember: if you call SVGElement&#8217;s init method, then every SVG tag <em>has already been parsed into a DOM Element (which extends DOM Node)<\/em>. It already has all the XML attributes pre-parsed and available to you!<\/p>\n<h5>Gotcha 1: SVG attributes are NOT nil<\/h5>\n<p>SVG Spec defines that &#8220;empty&#8221; or &#8220;missing&#8221; attributes have to be returned NOT AS NULL but as an empty string (&#8220;&#8221;).<\/p>\n<p>This means you must NEVER write:<br \/>\n[objc]<br \/>\nAttr* fillAttribute = [self getAttribute:@&quot;fill&quot;];<br \/>\nif( fillAttribute ) \/\/ DO NOT DO THIS!!!<br \/>\n  &#8230;<br \/>\n[\/objc]<br \/>\n&#8230;because according to the spec fillAttribute can be non-null even though in the SVG it&#8217;s blank. Instead, you must (according to spec) do:<br \/>\n[objc]<br \/>\nAttr* fillAttribute = [self getAttribute:@&quot;fill&quot;];<br \/>\nif( fillAttribute.length &gt; 0 ) \/\/ This is correct, according to SVG Spec<br \/>\n  &#8230;<br \/>\n[\/objc]<\/p>\n<h5>Gotcha 2: XML Namespaces<\/h5>\n<p>You can parse a lot of SVG&#8217;s and ignore namespaces; most SVG&#8217;s use the same &#8220;convention&#8221; for naming the XML tags.<\/p>\n<p>It&#8217;s a convention; it&#8217;s a default; it IS NOT GUARANTEED.<\/p>\n<p>But XML-namespaes are guaranteed. All SVGKit code should be using namespaces explicitly.<\/p>\n<p><strong>As a convenience for users<\/strong>, the DOM spec allows us to provide methods that do NOT need an explicit namespace &#8211; but you should not be using them! They will occasionally fail when used on some input SVG files<\/p>\n<p>So, for instance, you should NOT do this:<br \/>\n[objc]<br \/>\nAttr* fillAttribute = [self getAttribute:@&quot;fill&quot;]; \/\/ DON&#8217;T DO THIS (it&#8217;ll work 99% of the time, but &#8230; best not to)<br \/>\n[\/objc]<br \/>\ninstead do this:<br \/>\n[objc]<br \/>\nAttr* fillAttribute = [self getAttributeNS:svgNamespace localName:@&quot;fill&quot;]; \/\/ CORRECT. (svgNamespace is the HTTP URL of the official SVG Spec)<br \/>\n[\/objc]<br \/>\nAt the moment, we don&#8217;t have a convenience method for &#8220;get the namespace that means SVG&#8221; &#8211; this really should be part of the SVGKparserSVG extension.<\/p>\n<p>NB: if you&#8217;re afraid this namespace stuff won&#8217;t work, note that SVGKparser <em>already has full namespace support, and will automatically create the SVG namespace if needed when parsing incoming SVG files<\/em><\/p>\n<h5>Gotcha 3: Cascading (as in: &#8220;Cascading Style Sheets&#8221; i.e. CSS)<\/h5>\n<p>The SVG spec officially is based on DOM; but it&#8217;s also (officially) based on CSS.<\/p>\n<p>Fortunately, we only have to support a subset of CSS &#8211; the two parts that SVG uses are:<\/p>\n<ol>\n<li>Embedding stylesheets, or referencing them with an external &#8220;link&#8221; tag\n<li>Cascading\n<\/ol>\n<p>But cascading is tricky. There are approximately 50 XML attributes that &#8211; officially &#8211; must be &#8220;cascaded&#8221; when using SVG. There&#8217;s a table of them in the spec &#8211; <a href=\"http:\/\/www.w3.org\/TR\/SVG\/propidx.html\">http:\/\/www.w3.org\/TR\/SVG\/propidx.html<\/a><\/p>\n<p>Cascading is tricky, and it&#8217;s potentially quite slow &#8211; you have to look-up the property in many different places, and check each one &#8220;in correct order&#8221; until you find the first match.<\/p>\n<p>So, we have a method in SVGElement that does all this for you:<br \/>\n[objc]<br \/>\n-(NSString*) cascadedValueForStylableProperty:(NSString*) stylableProperty<br \/>\n[\/objc]<br \/>\n&#8230;but that is not part of the SVG Spec, and it&#8217;s possible it *is* part of the CSS spec, but located in a different class (I haven&#8217;t found it yet). We&#8217;ll leave that method there as a convenience, but you might need to import a special header to access it (since it&#8217;s not part of the SVG Spec).<\/p>\n<p>To use cascading (which you MUST do), instead of this:<br \/>\n[objc]<br \/>\n\/\/ DON&#8217;T DO THIS (it ignores cascading and styles and CSS-classes)<br \/>\nAttr* fillAttribute = [self getAttribute:@&quot;fill&quot;];<br \/>\n[\/objc]<br \/>\n&#8230;do this:<br \/>\n[objc]<br \/>\n\/\/ Automatically does all the CSS stuff for you<br \/>\nNSString* fillAttributeValue = [self cascadedValueForStylableProperty:@&quot;fill&quot;];<br \/>\n[\/objc]<br \/>\n&#8230;eventually, we&#8217;ll add an error \/ NSAssert for cases where you pass in a property that is not one of the cascadeable ones &#8211; for now, just use the table as a reference.<\/p>\n<h3>Class names and method names<\/h3>\n<p>We couldn&#8217;t use a classname prefix of &#8220;SVG&#8221; because the SVG spec reserves all classnames beginning &#8220;SVG&#8221;. Inside the project, you&#8217;ll find all of these in the &#8220;SVG DOM&#8221; folder &#8211; please note: these are MANDATED by the SVG Spec, we did NOT come up with the names.<\/p>\n<p>Apple had a similar problem when they invented GLKit &#8211; the prefix &#8220;GL&#8221; was already used in the OpenGL library they were extending, so they named their classes &#8220;GLK&#8221; prefix. Hence &#8230; &#8220;SVGK&#8221;)<\/p>\n<p>Whenever you create a new class that is not part of the SVG Spec &#8211; for any reason &#8211; you must prefix the name with &#8220;SVGK&#8221;.<\/p>\n<p>Some of our classes &#8211; for historic reasons &#8211; don&#8217;t follow this convention. Yet. Feel free to refactor any you encounter.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>SVG is an awesome image format thats widely used, works in all browsers. SVG graphics make better apps and better games &#8211; and automatically &#8220;upgrade&#8221; themselves for future devices. This post explains the underlying code architecture of SVGKit &#8211; the open-source SVG implementation for iOS\/OS X; the target audience is developers who want to help [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[20],"tags":[],"_links":{"self":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/2127"}],"collection":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/comments?post=2127"}],"version-history":[{"count":3,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/2127\/revisions"}],"predecessor-version":[{"id":3051,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/posts\/2127\/revisions\/3051"}],"wp:attachment":[{"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/media?parent=2127"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/categories?post=2127"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/new.t-machine.org\/index.php\/wp-json\/wp\/v2\/tags?post=2127"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}