Compare commits

..

83 Commits

Author SHA1 Message Date
Curtis Hard 68393b296b Faster low level parsing for paths 2021-01-25 19:29:53 +00:00
Curtis Hard 1cfcb596d9 Much faster command parsing
tl;dr, stops converting between char*/unichar* and NSString*, the low level code for passing commands wants char* so no point of converting it to a higher level NSString* just to convert it back again, waste of time.
2021-01-24 22:18:07 +00:00
Curtis Hard 69077e49cf Faster parsing for very large SVGs 2021-01-24 19:16:10 +00:00
Curtis Hard 3bdf2151ca Rewrote how we parse transforms (its much faster)
Also much faster arc transform processsing
2021-01-23 18:58:39 +00:00
Curtis Hard f7e28a2962 Added originalType for future reference 2021-01-20 21:39:10 +00:00
Curtis Hard 2c07cfabdd Added more units and neatened up methods 2021-01-20 21:19:28 +00:00
Curtis Hard 3e356b3fdc Possible fix for CM and MM units 2021-01-20 21:00:15 +00:00
Curtis Hard 49f759edc0 Added extra header in 2021-01-10 16:55:28 +00:00
Curtis Hard 38e314eb99 Cant use description as its already a thing on NSObject. #oops 2020-11-22 18:50:09 +00:00
Curtis Hard d0eb015cf1 Added title and desc parsing into the IJSVGNode 2020-11-22 18:14:04 +00:00
Curtis Hard 080626a022 Removed warning 2020-11-14 16:44:13 +00:00
Curtis Hard 36c20bc55c Updated clang warning 2020-11-13 20:01:37 +00:00
Curtis Hard 4d89631e9a Brace in the wrong place! #oops 2020-09-21 20:16:53 +01:00
Curtis Hard 65f62007f3 Fixes exception being thrown and lower cased move command 2020-09-21 19:55:31 +01:00
Curtis Hard af5f16288d Updated opyright 2020-09-21 09:58:38 +01:00
Curtis Hard 08e0f9288d Adds support to remove default attributes from exported files 2020-09-16 18:40:05 +01:00
Curtis Hard 7650f6205c More improvements to exporting data 2020-09-13 20:36:29 +01:00
Curtis Hard 263768af5b Initial compression changes 2020-09-12 22:59:54 +01:00
Curtis Hard 4b1be17f9a Reduces exported data by a fair amount on larger SVGs by using NSXMLNodeCompactEmptyElement to self close tags
- also added option to get back an IJSVG object from the current export string
2020-09-08 18:57:20 +01:00
Curtis Hard 6b0d6b1452 This adds in floating point export options
- aswell as an option to round matrix values
2020-09-03 19:37:37 +01:00
Curtis Hard f80d4145bb Default rounds numbers, but not for transforms 2020-08-14 20:48:33 +01:00
Curtis Hard f68c83285b Fixes issuew here stroke-opacity was not exported 2020-08-14 20:13:53 +01:00
Curtis Hard 1f4bd989d8 Fixes RX and RY on rect 2020-08-13 12:10:49 +01:00
Curtis Hard 5b5d0b738b Added instruction passing for c -> s + t 2020-08-04 20:58:05 +01:00
Curtis Hard bd82c5d81b Only add the xlink namespace attribute on if required 2020-07-31 18:27:16 +01:00
Curtis Hard 145bbb17f8 Adds xml declaration removal to options 2020-07-29 20:13:01 +01:00
Curtis Hard d9f40551a4 Added ijsvg_isHexString for faster hex comparison 2020-07-27 12:06:03 +01:00
Curtis Hard 4448f6aeaf Small perf increase 2020-07-12 14:36:12 +01:00
Curtis Hard 6834abba19 Typo fix 2020-07-11 11:56:42 +01:00
Curtis Hard a86b4e80ba Refacator name 2020-07-09 16:32:20 +01:00
Curtis Hard 2e8d039599 Memory reductions 2020-06-27 19:00:38 +01:00
Curtis Hard 4ba6bb776d Xcode stuff 2020-06-27 12:28:35 +01:00
Curtis Hard 9c80412e88 Various image additions 2020-06-15 09:25:53 +01:00
Curtis Hard 509f0e0b0a Only allow removal of 0 if the float contains a floating point 2020-06-05 13:34:44 +01:00
Curtis Hard ce30877a26 Xcode stuff 2020-05-25 19:58:48 +01:00
Curtis Hard 122271bf36 Fixes for layer tree masking 2020-04-18 17:16:29 +01:00
Curtis Hard 28b4c6b85c Fixes issue with intrinsicSize size and dom being discarded 2020-03-01 18:19:38 +00:00
Curtis Hard 4b308d3a3e Wrong unit used 2020-02-27 08:33:16 +00:00
Curtis Hard d3ee05d8ac #oops 2020-02-25 08:29:59 +00:00
Curtis Hard 204b516e77 Fixes width and height not working correctly 2020-02-12 22:32:52 +00:00
Curtis Hard a832a986fd Swaps back to CATransaction 2020-01-16 20:37:48 +00:00
Curtis Hard f875714609 FOrmatting 2020-01-14 21:12:38 +00:00
Curtis Hard b8f166f4c1 Uses NSAnimationContext over CATransaction for safety 2020-01-14 21:12:33 +00:00
Curtis Hard 7901c9eafe Possible fixes for main thread 2020-01-14 20:41:59 +00:00
Curtis Hard 5de88cb1d7 #oopsie 2020-01-14 16:38:08 +00:00
Curtis Hard 6216b61c19 Fixes compat for 10.13 with CGPath block applying 2020-01-14 16:34:52 +00:00
Curtis Hard e05f5a0884 Fixes compatibility issues for 10.9 2020-01-14 15:51:15 +00:00
Curtis Hard 849807d3c5 Updated build number 2020-01-05 17:11:41 +00:00
Curtis Hard eeb03bd89c Fixes exporter being wrong if the viewbox is not zero 2020-01-02 14:16:17 +00:00
Curtis Hard fdc0859e3f Faster parsing still 2020-01-01 22:06:29 +00:00
Curtis Hard e2a7f51507 Fixes 2020-01-01 17:48:15 +00:00
Curtis Hard ab69ed94dc Formatting 2019-12-29 20:50:44 +00:00
Curtis Hard c5be83f57f Code cleanup 2019-12-28 15:48:43 +00:00
Curtis Hard ed00aca06e Refactor 2019-12-27 16:48:12 +00:00
Curtis Hard f51fda5f1e Removed cache
- also fixes center if no scale was applied
2019-12-27 15:01:20 +00:00
Curtis Hard dd655053ca Finally fixes memory leak
- due to this being fixed, we can also get rid of the transaction lock
2019-12-26 20:10:47 +00:00
Curtis Hard 8c89627f74 Just use matrix, attributes are a pain in the butt 2019-12-26 16:08:14 +00:00
Curtis Hard eb1d27decd Much better path compression and float rewriting for exporting 2019-12-26 13:49:41 +00:00
Curtis Hard e6141d7a0f Fixes a long time ago bug with transforms with matrix converting to attribute strings 2019-12-25 21:05:56 +00:00
Curtis Hard 3b7ad40794 Vey strange... 2019-12-25 18:40:50 +00:00
Curtis Hard 674d17863f Fixes memory overflow issues
- also added centering for exported SVG’s as an option
2019-12-25 18:19:28 +00:00
Curtis Hard a59072fa1d Refactor 2019-12-25 13:53:42 +00:00
Curtis Hard 910b90cd4e More perf stuff 2019-12-25 13:45:13 +00:00
Curtis Hard bb3b9c5378 More improvements 2019-12-25 11:58:10 +00:00
Curtis Hard 6d10b172e7 Increases again 2019-12-24 14:47:47 +00:00
Curtis Hard c6e59a9280 More perf increases 2019-12-24 09:27:06 +00:00
Curtis Hard df5b3219ca Refactor of naming 2019-12-23 21:59:19 +00:00
Curtis Hard 2c1ae8d0f3 Credit where its due 2019-12-23 21:40:02 +00:00
Curtis Hard 7ba939aabf This is insanely fast 2019-12-23 20:48:12 +00:00
Curtis Hard 24e097fedf Faster again! 2019-12-23 20:30:05 +00:00
Curtis Hard 025ac84958 Refactor of command parsing 2019-12-23 19:37:56 +00:00
Curtis Hard ac9ccdda25 Refactor 2019-12-23 16:17:47 +00:00
Curtis Hard 14641ddd60 More goodies 2019-12-23 15:40:41 +00:00
Curtis Hard 5f32d03744 Intiial commit 2019-12-23 14:24:49 +00:00
Curtis Hard af086622b1 Faster implementation of transaction 2019-12-22 22:19:20 +00:00
Curtis Hard 79187326bc Added CGImageRef method 2019-12-21 13:23:52 +00:00
Curtis Hard ee570eb77f Fixes scale being incorrect 2019-12-20 08:36:12 +00:00
Curtis Hard d8e1ce8d70 This is much more performant then using nsimage lockFocus 2019-12-19 22:32:55 +00:00
Curtis Hard a900e2dc50 Resorted this list as its actually faster, basically stick the commonly used commands first 2019-12-17 21:48:52 +00:00
Curtis Hard 21f97babec Refactor of contsant names 2019-12-12 11:02:49 +00:00
Curtis Hard 0273a491de Refactor of path - uses less memory and makes more sense 2019-12-12 10:51:20 +00:00
Curtis Hard 488a0c3654 Update all things! 2019-12-12 08:09:08 +00:00
Curtis Hard 7f7321e6a7 Merge branch 'feature/primitiveExportTypes' 2019-12-12 08:07:05 +00:00
67 changed files with 3078 additions and 1445 deletions
+79 -33
View File
@@ -7,10 +7,20 @@
objects = {
/* Begin PBXBuildFile section */
5919E65723F47FF60051873A /* IJSVGUnitRect.h in Headers */ = {isa = PBXBuildFile; fileRef = 5919E65523F47FF60051873A /* IJSVGUnitRect.h */; settings = {ATTRIBUTES = (Public, ); }; };
5919E65823F47FF60051873A /* IJSVGUnitRect.m in Sources */ = {isa = PBXBuildFile; fileRef = 5919E65623F47FF60051873A /* IJSVGUnitRect.m */; };
5919E65B23F480330051873A /* IJSVGUnitPoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 5919E65923F480330051873A /* IJSVGUnitPoint.h */; settings = {ATTRIBUTES = (Public, ); }; };
5919E65C23F480330051873A /* IJSVGUnitPoint.m in Sources */ = {isa = PBXBuildFile; fileRef = 5919E65A23F480330051873A /* IJSVGUnitPoint.m */; };
594A10DA248D7C90001A3181 /* NSImage+IJSVGAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 594A10D8248D7C90001A3181 /* NSImage+IJSVGAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
594A10DB248D7C90001A3181 /* NSImage+IJSVGAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 594A10D9248D7C90001A3181 /* NSImage+IJSVGAdditions.m */; };
594CF55F238FF462009B251B /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF55E238FF462009B251B /* AppKit.framework */; };
594CF561238FF46C009B251B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF560238FF46C009B251B /* Foundation.framework */; };
594CF563238FF473009B251B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF562238FF473009B251B /* QuartzCore.framework */; };
599EB4D3238FF570004CB6BC /* libobjc.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 599EB4D2238FF535004CB6BC /* libobjc.tbd */; };
59A24EBC23F480EA0090C374 /* IJSVGUnitSize.h in Headers */ = {isa = PBXBuildFile; fileRef = 59A24EBA23F480EA0090C374 /* IJSVGUnitSize.h */; settings = {ATTRIBUTES = (Public, ); }; };
59A24EBD23F480EA0090C374 /* IJSVGUnitSize.m in Sources */ = {isa = PBXBuildFile; fileRef = 59A24EBB23F480EA0090C374 /* IJSVGUnitSize.m */; };
59E7CFAF23B148600077D599 /* IJSVGCommandParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 59E7CFAD23B148600077D599 /* IJSVGCommandParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
59E7CFB023B148600077D599 /* IJSVGCommandParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E7CFAE23B148600077D599 /* IJSVGCommandParser.m */; };
59EB75D623905F7300F5AE63 /* IJSVGLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB756523905F6B00F5AE63 /* IJSVGLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB75D723905F7300F5AE63 /* IJSVGGradientLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB756623905F6B00F5AE63 /* IJSVGGradientLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB75D823905F7300F5AE63 /* IJSVGStyleSheetRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB756723905F6B00F5AE63 /* IJSVGStyleSheetRule.m */; };
@@ -57,7 +67,6 @@
59EB760123905F7300F5AE63 /* IJSVGLayerTree.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB759023905F6D00F5AE63 /* IJSVGLayerTree.m */; };
59EB760223905F7300F5AE63 /* IJSVGCommandVerticalLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB759123905F6D00F5AE63 /* IJSVGCommandVerticalLine.m */; };
59EB760323905F7300F5AE63 /* IJSVGNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB759223905F6D00F5AE63 /* IJSVGNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB760423905F7300F5AE63 /* IJSVGCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB759323905F6D00F5AE63 /* IJSVGCache.m */; };
59EB760523905F7300F5AE63 /* IJSVGUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB759423905F6D00F5AE63 /* IJSVGUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB760623905F7300F5AE63 /* IJSVGCommandHorizontalLine.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB759523905F6D00F5AE63 /* IJSVGCommandHorizontalLine.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB760723905F7300F5AE63 /* IJSVGNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB759623905F6D00F5AE63 /* IJSVGNode.m */; };
@@ -100,7 +109,6 @@
59EB762C23905F7300F5AE63 /* IJSVGPattern.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB75BB23905F7000F5AE63 /* IJSVGPattern.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB762D23905F7300F5AE63 /* IJSVGLayerTree.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB75BC23905F7000F5AE63 /* IJSVGLayerTree.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB762E23905F7300F5AE63 /* IJSVGExporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75BD23905F7000F5AE63 /* IJSVGExporter.m */; };
59EB762F23905F7300F5AE63 /* IJSVGCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB75BE23905F7000F5AE63 /* IJSVGCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB763023905F7300F5AE63 /* IJSVGPatternLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB75BF23905F7000F5AE63 /* IJSVGPatternLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB763123905F7300F5AE63 /* IJSVGText.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C023905F7100F5AE63 /* IJSVGText.m */; };
59EB763223905F7300F5AE63 /* IJSVGView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C123905F7100F5AE63 /* IJSVGView.m */; };
@@ -127,22 +135,32 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
5919E65523F47FF60051873A /* IJSVGUnitRect.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGUnitRect.h; sourceTree = "<group>"; };
5919E65623F47FF60051873A /* IJSVGUnitRect.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGUnitRect.m; sourceTree = "<group>"; };
5919E65923F480330051873A /* IJSVGUnitPoint.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGUnitPoint.h; sourceTree = "<group>"; };
5919E65A23F480330051873A /* IJSVGUnitPoint.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGUnitPoint.m; sourceTree = "<group>"; };
594A10D8248D7C90001A3181 /* NSImage+IJSVGAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSImage+IJSVGAdditions.h"; sourceTree = "<group>"; };
594A10D9248D7C90001A3181 /* NSImage+IJSVGAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSImage+IJSVGAdditions.m"; sourceTree = "<group>"; };
594CF46F238FF38E009B251B /* IJSVG.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = IJSVG.framework; sourceTree = BUILT_PRODUCTS_DIR; };
594CF473238FF38E009B251B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
594CF55E238FF462009B251B /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
594CF560238FF46C009B251B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
594CF562238FF473009B251B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
599EB4D2238FF535004CB6BC /* libobjc.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libobjc.tbd; path = usr/lib/libobjc.tbd; sourceTree = SDKROOT; };
59A24EBA23F480EA0090C374 /* IJSVGUnitSize.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGUnitSize.h; sourceTree = "<group>"; };
59A24EBB23F480EA0090C374 /* IJSVGUnitSize.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGUnitSize.m; sourceTree = "<group>"; };
59E7CFAD23B148600077D599 /* IJSVGCommandParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = IJSVGCommandParser.h; path = IJSVG/Source/Parsing/IJSVGCommandParser.h; sourceTree = SOURCE_ROOT; };
59E7CFAE23B148600077D599 /* IJSVGCommandParser.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = IJSVGCommandParser.m; path = IJSVG/Source/Parsing/IJSVGCommandParser.m; sourceTree = SOURCE_ROOT; };
59EB756523905F6B00F5AE63 /* IJSVGLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGLayer.h; sourceTree = "<group>"; };
59EB756623905F6B00F5AE63 /* IJSVGGradientLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGGradientLayer.h; sourceTree = "<group>"; };
59EB756723905F6B00F5AE63 /* IJSVGStyleSheetRule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGStyleSheetRule.m; sourceTree = "<group>"; };
59EB756823905F6B00F5AE63 /* IJSVGCommandLineTo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGCommandLineTo.h; sourceTree = "<group>"; };
59EB756923905F6B00F5AE63 /* IJSVGDef.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGDef.m; sourceTree = "<group>"; };
59EB756A23905F6B00F5AE63 /* IJSVG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJSVG.h; path = ../../../../source/IJSVG.h; sourceTree = "<group>"; };
59EB756A23905F6B00F5AE63 /* IJSVG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVG.h; sourceTree = "<group>"; };
59EB756B23905F6B00F5AE63 /* IJSVGText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGText.h; sourceTree = "<group>"; };
59EB756C23905F6B00F5AE63 /* IJSVGCommandQuadraticCurve.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGCommandQuadraticCurve.m; sourceTree = "<group>"; };
59EB756D23905F6C00F5AE63 /* IJSVGCommandVerticalLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGCommandVerticalLine.h; sourceTree = "<group>"; };
59EB756E23905F6C00F5AE63 /* IJSVGWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJSVGWriter.h; path = ../../../../source/IJSVGWriter.h; sourceTree = "<group>"; };
59EB756E23905F6C00F5AE63 /* IJSVGWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGWriter.h; sourceTree = "<group>"; };
59EB756F23905F6C00F5AE63 /* IJSVGStyleSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGStyleSheet.h; sourceTree = "<group>"; };
59EB757023905F6C00F5AE63 /* IJSVGCommandSmoothCurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGCommandSmoothCurve.h; sourceTree = "<group>"; };
59EB757123905F6C00F5AE63 /* IJSVGColorList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGColorList.m; sourceTree = "<group>"; };
@@ -156,20 +174,20 @@
59EB757923905F6C00F5AE63 /* IJSVGTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGTransaction.h; sourceTree = "<group>"; };
59EB757A23905F6C00F5AE63 /* IJSVGShapeLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGShapeLayer.m; sourceTree = "<group>"; };
59EB757B23905F6C00F5AE63 /* IJSVGLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGLayer.m; sourceTree = "<group>"; };
59EB757C23905F6C00F5AE63 /* IJSVGFontConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IJSVGFontConverter.m; path = ../../../../source/IJSVGFontConverter.m; sourceTree = "<group>"; };
59EB757D23905F6C00F5AE63 /* IJSVGImageRep.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IJSVGImageRep.m; path = ../../../../source/IJSVGImageRep.m; sourceTree = "<group>"; };
59EB757C23905F6C00F5AE63 /* IJSVGFontConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGFontConverter.m; sourceTree = "<group>"; };
59EB757D23905F6C00F5AE63 /* IJSVGImageRep.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGImageRep.m; sourceTree = "<group>"; };
59EB757E23905F6C00F5AE63 /* IJSVGCommandQuadraticCurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGCommandQuadraticCurve.h; sourceTree = "<group>"; };
59EB757F23905F6C00F5AE63 /* IJSVGStyleSheetSelectorRaw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGStyleSheetSelectorRaw.h; sourceTree = "<group>"; };
59EB758023905F6C00F5AE63 /* IJSVGUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGUtils.m; sourceTree = "<group>"; };
59EB758123905F6C00F5AE63 /* IJSVGStyleSheetSelectorRaw.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGStyleSheetSelectorRaw.m; sourceTree = "<group>"; };
59EB758223905F6C00F5AE63 /* IJSVGGroupLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGGroupLayer.m; sourceTree = "<group>"; };
59EB758323905F6C00F5AE63 /* IJSVGColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGColor.h; sourceTree = "<group>"; };
59EB758423905F6C00F5AE63 /* IJSVGError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJSVGError.h; path = ../../../../source/IJSVGError.h; sourceTree = "<group>"; };
59EB758423905F6C00F5AE63 /* IJSVGError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGError.h; sourceTree = "<group>"; };
59EB758523905F6C00F5AE63 /* IJSVGColor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGColor.m; sourceTree = "<group>"; };
59EB758623905F6C00F5AE63 /* IJSVGDef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGDef.h; sourceTree = "<group>"; };
59EB758723905F6C00F5AE63 /* IJSVGCommandHorizontalLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGCommandHorizontalLine.m; sourceTree = "<group>"; };
59EB758823905F6D00F5AE63 /* IJSVGFontConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJSVGFontConverter.h; path = ../../../../source/IJSVGFontConverter.h; sourceTree = "<group>"; };
59EB758923905F6D00F5AE63 /* IJSVGWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IJSVGWriter.m; path = ../../../../source/IJSVGWriter.m; sourceTree = "<group>"; };
59EB758823905F6D00F5AE63 /* IJSVGFontConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGFontConverter.h; sourceTree = "<group>"; };
59EB758923905F6D00F5AE63 /* IJSVGWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGWriter.m; sourceTree = "<group>"; };
59EB758A23905F6D00F5AE63 /* IJSVGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGPath.m; sourceTree = "<group>"; };
59EB758B23905F6D00F5AE63 /* IJSVGGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGGroup.h; sourceTree = "<group>"; };
59EB758C23905F6D00F5AE63 /* IJSVGPatternLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGPatternLayer.m; sourceTree = "<group>"; };
@@ -179,7 +197,6 @@
59EB759023905F6D00F5AE63 /* IJSVGLayerTree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGLayerTree.m; sourceTree = "<group>"; };
59EB759123905F6D00F5AE63 /* IJSVGCommandVerticalLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGCommandVerticalLine.m; sourceTree = "<group>"; };
59EB759223905F6D00F5AE63 /* IJSVGNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGNode.h; sourceTree = "<group>"; };
59EB759323905F6D00F5AE63 /* IJSVGCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IJSVGCache.m; path = ../../../../source/IJSVGCache.m; sourceTree = "<group>"; };
59EB759423905F6D00F5AE63 /* IJSVGUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGUtils.h; sourceTree = "<group>"; };
59EB759523905F6D00F5AE63 /* IJSVGCommandHorizontalLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGCommandHorizontalLine.h; sourceTree = "<group>"; };
59EB759623905F6D00F5AE63 /* IJSVGNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGNode.m; sourceTree = "<group>"; };
@@ -194,8 +211,8 @@
59EB759F23905F6E00F5AE63 /* IJSVGLinearGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGLinearGradient.h; sourceTree = "<group>"; };
59EB75A023905F6E00F5AE63 /* IJSVGCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGCommand.m; sourceTree = "<group>"; };
59EB75A123905F6E00F5AE63 /* IJSVGRenderingStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGRenderingStyle.h; sourceTree = "<group>"; };
59EB75A223905F6E00F5AE63 /* IJSVG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IJSVG.m; path = ../../../../source/IJSVG.m; sourceTree = "<group>"; };
59EB75A323905F6E00F5AE63 /* IJSVGImageRep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJSVGImageRep.h; path = ../../../../source/IJSVGImageRep.h; sourceTree = "<group>"; };
59EB75A223905F6E00F5AE63 /* IJSVG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVG.m; sourceTree = "<group>"; };
59EB75A323905F6E00F5AE63 /* IJSVGImageRep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGImageRep.h; sourceTree = "<group>"; };
59EB75A423905F6E00F5AE63 /* IJSVGStrokeLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGStrokeLayer.h; sourceTree = "<group>"; };
59EB75A523905F6E00F5AE63 /* IJSVGParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGParser.m; sourceTree = "<group>"; };
59EB75A623905F6F00F5AE63 /* IJSVGRadialGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGRadialGradient.m; sourceTree = "<group>"; };
@@ -222,10 +239,9 @@
59EB75BB23905F7000F5AE63 /* IJSVGPattern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGPattern.h; sourceTree = "<group>"; };
59EB75BC23905F7000F5AE63 /* IJSVGLayerTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGLayerTree.h; sourceTree = "<group>"; };
59EB75BD23905F7000F5AE63 /* IJSVGExporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGExporter.m; sourceTree = "<group>"; };
59EB75BE23905F7000F5AE63 /* IJSVGCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJSVGCache.h; path = ../../../../source/IJSVGCache.h; sourceTree = "<group>"; };
59EB75BF23905F7000F5AE63 /* IJSVGPatternLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGPatternLayer.h; sourceTree = "<group>"; };
59EB75C023905F7100F5AE63 /* IJSVGText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGText.m; sourceTree = "<group>"; };
59EB75C123905F7100F5AE63 /* IJSVGView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IJSVGView.m; path = ../../../../source/IJSVGView.m; sourceTree = "<group>"; };
59EB75C123905F7100F5AE63 /* IJSVGView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGView.m; sourceTree = "<group>"; };
59EB75C223905F7100F5AE63 /* IJSVGLinearGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGLinearGradient.m; sourceTree = "<group>"; };
59EB75C323905F7100F5AE63 /* IJSVGBezierPathAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGBezierPathAdditions.h; sourceTree = "<group>"; };
59EB75C423905F7100F5AE63 /* IJSVGStyleSheetSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGStyleSheetSelector.m; sourceTree = "<group>"; };
@@ -237,7 +253,7 @@
59EB75CA23905F7200F5AE63 /* IJSVGCommandSmoothQuadraticCurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGCommandSmoothQuadraticCurve.h; sourceTree = "<group>"; };
59EB75CB23905F7200F5AE63 /* IJSVGCommandEllipticalArc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGCommandEllipticalArc.m; sourceTree = "<group>"; };
59EB75CC23905F7200F5AE63 /* IJSVGGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGGroup.m; sourceTree = "<group>"; };
59EB75CD23905F7200F5AE63 /* IJSVGView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IJSVGView.h; path = ../../../../source/IJSVGView.h; sourceTree = "<group>"; };
59EB75CD23905F7200F5AE63 /* IJSVGView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGView.h; sourceTree = "<group>"; };
59EB75CE23905F7200F5AE63 /* IJSVGForeignObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGForeignObject.h; sourceTree = "<group>"; };
59EB75CF23905F7200F5AE63 /* IJSVGExporterPathInstruction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGExporterPathInstruction.m; sourceTree = "<group>"; };
59EB75D023905F7200F5AE63 /* IJSVGImageLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGImageLayer.h; sourceTree = "<group>"; };
@@ -346,6 +362,8 @@
592ABBE62397A00C00F44380 /* Parsing */ = {
isa = PBXGroup;
children = (
59E7CFAD23B148600077D599 /* IJSVGCommandParser.h */,
59E7CFAE23B148600077D599 /* IJSVGCommandParser.m */,
59EB75A723905F6F00F5AE63 /* IJSVGParser.h */,
59EB75A523905F6E00F5AE63 /* IJSVGParser.m */,
);
@@ -394,6 +412,12 @@
59EB75BA23905F7000F5AE63 /* IJSVGUnitLength.m */,
59EB759423905F6D00F5AE63 /* IJSVGUtils.h */,
59EB758023905F6C00F5AE63 /* IJSVGUtils.m */,
5919E65523F47FF60051873A /* IJSVGUnitRect.h */,
5919E65623F47FF60051873A /* IJSVGUnitRect.m */,
5919E65923F480330051873A /* IJSVGUnitPoint.h */,
5919E65A23F480330051873A /* IJSVGUnitPoint.m */,
59A24EBA23F480EA0090C374 /* IJSVGUnitSize.h */,
59A24EBB23F480EA0090C374 /* IJSVGUnitSize.m */,
);
path = Utils;
sourceTree = "<group>";
@@ -429,6 +453,8 @@
59EB758E23905F6D00F5AE63 /* IJSVGBezierPathAdditions.m */,
59EB75B823905F7000F5AE63 /* IJSVGStringAdditions.h */,
59EB757823905F6C00F5AE63 /* IJSVGStringAdditions.m */,
594A10D8248D7C90001A3181 /* NSImage+IJSVGAdditions.h */,
594A10D9248D7C90001A3181 /* NSImage+IJSVGAdditions.m */,
);
path = Additions;
sourceTree = "<group>";
@@ -470,9 +496,28 @@
name = Frameworks;
sourceTree = "<group>";
};
59DD1FD123A22CFD00E28C56 /* Core */ = {
isa = PBXGroup;
children = (
59EB756A23905F6B00F5AE63 /* IJSVG.h */,
59EB75A223905F6E00F5AE63 /* IJSVG.m */,
59EB758423905F6C00F5AE63 /* IJSVGError.h */,
59EB758823905F6D00F5AE63 /* IJSVGFontConverter.h */,
59EB757C23905F6C00F5AE63 /* IJSVGFontConverter.m */,
59EB75A323905F6E00F5AE63 /* IJSVGImageRep.h */,
59EB757D23905F6C00F5AE63 /* IJSVGImageRep.m */,
59EB75CD23905F7200F5AE63 /* IJSVGView.h */,
59EB75C123905F7100F5AE63 /* IJSVGView.m */,
59EB756E23905F6C00F5AE63 /* IJSVGWriter.h */,
59EB758923905F6D00F5AE63 /* IJSVGWriter.m */,
);
path = Core;
sourceTree = "<group>";
};
59EB756423905F3100F5AE63 /* Source */ = {
isa = PBXGroup;
children = (
59DD1FD123A22CFD00E28C56 /* Core */,
592ABBEB2397A11800F44380 /* Additions */,
592ABBEA2397A10000F44380 /* Rendering */,
592ABBE92397A08500F44380 /* Exporter */,
@@ -483,19 +528,6 @@
592ABBE423979FD200F44380 /* Colors */,
592ABBE323979FBB00F44380 /* Stylesheets */,
592ABBE223979F9B00F44380 /* Commands */,
59EB756A23905F6B00F5AE63 /* IJSVG.h */,
59EB75A223905F6E00F5AE63 /* IJSVG.m */,
59EB75BE23905F7000F5AE63 /* IJSVGCache.h */,
59EB759323905F6D00F5AE63 /* IJSVGCache.m */,
59EB758423905F6C00F5AE63 /* IJSVGError.h */,
59EB758823905F6D00F5AE63 /* IJSVGFontConverter.h */,
59EB757C23905F6C00F5AE63 /* IJSVGFontConverter.m */,
59EB75A323905F6E00F5AE63 /* IJSVGImageRep.h */,
59EB757D23905F6C00F5AE63 /* IJSVGImageRep.m */,
59EB75CD23905F7200F5AE63 /* IJSVGView.h */,
59EB75C123905F7100F5AE63 /* IJSVGView.m */,
59EB756E23905F6C00F5AE63 /* IJSVGWriter.h */,
59EB758923905F6D00F5AE63 /* IJSVGWriter.m */,
);
path = Source;
sourceTree = "<group>";
@@ -537,7 +569,6 @@
59EB75E823905F7300F5AE63 /* IJSVGUnitLength.h in Headers */,
59EB762723905F7300F5AE63 /* IJSVGMath.h in Headers */,
59EB761523905F7300F5AE63 /* IJSVGStrokeLayer.h in Headers */,
59EB762F23905F7300F5AE63 /* IJSVGCache.h in Headers */,
59EB75DE23905F7300F5AE63 /* IJSVGCommandVerticalLine.h in Headers */,
59EB764123905F7300F5AE63 /* IJSVGImageLayer.h in Headers */,
59EB75E023905F7300F5AE63 /* IJSVGStyleSheet.h in Headers */,
@@ -559,9 +590,14 @@
59EB761823905F7300F5AE63 /* IJSVGParser.h in Headers */,
59EB761E23905F7300F5AE63 /* IJSVGGroupLayer.h in Headers */,
59EB761D23905F7300F5AE63 /* IJSVGStyle.h in Headers */,
5919E65723F47FF60051873A /* IJSVGUnitRect.h in Headers */,
5919E65B23F480330051873A /* IJSVGUnitPoint.h in Headers */,
59A24EBC23F480EA0090C374 /* IJSVGUnitSize.h in Headers */,
59EB764523905F7300F5AE63 /* IJSVGExporter.h in Headers */,
59E7CFAF23B148600077D599 /* IJSVGCommandParser.h in Headers */,
59EB762823905F7300F5AE63 /* IJSVGCommandClose.h in Headers */,
59EB75E423905F7300F5AE63 /* IJSVGGradientUnitLength.h in Headers */,
594A10DA248D7C90001A3181 /* NSImage+IJSVGAdditions.h in Headers */,
59EB762D23905F7300F5AE63 /* IJSVGLayerTree.h in Headers */,
59EB760B23905F7300F5AE63 /* IJSVGStyleSheetSelector.h in Headers */,
);
@@ -594,7 +630,7 @@
594CF466238FF38E009B251B /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1100;
LastUpgradeCheck = 1220;
ORGANIZATIONNAME = "Curtis Hard";
TargetAttributes = {
594CF46E238FF38E009B251B = {
@@ -636,6 +672,7 @@
buildActionMask = 2147483647;
files = (
59EB763123905F7300F5AE63 /* IJSVGText.m in Sources */,
59A24EBD23F480EA0090C374 /* IJSVGUnitSize.m in Sources */,
59EB760223905F7300F5AE63 /* IJSVGCommandVerticalLine.m in Sources */,
59EB75FD23905F7300F5AE63 /* IJSVGPatternLayer.m in Sources */,
59EB763923905F7300F5AE63 /* IJSVGCommandSmoothQuadraticCurve.m in Sources */,
@@ -646,6 +683,7 @@
59EB761123905F7300F5AE63 /* IJSVGCommand.m in Sources */,
59EB760923905F7300F5AE63 /* IJSVGCommandCurve.m in Sources */,
59EB763323905F7300F5AE63 /* IJSVGLinearGradient.m in Sources */,
594A10DB248D7C90001A3181 /* NSImage+IJSVGAdditions.m in Sources */,
59EB761923905F7300F5AE63 /* IJSVGCommandSmoothCurve.m in Sources */,
59EB761323905F7300F5AE63 /* IJSVG.m in Sources */,
59EB763623905F7300F5AE63 /* IJSVGImageLayer.m in Sources */,
@@ -653,10 +691,11 @@
59EB75E523905F7300F5AE63 /* IJSVGStrokeLayer.m in Sources */,
59EB763223905F7300F5AE63 /* IJSVGView.m in Sources */,
59EB763523905F7300F5AE63 /* IJSVGStyleSheetSelector.m in Sources */,
59E7CFB023B148600077D599 /* IJSVGCommandParser.m in Sources */,
59EB760723905F7300F5AE63 /* IJSVGNode.m in Sources */,
5919E65C23F480330051873A /* IJSVGUnitPoint.m in Sources */,
59EB75E923905F7300F5AE63 /* IJSVGStringAdditions.m in Sources */,
59EB761723905F7300F5AE63 /* IJSVGRadialGradient.m in Sources */,
59EB760423905F7300F5AE63 /* IJSVGCache.m in Sources */,
59EB75E223905F7300F5AE63 /* IJSVGColorList.m in Sources */,
59EB75ED23905F7300F5AE63 /* IJSVGFontConverter.m in Sources */,
59EB763C23905F7300F5AE63 /* IJSVGCommandEllipticalArc.m in Sources */,
@@ -666,6 +705,7 @@
59EB75EB23905F7300F5AE63 /* IJSVGShapeLayer.m in Sources */,
59EB75F623905F7300F5AE63 /* IJSVGColor.m in Sources */,
59EB75F323905F7300F5AE63 /* IJSVGGroupLayer.m in Sources */,
5919E65823F47FF60051873A /* IJSVGUnitRect.m in Sources */,
59EB762523905F7300F5AE63 /* IJSVGTransform.m in Sources */,
59EB760023905F7300F5AE63 /* IJSVGGradientLayer.m in Sources */,
59EB762B23905F7300F5AE63 /* IJSVGUnitLength.m in Sources */,
@@ -723,6 +763,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -738,6 +779,7 @@
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREFIX_HEADER = "";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
@@ -784,6 +826,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -797,6 +840,7 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREFIX_HEADER = "";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -820,6 +864,7 @@
CLANG_WARN_STRICT_PROTOTYPES = YES;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2;
DEFINES_MODULE = NO;
DEVELOPMENT_TEAM = 9KTR4W9XX6;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -832,7 +877,7 @@
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 2.1;
MARKETING_VERSION = 2.2.0;
PRODUCT_BUNDLE_IDENTIFIER = com.iconjar.ijsvg;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
@@ -847,6 +892,7 @@
CLANG_WARN_STRICT_PROTOTYPES = YES;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2;
DEFINES_MODULE = NO;
DEVELOPMENT_TEAM = 9KTR4W9XX6;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -859,7 +905,7 @@
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 2.1;
MARKETING_VERSION = 2.2.0;
PRODUCT_BUNDLE_IDENTIFIER = com.iconjar.ijsvg;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1120"
LastUpgradeVersion = "1220"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -15,7 +15,7 @@
<key>594CF46E238FF38E009B251B</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
</dict>
</dict>
+1 -1
View File
@@ -19,6 +19,6 @@
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 Curtis Hard. All rights reserved.</string>
<string>Copyright © 2020 Curtis Hard. All rights reserved.</string>
</dict>
</plist>
@@ -14,4 +14,6 @@
- (void)addQuadCurveToPoint:(NSPoint)aPoint
controlPoint:(NSPoint)cp;
- (CGPathRef)newCGPathRef:(BOOL)autoClose;
@end
@@ -23,4 +23,65 @@
controlPoint2:CP2];
}
- (CGPathRef)newCGPathRef:(BOOL)autoClose
{
NSInteger i = 0;
NSInteger numElements = self.elementCount;
NSBezierPath* bezPath = self;
// nothing to return
if (numElements == 0) {
return NULL;
}
CGMutablePathRef aPath = CGPathCreateMutable();
NSPoint points[3];
BOOL didClosePath = YES;
for (i = 0; i < numElements; i++) {
switch ([bezPath elementAtIndex:i associatedPoints:points]) {
// move
case NSMoveToBezierPathElement: {
CGPathMoveToPoint(aPath, NULL, points[0].x, points[0].y);
break;
}
// line
case NSLineToBezierPathElement: {
CGPathAddLineToPoint(aPath, NULL, points[0].x, points[0].y);
didClosePath = NO;
break;
}
// curve
case NSCurveToBezierPathElement: {
CGPathAddCurveToPoint(aPath, NULL, points[0].x, points[0].y,
points[1].x, points[1].y,
points[2].x, points[2].y);
didClosePath = NO;
break;
}
// close
case NSClosePathBezierPathElement: {
CGPathCloseSubpath(aPath);
didClosePath = YES;
break;
}
}
}
if (!didClosePath && autoClose) {
CGPathCloseSubpath(aPath);
}
// create immutable and release
CGPathRef pathToReturn = CGPathCreateCopy(aPath);
CGPathRelease(aPath);
return pathToReturn;
}
@end
@@ -10,9 +10,10 @@
@interface NSString (IJSVGAdditions)
- (NSArray<NSString *> *)ijsvg_componentsSeparatedByChars:(char *)aChar;
- (NSArray<NSString*>*)ijsvg_componentsSeparatedByChars:(const char*)aChar;
- (BOOL)ijsvg_isNumeric;
- (BOOL)ijsvg_containsAlpha;
- (NSArray*)ijsvg_componentsSplitByWhiteSpace;
- (BOOL)ijsvg_isHexString;
@end
@@ -10,7 +10,7 @@
@implementation NSString (IJSVGAdditions)
- (NSArray<NSString*>*)ijsvg_componentsSeparatedByChars:(char*)aChar
- (NSArray<NSString*>*)ijsvg_componentsSeparatedByChars:(const char*)aChar
{
NSMutableArray* comp = [[[NSMutableArray alloc] init] autorelease];
NSInteger length = self.length;
@@ -18,25 +18,13 @@
NSInteger ind = 0;
BOOL startedString = NO;
// block for easy comparison
NSUInteger aLength = strlen(aChar);
BOOL (^charsContainsChar)(char anotherChar) = ^(char anotherChar) {
for (NSInteger i = 0; i < aLength; i++) {
if (aChar[i] == anotherChar) {
return YES;
}
}
return NO;
};
const char* buffer = self.UTF8String;
for (NSInteger i = 0; i < length; i++) {
// the char
unichar theChar = [self characterAtIndex:i];
unichar theChar = buffer[i];
// start the buffer
BOOL isEqualToChar = charsContainsChar(theChar);
BOOL isEqualToChar = strchr(aChar, theChar) != NULL;
if (isEqualToChar == NO) {
startedString = YES;
chars[ind++] = theChar;
@@ -48,11 +36,10 @@
// append the comp
[comp addObject:[NSString stringWithCharacters:chars length:ind]];
free(chars);
// restart and realloc the memory
ind = 0;
chars = (unichar*)calloc(sizeof(unichar), self.length);
chars = memset(chars, '\0', sizeof(unichar) * ind);
}
}
free(chars);
@@ -62,9 +49,9 @@
- (BOOL)ijsvg_containsAlpha
{
const char* buffer = self.UTF8String;
unsigned long length = strlen(buffer);
for (int i = 0; i < length; i++) {
if (isalpha(buffer[i])) {
char currentChar;
while((currentChar = *buffer++) ) {
if (isalpha(currentChar)) {
return YES;
}
}
@@ -74,9 +61,9 @@
- (BOOL)ijsvg_isNumeric
{
const char* buffer = self.UTF8String;
unsigned long length = strlen(buffer);
for (int i = 0; i < length; i++) {
if (!isnumber(buffer[i])) {
char currentChar;
while((currentChar = *buffer++) ) {
if (!isnumber(currentChar)) {
return NO;
}
}
@@ -88,4 +75,20 @@
return [self ijsvg_componentsSeparatedByChars:"\t\n\r "];
}
- (BOOL)ijsvg_isHexString
{
const char* chars = self.UTF8String;
char c;
while((c = *chars++)) {
BOOL flag = ((c == '#') ||
(c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'));
if(flag == NO) {
return NO;
}
}
return YES;
}
@end
@@ -0,0 +1,18 @@
//
// NSImage+IJSVGAdditions.h
// IJSVG
//
// Created by Curtis Hard on 07/06/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import <AppKit/AppKit.h>
#import <Cocoa/Cocoa.h>
IJSVG* IJSVGGetFromNSImage(NSImage* image);
@interface NSImage (IJSVGAdditions)
+ (NSImage*)SVGImageNamed:(NSString*)imageName;
@end
@@ -0,0 +1,53 @@
//
// NSImage+IJSVGAdditions.m
// IJSVG
//
// Created by Curtis Hard on 07/06/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import "IJSVGImageRep.h"
#import "NSImage+IJSVGAdditions.h"
IJSVG* IJSVGGetFromNSImage(NSImage* image)
{
for (NSImageRep* rep in image.representations) {
if ([rep isKindOfClass:IJSVGImageRep.class]) {
return ((IJSVGImageRep*)rep).SVG;
}
}
return nil;
}
@implementation NSImage (IJSVGAdditions)
+ (NSImage*)SVGImageNamed:(NSString*)imageName
{
// find the image
NSBundle* bundle = NSBundle.mainBundle;
NSString* str = nil;
NSString* ext = imageName.pathExtension;
if (ext == nil || ext.length == 0) {
ext = @"svg";
}
if ((str = [bundle pathForResource:imageName.stringByDeletingPathExtension
ofType:ext])
!= nil) {
// work out if we can get the data
NSData* data = [[[NSData alloc] initWithContentsOfFile:str] autorelease];
if (data == nil) {
return nil;
}
// grab the image rep
IJSVGImageRep* rep = [[[IJSVGImageRep alloc] initWithData:data] autorelease];
NSImage* image = [[[NSImage alloc] init] autorelease];
[image addRepresentation:rep];
return image;
}
return nil;
}
@end
@@ -8,6 +8,7 @@
#import "IJSVGColor.h"
#import "IJSVGUtils.h"
#import "IJSVGStringAdditions.h"
@implementation IJSVGColor
@@ -385,8 +386,8 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
}
// note the %g, CSS alpha is 0 to 1, not 0 - 100, my bad!
return [NSString stringWithFormat:@"rgba(%d,%d,%d,%g)", red, green, blue,
((float)alpha / 100.f)];
return [NSString stringWithFormat:@"rgba(%d,%d,%d,%@)", red, green, blue,
IJSVGShortFloatString((float)alpha / 100.f)];
}
+ (NSString*)colorNameFromPredefinedColor:(IJSVGPredefinedColor)color
@@ -707,14 +708,7 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
+ (BOOL)isHex:(NSString*)string
{
const char* validList = "0123456789ABCDEFabcdef#";
for (NSInteger i = 0; i < string.length; i++) {
char c = [string characterAtIndex:i];
if (strchr(validList, c) == NULL) {
return NO;
}
}
return YES;
return string.ijsvg_isHexString;
}
+ (unsigned long)lengthOfHEXInteger:(NSUInteger)hex
@@ -6,6 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGCommandParser.h"
#import "IJSVGPath.h"
#import <Foundation/Foundation.h>
@@ -16,14 +17,9 @@ typedef NS_ENUM(NSInteger, IJSVGCommandType) {
kIJSVGCommandTypeRelative
};
typedef NS_ENUM(NSUInteger, IJSVGPathDataSequence) {
kIJSVGPathDataSequenceTypeFloat,
kIJSVGPathDataSequenceTypeFlag
};
@interface IJSVGCommand : NSObject {
NSString* commandString;
NSString* command;
char command;
CGFloat* parameters;
NSInteger parameterCount;
NSArray<IJSVGCommand*>* subCommands;
@@ -35,10 +31,9 @@ typedef NS_ENUM(NSUInteger, IJSVGPathDataSequence) {
}
@property (nonatomic, copy) NSString* commandString;
@property (nonatomic, copy) NSString* command;
@property (nonatomic, assign) char command;
@property (nonatomic, assign) CGFloat* parameters;
@property (nonatomic, assign) NSInteger parameterCount;
@property (nonatomic, assign) NSInteger requiredParameters;
@property (nonatomic, assign) IJSVGCommandType type;
@property (nonatomic, retain) NSArray<IJSVGCommand*>* subCommands;
@property (nonatomic, assign) IJSVGCommand* previousCommand;
@@ -60,8 +55,10 @@ typedef NS_ENUM(NSUInteger, IJSVGPathDataSequence) {
intoArray:(NSMutableArray<IJSVGCommand*>*)commands
parentCommand:(IJSVGCommand*)parentCommand;
- (id)initWithCommandString:(NSString*)commandString;
- (id)initWithCommandStringBuffer:(const char*)str
dataStream:(IJSVGPathDataStream*)dataStream;
- (IJSVGCommand*)subcommandWithParameters:(CGFloat*)subParams
paramCount:(NSInteger)paramCount
previousCommand:(IJSVGCommand*)command;
- (CGFloat)readFloat;
@@ -27,7 +27,6 @@
@synthesize parameterCount;
@synthesize parameters;
@synthesize subCommands;
@synthesize requiredParameters;
@synthesize type;
@synthesize previousCommand;
@synthesize isSubCommand;
@@ -105,7 +104,6 @@
- (void)dealloc
{
(void)([commandString release]), commandString = nil;
(void)([command release]), command = nil;
(void)([subCommands release]), subCommands = nil;
if (parameters) {
(void)(free(parameters)), parameters = nil;
@@ -113,25 +111,27 @@
[super dealloc];
}
- (id)initWithCommandString:(NSString*)str
- (id)initWithCommandStringBuffer:(const char*)str
dataStream:(IJSVGPathDataStream*)dataStream
{
if ((self = [super init]) != nil) {
// work out the basics
_currentIndex = 0;
command = [[str substringToIndex:1] copy];
type = [IJSVGUtils typeForCommandString:self.command];
requiredParameters = [self.class requiredParameterCount];
command = str[0];
type = [IJSVGUtils typeForCommandChar:command];
NSInteger sets = 0;
NSInteger paramCount = [self.class requiredParameterCount];
IJSVGPathDataSequence* sequence = [self.class pathDataSequence];
parameters = IJSVGParsePathDataSequence(str, sequence, requiredParameters, &sets);
parameters = IJSVGParsePathDataStreamSequence(str, strlen(str),
dataStream, sequence, [self.class requiredParameterCount], &sets);
if (sets <= 1) {
CGFloat* subParams = [self parametersFromIndexOffset:0];
IJSVGCommand* command = [self subcommandWithParameters:subParams
paramCount:paramCount
previousCommand:nil];
subCommands = @[ command ].retain;
} else {
NSMutableArray<IJSVGCommand*>* subCommandArray = nil;
subCommandArray = [[NSMutableArray alloc] initWithCapacity:sets].autorelease;
@@ -143,6 +143,7 @@
// generate the subcommand
IJSVGCommand* command = [self subcommandWithParameters:subParams
paramCount:paramCount
previousCommand:lastCommand];
// make sure we assign the last command or hell breaks
@@ -162,7 +163,7 @@
- (CGFloat*)parametersFromIndexOffset:(NSInteger)index
{
CGFloat* subParams = 0;
NSInteger req = self.requiredParameters;
NSInteger req = [self.class requiredParameterCount];
if (req != 0) {
subParams = (CGFloat*)malloc(req * sizeof(CGFloat));
memcpy(subParams, &self.parameters[index * req], sizeof(CGFloat) * req);
@@ -171,11 +172,12 @@
}
- (IJSVGCommand*)subcommandWithParameters:(CGFloat*)subParams
paramCount:(NSInteger)paramCount
previousCommand:(IJSVGCommand*)aPreviousCommand
{
// create a subcommand per set
IJSVGCommand* c = [[[self.class alloc] init] autorelease];
c.parameterCount = self.requiredParameters;
c.parameterCount = paramCount;
c.parameters = subParams;
c.type = self.type;
c.command = self.command;
@@ -23,14 +23,14 @@
path:(IJSVGPath*)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[[path currentSubpath] curveToPoint:NSMakePoint(params[4], params[5])
controlPoint1:NSMakePoint(params[0], params[1])
controlPoint2:NSMakePoint(params[2], params[3])];
[path.path curveToPoint:NSMakePoint(params[4], params[5])
controlPoint1:NSMakePoint(params[0], params[1])
controlPoint2:NSMakePoint(params[2], params[3])];
return;
}
[[path currentSubpath] relativeCurveToPoint:NSMakePoint(params[4], params[5])
controlPoint1:NSMakePoint(params[0], params[1])
controlPoint2:NSMakePoint(params[2], params[3])];
[path.path relativeCurveToPoint:NSMakePoint(params[4], params[5])
controlPoint1:NSMakePoint(params[0], params[1])
controlPoint2:NSMakePoint(params[2], params[3])];
}
@end
@@ -44,8 +44,8 @@ static IJSVGPathDataSequence* _sequence;
CGPoint arcEndPoint = CGPointZero;
CGPoint arcStartPoint = path.currentPoint;
CGFloat xAxisRotation = 0;
BOOL largeArcFlag = 0;
BOOL sweepFlag = 0;
BOOL largeArcFlag = NO;
BOOL sweepFlag = NO;
radii = [currentCommand readPoint];
xAxisRotation = [currentCommand readFloat];
@@ -91,34 +91,26 @@ static IJSVGPathDataSequence* _sequence;
CGPoint scale = (radii.x > radii.y) ? CGPointMake(1, radii.y / radii.x) : CGPointMake(radii.x / radii.y, 1);
NSAffineTransform* trans = NSAffineTransform.transform;
[trans translateXBy:-centerPoint.x yBy:-centerPoint.y];
[path.currentSubpath transformUsingAffineTransform:trans];
trans = NSAffineTransform.transform;
[trans translateXBy:-centerPoint.x
yBy:-centerPoint.y];
[trans rotateByRadians:-xAxisRotation];
[path.currentSubpath transformUsingAffineTransform:trans];
trans = NSAffineTransform.transform;
[trans scaleXBy:(1 / scale.x) yBy:(1 / scale.y)];
[path.currentSubpath transformUsingAffineTransform:trans];
[path.currentSubpath appendBezierPathWithArcWithCenter:NSZeroPoint
radius:radius
startAngle:radians_to_degrees(startAngle)
endAngle:radians_to_degrees(startAngle + angleDelta)
clockwise:!sweepFlag];
trans = NSAffineTransform.transform;
[trans scaleXBy:scale.x yBy:scale.y];
[path.currentSubpath transformUsingAffineTransform:trans];
[trans scaleXBy:(1 / scale.x)
yBy:(1 / scale.y)];
[path.path transformUsingAffineTransform:trans];
[path.path appendBezierPathWithArcWithCenter:NSZeroPoint
radius:radius
startAngle:radians_to_degrees(startAngle)
endAngle:radians_to_degrees(startAngle + angleDelta)
clockwise:!sweepFlag];
trans = NSAffineTransform.transform;
[trans scaleXBy:scale.x
yBy:scale.y];
[trans rotateByRadians:xAxisRotation];
[path.currentSubpath transformUsingAffineTransform:trans];
trans = NSAffineTransform.transform;
[trans translateXBy:centerPoint.x yBy:centerPoint.y];
[path.currentSubpath transformUsingAffineTransform:trans];
[trans translateXBy:centerPoint.x
yBy:centerPoint.y];
[path.path transformUsingAffineTransform:trans];
}
@end
@@ -23,10 +23,10 @@
path:(IJSVGPath*)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[[path currentSubpath] lineToPoint:NSMakePoint(params[0], [path currentSubpath].currentPoint.y)];
[path.path lineToPoint:NSMakePoint(params[0], path.currentPoint.y)];
return;
}
[[path currentSubpath] relativeLineToPoint:NSMakePoint(params[0], 0.f)];
[path.path relativeLineToPoint:NSMakePoint(params[0], 0.f)];
}
@end
@@ -23,12 +23,12 @@
path:(IJSVGPath*)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[[path currentSubpath] lineToPoint:NSMakePoint(params[0], params[1])];
[path.path lineToPoint:NSMakePoint(params[0], params[1])];
return;
}
NSPoint point = NSMakePoint([path currentSubpath].currentPoint.x + params[0],
[path currentSubpath].currentPoint.y + params[1]);
[[path currentSubpath] lineToPoint:point];
NSPoint point = NSMakePoint(path.currentPoint.x + params[0],
path.currentPoint.y + params[1]);
[path.path lineToPoint:point];
}
@end
@@ -35,17 +35,16 @@
return;
}
// actual move to command
if (type == kIJSVGCommandTypeAbsolute) {
[[path currentSubpath] moveToPoint:NSMakePoint(params[0], params[1])];
// actual move to command - do a moveToPoint only
// if the type is absolute, or its possible the type is
// relative but there is no previous command which means
// there is no current point. Asking for current point on an empty
// path will result in an exception being thrown
if (type == kIJSVGCommandTypeAbsolute || command == nil) {
[path.path moveToPoint:NSMakePoint(params[0], params[1])];
return;
}
@try {
[[path currentSubpath] relativeMoveToPoint:NSMakePoint(params[0], params[1])];
}
@catch (NSException* exception) {
[[path currentSubpath] moveToPoint:NSMakePoint(params[0], params[1])];
}
[path.path relativeMoveToPoint:NSMakePoint(params[0], params[1])];
}
@end
@@ -24,12 +24,12 @@
path:(IJSVGPath*)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[[path currentSubpath] addQuadCurveToPoint:NSMakePoint(params[2], params[3])
controlPoint:NSMakePoint(params[0], params[1])];
[path.path addQuadCurveToPoint:NSMakePoint(params[2], params[3])
controlPoint:NSMakePoint(params[0], params[1])];
return;
}
[[path currentSubpath] addQuadCurveToPoint:NSMakePoint([path currentSubpath].currentPoint.x + params[2], [path currentSubpath].currentPoint.y + params[3])
controlPoint:NSMakePoint([path currentSubpath].currentPoint.x + params[0], [path currentSubpath].currentPoint.y + params[1])];
[path.path addQuadCurveToPoint:NSMakePoint(path.currentPoint.x + params[2], path.currentPoint.y + params[3])
controlPoint:NSMakePoint(path.currentPoint.x + params[0], path.currentPoint.y + params[1])];
}
@end
@@ -24,41 +24,41 @@
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
{
NSPoint firstControl = NSMakePoint([path currentSubpath].currentPoint.x, [path currentSubpath].currentPoint.y);
NSPoint firstControl = NSMakePoint(path.currentPoint.x, path.currentPoint.y);
if (command != nil) {
if (command.class == [IJSVGCommandCurve class] || command.class == self.class) {
if (command.class == [IJSVGCommandCurve class]) {
if (command.type == kIJSVGCommandTypeAbsolute) {
firstControl = NSMakePoint(-1 * command.parameters[2] + 2 * [path currentSubpath].currentPoint.x,
-1 * command.parameters[3] + 2 * [path currentSubpath].currentPoint.y);
firstControl = NSMakePoint(-1 * command.parameters[2] + 2 * path.currentPoint.x,
-1 * command.parameters[3] + 2 * path.currentPoint.y);
} else {
NSPoint oldPoint = NSMakePoint([path currentSubpath].currentPoint.x - command.parameters[4],
[path currentSubpath].currentPoint.y - command.parameters[5]);
firstControl = NSMakePoint(-1 * (command.parameters[2] + oldPoint.x) + 2 * [path currentSubpath].currentPoint.x,
-1 * (command.parameters[3] + oldPoint.y) + 2 * [path currentSubpath].currentPoint.y);
NSPoint oldPoint = NSMakePoint(path.currentPoint.x - command.parameters[4],
path.currentPoint.y - command.parameters[5]);
firstControl = NSMakePoint(-1 * (command.parameters[2] + oldPoint.x) + 2 * path.currentPoint.x,
-1 * (command.parameters[3] + oldPoint.y) + 2 * path.currentPoint.y);
}
} else {
if (command.type == kIJSVGCommandTypeAbsolute) {
firstControl = NSMakePoint(-1 * command.parameters[0] + 2 * [path currentSubpath].currentPoint.x,
-1 * command.parameters[1] + 2 * [path currentSubpath].currentPoint.y);
firstControl = NSMakePoint(-1 * command.parameters[0] + 2 * path.currentPoint.x,
-1 * command.parameters[1] + 2 * path.currentPoint.y);
} else {
NSPoint oldPoint = NSMakePoint([path currentSubpath].currentPoint.x - command.parameters[2],
[path currentSubpath].currentPoint.y - command.parameters[3]);
firstControl = NSMakePoint(-1 * (command.parameters[0] + oldPoint.x) + 2 * [path currentSubpath].currentPoint.x,
-1 * (command.parameters[1] + oldPoint.y) + 2 * [path currentSubpath].currentPoint.y);
NSPoint oldPoint = NSMakePoint(path.currentPoint.x - command.parameters[2],
path.currentPoint.y - command.parameters[3]);
firstControl = NSMakePoint(-1 * (command.parameters[0] + oldPoint.x) + 2 * path.currentPoint.x,
-1 * (command.parameters[1] + oldPoint.y) + 2 * path.currentPoint.y);
}
}
}
}
if (type == kIJSVGCommandTypeAbsolute) {
[[path currentSubpath] curveToPoint:NSMakePoint(params[2], params[3])
controlPoint1:NSMakePoint(firstControl.x, firstControl.y)
controlPoint2:NSMakePoint(params[0], params[1])];
[path.path curveToPoint:NSMakePoint(params[2], params[3])
controlPoint1:NSMakePoint(firstControl.x, firstControl.y)
controlPoint2:NSMakePoint(params[0], params[1])];
return;
}
[[path currentSubpath] curveToPoint:NSMakePoint([path currentSubpath].currentPoint.x + params[2], [path currentSubpath].currentPoint.y + params[3])
controlPoint1:NSMakePoint(firstControl.x, firstControl.y)
controlPoint2:NSMakePoint([path currentSubpath].currentPoint.x + params[0], [path currentSubpath].currentPoint.y + params[1])];
[path.path curveToPoint:NSMakePoint(path.currentPoint.x + params[2], path.currentPoint.y + params[3])
controlPoint1:NSMakePoint(firstControl.x, firstControl.y)
controlPoint2:NSMakePoint(path.currentPoint.x + params[0], path.currentPoint.y + params[1])];
}
@end
@@ -24,33 +24,33 @@
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
{
NSPoint commandPoint = NSMakePoint([path currentSubpath].currentPoint.x, [path currentSubpath].currentPoint.y);
NSPoint commandPoint = NSMakePoint(path.currentPoint.x, path.currentPoint.y);
if (command != nil) {
if (command.class == IJSVGCommandQuadraticCurve.class) {
// quadratic curve
if (command.type == kIJSVGCommandTypeAbsolute) {
commandPoint = NSMakePoint(-1 * command.parameters[0] + 2 * [path currentSubpath].currentPoint.x,
-1 * command.parameters[1] + 2 * [path currentSubpath].currentPoint.y);
commandPoint = NSMakePoint(-1 * command.parameters[0] + 2 * path.currentPoint.x,
-1 * command.parameters[1] + 2 * path.currentPoint.y);
} else {
NSPoint oldPoint = CGPointMake([path currentSubpath].currentPoint.x - command.parameters[2],
[path currentSubpath].currentPoint.y - command.parameters[3]);
commandPoint = CGPointMake(-1 * (command.parameters[0] + oldPoint.x) + 2 * ([path currentSubpath].currentPoint.x),
-1 * (command.parameters[1] + oldPoint.y) + 2 * [path currentSubpath].currentPoint.y);
NSPoint oldPoint = CGPointMake(path.currentPoint.x - command.parameters[2],
path.currentPoint.y - command.parameters[3]);
commandPoint = CGPointMake(-1 * (command.parameters[0] + oldPoint.x) + 2 * (path.currentPoint.x),
-1 * (command.parameters[1] + oldPoint.y) + 2 * path.currentPoint.y);
}
} else if (command.class == self.class) {
// smooth quadratic curve
commandPoint = CGPointMake(-1 * (path.lastControlPoint.x) + 2 * ([path currentSubpath].currentPoint.x),
-1 * (path.lastControlPoint.y) + 2 * [path currentSubpath].currentPoint.y);
commandPoint = CGPointMake(-1 * (path.lastControlPoint.x) + 2 * (path.currentPoint.x),
-1 * (path.lastControlPoint.y) + 2 * path.currentPoint.y);
}
}
path.lastControlPoint = commandPoint;
if (type == kIJSVGCommandTypeAbsolute) {
[[path currentSubpath] addQuadCurveToPoint:NSMakePoint(params[0], params[1])
controlPoint:commandPoint];
[path.path addQuadCurveToPoint:NSMakePoint(params[0], params[1])
controlPoint:commandPoint];
return;
}
[[path currentSubpath] addQuadCurveToPoint:NSMakePoint([path currentSubpath].currentPoint.x + params[0], [path currentSubpath].currentPoint.y + params[1])
controlPoint:commandPoint];
[path.path addQuadCurveToPoint:NSMakePoint(path.currentPoint.x + params[0], path.currentPoint.y + params[1])
controlPoint:commandPoint];
}
@end
@@ -23,10 +23,10 @@
path:(IJSVGPath*)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[[path currentSubpath] lineToPoint:NSMakePoint([path currentSubpath].currentPoint.x, params[0])];
[path.path lineToPoint:NSMakePoint(path.currentPoint.x, params[0])];
return;
}
[[path currentSubpath] relativeLineToPoint:NSMakePoint(0.f, params[0])];
[path.path relativeLineToPoint:NSMakePoint(0.f, params[0])];
}
@end
@@ -44,7 +44,7 @@
id<IJSVGDelegate> _delegate;
IJSVGLayer* _layerTree;
CGRect _viewBox;
CGSize _proposedViewSize;
CGFloat _backingScaleFactor;
CGFloat _lastProposedBackingScale;
IJSVGRenderQuality _lastProposedRenderQuality;
CGFloat _backingScale;
@@ -67,7 +67,10 @@
// fillColor, strokeColor, pattern and gradient fill
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
@property (nonatomic, assign) BOOL clipToViewport;
@property (nonatomic, retain) IJSVGRenderingStyle* style;
@property (nonatomic, retain) IJSVGRenderingStyle* renderingStyle;
@property (nonatomic, readonly) IJSVGUnitSize * intrinsicSize;
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* desc;
- (void)prepForDrawingInView:(NSView*)view;
- (BOOL)isFont;
@@ -79,6 +82,8 @@
- (IJSVGLayer*)layerWithTree:(IJSVGLayerTree*)tree;
- (NSArray<IJSVG*>*)subSVGs:(BOOL)recursive;
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options;
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (CGFloat)computeBackingScale:(CGFloat)scale;
- (void)discardDOM;
@@ -86,9 +91,6 @@
+ (id)svgNamed:(NSString*)string;
+ (id)svgNamed:(NSString*)string
delegate:(id<IJSVGDelegate>)delegate;
+ (id)svgNamed:(NSString*)string
useCache:(BOOL)useCache
delegate:(id<IJSVGDelegate>)delegate;
- (id)initWithImage:(NSImage*)image;
@@ -103,8 +105,10 @@
- (id)initWithSVGString:(NSString*)string
error:(NSError**)error;
- (id)initWithFile:(NSString*)file
useCache:(BOOL)useCache;
- (id)initWithSVGData:(NSData*)data;
- (id)initWithSVGData:(NSData*)data
error:(NSError**)error;
- (id)initWithFile:(NSString*)file;
- (id)initWithFile:(NSString*)file
error:(NSError**)error;
@@ -113,19 +117,12 @@
- (id)initWithFile:(NSString*)file
error:(NSError**)error
delegate:(id<IJSVGDelegate>)delegate;
- (id)initWithFile:(NSString*)file
useCache:(BOOL)useCache
error:(NSError**)error
delegate:(id<IJSVGDelegate>)delegate;
- (id)initWithFilePathURL:(NSURL*)aURL;
- (id)initWithFilePathURL:(NSURL*)aURL
useCache:(BOOL)useCache;
- (id)initWithFilePathURL:(NSURL*)aURL
error:(NSError**)error;
- (id)initWithFilePathURL:(NSURL*)aURL
delegate:(id<IJSVGDelegate>)delegate;
- (id)initWithFilePathURL:(NSURL*)aURL
useCache:(BOOL)useCache
error:(NSError**)error
delegate:(id<IJSVGDelegate>)delegate;
- (NSImage*)imageWithSize:(NSSize)aSize;
@@ -133,9 +130,16 @@
error:(NSError**)error;
- (NSImage*)imageWithSize:(NSSize)aSize
flipped:(BOOL)flipped;
- (NSImage*)imageWithSize:(NSSize)aSize
flipped:(BOOL)flipped
error:(NSError**)error;
- (NSImage*)imageByMaintainingAspectRatioWithSize:(NSSize)aSize
flipped:(BOOL)flipped
error:(NSError**)error;
- (CGImageRef)newCGImageRefWithSize:(CGSize)size
flipped:(BOOL)flipped
error:(NSError**)error;
- (BOOL)drawAtPoint:(NSPoint)point
size:(NSSize)size;
- (BOOL)drawAtPoint:(NSPoint)point
@@ -161,4 +165,5 @@
// colors
- (IJSVGColorList*)computedColorList:(BOOL*)hasPatternFills;
- (void)performBlock:(dispatch_block_t)block;
@end
@@ -7,64 +7,63 @@
//
#import "IJSVG.h"
#import "IJSVGCache.h"
#import "IJSVGExporter.h"
#import "IJSVGTransaction.h"
@implementation IJSVG
@synthesize title = _title;
@synthesize desc = _desc;
@synthesize renderingBackingScaleHelper;
@synthesize clipToViewport;
@synthesize renderQuality;
@synthesize style = _style;
@synthesize renderingStyle = _renderingStyle;
@synthesize intrinsicSize = _intrinsicSize;
- (void)dealloc
{
IJSVGBeginTransactionLock();
// this can all be called on the background thread to be released
BOOL hasTransaction = IJSVGBeginTransaction();
(void)([renderingBackingScaleHelper release]),
renderingBackingScaleHelper = nil;
(void)([_group release]), _group = nil;
(void)([_layerTree release]), _layerTree = nil;
(void)([_replacementColors release]), _replacementColors = nil;
(void)([_style release]), _style = nil;
IJSVGEndTransactionLock();
(void)([_renderingStyle release]), _renderingStyle = nil;
(void)([_group release]), _group = nil;
(void)([_intrinsicSize release]), _intrinsicSize = nil;
(void)([_desc release]), _desc = nil;
(void)([_title release]), _title = nil;
// kill any memory that has been around
(void)([_layerTree release]), _layerTree = nil;
[super dealloc];
}
+ (id)svgNamed:(NSString*)string error:(NSError**)error
{
return [self.class svgNamed:string error:error delegate:nil];
}
+ (id)svgNamed:(NSString*)string
{
return [self.class svgNamed:string error:nil];
}
+ (id)svgNamed:(NSString*)string
useCache:(BOOL)useCache
delegate:(id<IJSVGDelegate>)delegate
{
return [self.class svgNamed:string
useCache:useCache
error:nil
delegate:delegate];
}
+ (id)svgNamed:(NSString*)string delegate:(id<IJSVGDelegate>)delegate
{
return [self.class svgNamed:string error:nil delegate:delegate];
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
}
+ (id)svgNamed:(NSString*)string
error:(NSError**)error
delegate:(id<IJSVGDelegate>)delegate
{
return [self svgNamed:string useCache:YES error:error delegate:delegate];
return [self.class svgNamed:string
error:error
delegate:nil];
}
+ (id)svgNamed:(NSString*)string
{
return [self.class svgNamed:string
error:nil];
}
+ (id)svgNamed:(NSString*)string
delegate:(id<IJSVGDelegate>)delegate
{
return [self.class svgNamed:string
error:nil
delegate:delegate];
}
+ (id)svgNamed:(NSString*)string
useCache:(BOOL)useCache
error:(NSError**)error
delegate:(id<IJSVGDelegate>)delegate
{
@@ -78,7 +77,6 @@
ofType:ext])
!= nil) {
return [[[self alloc] initWithFile:str
useCache:useCache
error:error
delegate:delegate] autorelease];
}
@@ -90,20 +88,23 @@
__block IJSVGGroupLayer* layer = nil;
__block IJSVGImageLayer* imageLayer = nil;
// make sure we obtain a lock, with whatever we do with layers!
IJSVGBeginTransactionLock();
// create the layers we require
BOOL hasTransaction = IJSVGBeginTransaction();
layer = [[[IJSVGGroupLayer alloc] init] autorelease];
imageLayer =
[[[IJSVGImageLayer alloc] initWithImage:image] autorelease];
[layer addSublayer:imageLayer];
IJSVGEndTransactionLock();
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
// return the initialized SVG
return [self initWithSVGLayer:layer viewBox:imageLayer.frame];
return [self initWithSVGLayer:layer
viewBox:imageLayer.frame];
}
- (id)initWithSVGLayer:(IJSVGGroupLayer*)group viewBox:(NSRect)viewBox
- (id)initWithSVGLayer:(IJSVGGroupLayer*)group
viewBox:(NSRect)viewBox
{
// this completely bypasses passing of files
if ((self = [super init]) != nil) {
@@ -119,91 +120,63 @@
- (id)initWithFile:(NSString*)file
{
return [self initWithFile:file delegate:nil];
}
- (id)initWithFile:(NSString*)file useCache:(BOOL)useCache
{
return [self initWithFile:file useCache:useCache error:nil delegate:nil];
}
- (id)initWithFile:(NSString*)file
useCache:(BOOL)useCache
error:(NSError**)error
delegate:(id<IJSVGDelegate>)delegate
{
return [self initWithFilePathURL:[NSURL fileURLWithPath:file]
useCache:useCache
error:error
delegate:delegate];
}
- (id)initWithFile:(NSString*)file error:(NSError**)error
{
return [self initWithFile:file error:error delegate:nil];
}
- (id)initWithFile:(NSString*)file delegate:(id<IJSVGDelegate>)delegate
{
return [self initWithFile:file error:nil delegate:delegate];
return [self initWithFile:file
error:nil
delegate:nil];
}
- (id)initWithFile:(NSString*)file
error:(NSError**)error
delegate:(id<IJSVGDelegate>)delegate
{
return [self initWithFilePathURL:[NSURL fileURLWithPath:file]
useCache:YES
return [self initWithFilePathURL:[NSURL fileURLWithPath:file isDirectory:NO]
error:error
delegate:delegate];
}
- (id)initWithFile:(NSString*)file
error:(NSError**)error
{
return [self initWithFile:file
error:error
delegate:nil];
}
- (id)initWithFile:(NSString*)file
delegate:(id<IJSVGDelegate>)delegate
{
return [self initWithFile:file
error:nil
delegate:delegate];
}
- (id)initWithFilePathURL:(NSURL*)aURL
{
return [self initWithFilePathURL:aURL useCache:YES error:nil delegate:nil];
}
- (id)initWithFilePathURL:(NSURL*)aURL error:(NSError**)error
{
return [self initWithFilePathURL:aURL
useCache:YES
error:error
delegate:nil];
}
- (id)initWithFilePathURL:(NSURL*)aURL useCache:(BOOL)useCache
{
return [self initWithFilePathURL:aURL
useCache:useCache
error:nil
delegate:nil];
}
- (id)initWithFilePathURL:(NSURL*)aURL delegate:(id<IJSVGDelegate>)delegate
- (id)initWithFilePathURL:(NSURL*)aURL
error:(NSError**)error
{
return [self initWithFilePathURL:aURL
error:error
delegate:nil];
}
- (id)initWithFilePathURL:(NSURL*)aURL
delegate:(id<IJSVGDelegate>)delegate
{
return [self initWithFilePathURL:aURL
useCache:YES
error:nil
delegate:delegate];
}
- (id)initWithFilePathURL:(NSURL*)aURL
useCache:(BOOL)useCache
error:(NSError**)error
delegate:(id<IJSVGDelegate>)delegate
{
#ifndef __clang_analyzer__
// check the cache first
if (useCache && [IJSVGCache enabled]) {
IJSVG* svg = nil;
if ((svg = [IJSVGCache cachedSVGForFileURL:aURL]) != nil) {
// have to release, as this was called from an alloc..!
[self release];
return [svg retain];
}
}
// create the object
if ((self = [super init]) != nil) {
NSError* anError = nil;
@@ -214,8 +187,9 @@
[self _checkDelegate];
// create the group
_group = [[IJSVGParser groupForFileURL:aURL error:&anError delegate:self]
retain];
_group = [[IJSVGParser groupForFileURL:aURL
error:&anError
delegate:self] retain];
[self _setupBasicInfoFromGroup];
[self _setupBasicsFromAnyInitializer];
@@ -228,24 +202,38 @@
(void)([self release]), self = nil;
return nil;
}
// cache the file
if (useCache && [IJSVGCache enabled]) {
[IJSVGCache cacheSVG:self fileURL:aURL];
}
}
#endif
return self;
}
- (id)initWithSVGData:(NSData*)data
{
return [self initWithSVGData:data
error:nil];
}
- (id)initWithSVGData:(NSData*)data
error:(NSError**)error
{
NSString* svgString = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
return [self initWithSVGString:svgString.autorelease
error:error];
}
- (id)initWithSVGString:(NSString*)string
{
return [self initWithSVGString:string error:nil delegate:nil];
return [self initWithSVGString:string
error:nil
delegate:nil];
}
- (id)initWithSVGString:(NSString*)string error:(NSError**)error
- (id)initWithSVGString:(NSString*)string
error:(NSError**)error
{
return [self initWithSVGString:string error:error delegate:nil];
return [self initWithSVGString:string
error:error
delegate:nil];
}
- (id)initWithSVGString:(NSString*)string
@@ -279,6 +267,15 @@
return self;
}
- (void)performBlock:(dispatch_block_t)block
{
BOOL hasTransaction = IJSVGBeginTransaction();
block();
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
}
- (void)discardDOM
{
// if we discard, we can no longer create a tree, so lets create tree
@@ -291,24 +288,43 @@
- (void)_setupBasicInfoFromGroup
{
// store the viewbox
_viewBox = _group.viewBox;
_proposedViewSize = _group.proposedViewSize;
_intrinsicSize = _group.intrinsicSize.retain;
}
- (void)_setupBasicsFromAnyInitializer
{
self.style = [[[IJSVGRenderingStyle alloc] init] autorelease];
self.renderingStyle = [[[IJSVGRenderingStyle alloc] init] autorelease];
self.clipToViewport = YES;
self.renderQuality = kIJSVGRenderQualityFullResolution;
// setup low level backing scale
_lastProposedBackingScale = 0.f;
self.renderingBackingScaleHelper = ^CGFloat {
return 1.f;
return NSScreen.mainScreen.backingScaleFactor;
};
}
- (void)setTitle:(NSString*)title
{
_group.title = title;
}
- (NSString*)title
{
return _group.title;
}
- (void)setDesc:(NSString*)description
{
_group.desc = description;
}
- (NSString*)desc
{
return _group.desc;
}
- (NSString*)identifier
{
return _group.identifier;
@@ -351,51 +367,117 @@
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options
{
IJSVGExporter* exporter =
[[[IJSVGExporter alloc] initWithSVG:self
size:self.viewBox.size
options:options] autorelease];
IJSVGExporter* exporter = [[[IJSVGExporter alloc] initWithSVG:self
size:self.viewBox.size
options:options] autorelease];
return [exporter SVGString];
}
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
IJSVGExporter* exporter = [[[IJSVGExporter alloc] initWithSVG:self
size:self.viewBox.size
options:options
floatingPointOptions:floatingPointOptions] autorelease];
return [exporter SVGString];
}
- (NSImage*)imageWithSize:(NSSize)aSize
{
return [self imageWithSize:aSize flipped:NO error:nil];
return [self imageWithSize:aSize
flipped:NO
error:nil];
}
- (NSImage*)imageWithSize:(NSSize)aSize error:(NSError**)error;
- (NSImage*)imageWithSize:(NSSize)aSize
error:(NSError**)error;
{
return [self imageWithSize:aSize flipped:NO error:error];
return [self imageWithSize:aSize
flipped:NO
error:error];
}
- (NSImage*)imageWithSize:(NSSize)aSize flipped:(BOOL)flipped
- (NSImage*)imageWithSize:(NSSize)aSize
flipped:(BOOL)flipped
{
return [self imageWithSize:aSize flipped:flipped error:nil];
return [self imageWithSize:aSize
flipped:flipped
error:nil];
}
- (NSSize)computeSVGSizeWithRenderSize:(NSSize)size
{
IJSVGUnitSize* svgSize = _intrinsicSize;
return NSMakeSize([svgSize.width computeValue:size.width],
[svgSize.height computeValue:size.height]);
}
- (NSRect)computeOriginalDrawingFrameWithSize:(NSSize)aSize
{
[self _beginDraw:(NSRect){ .origin = CGPointZero, .size = aSize }];
return NSMakeRect(0.f, 0.f, _proposedViewSize.width * _clipScale,
_proposedViewSize.height * _clipScale);
NSSize propSize = [self computeSVGSizeWithRenderSize:aSize];
[self _beginDraw:(NSRect) { .origin = CGPointZero, .size = aSize }];
return NSMakeRect(0.f, 0.f, propSize.width * _clipScale,
propSize.height * _clipScale);
}
- (CGImageRef)newCGImageRefWithSize:(CGSize)size
flipped:(BOOL)flipped
error:(NSError**)error
{
// setup the drawing rect, this is used for both the intial drawing
// and the backing scale helper block
NSRect rect = (CGRect) {
.origin = CGPointZero,
.size = (CGSize)size
};
// this is highly important this is setup
[self _beginDraw:rect];
// make sure we setup the scale based on the backing scale factor
CGFloat scale = [self backingScaleFactor:NULL];
// create the context and colorspace
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef ref = CGBitmapContextCreate(NULL, (int)size.width * scale,
(int)size.height * scale, 8, 0, colorSpace,
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
// scale the context
CGContextScaleCTM(ref, scale, scale);
if (flipped == YES) {
CGContextTranslateCTM(ref, 0.f, size.height);
CGContextScaleCTM(ref, 1.f, -1.f);
}
// draw the SVG into the context
[self _drawInRect:rect
context:ref
error:error];
// create the image from the context
CGImageRef imageRef = CGBitmapContextCreateImage(ref);
// release all things!
CGColorSpaceRelease(colorSpace);
CGContextRelease(ref);
return imageRef;
}
- (NSImage*)imageWithSize:(NSSize)aSize
flipped:(BOOL)flipped
error:(NSError**)error
{
NSImage* im = [[[NSImage alloc] initWithSize:aSize] autorelease];
[im lockFocus];
CGContextRef ref = [[NSGraphicsContext currentContext] CGContext];
CGContextSaveGState(ref);
if (flipped) {
CGContextTranslateCTM(ref, 0.f, aSize.height);
CGContextScaleCTM(ref, 1.f, -1.f);
}
[self drawAtPoint:NSMakePoint(0.f, 0.f) size:aSize error:error];
CGContextRestoreGState(ref);
[im unlockFocus];
return im;
CGImageRef ref = [self newCGImageRefWithSize:aSize
flipped:flipped
error:error];
NSImage* image = [[NSImage alloc] initWithCGImage:ref
size:aSize];
CGImageRelease(ref);
return image.autorelease;
}
- (NSImage*)imageByMaintainingAspectRatioWithSize:(NSSize)aSize
@@ -414,7 +496,7 @@
- (NSData*)PDFData:(NSError**)error
{
return [self
PDFDataWithRect:(NSRect){ .origin = NSZeroPoint, .size = _viewBox.size }
PDFDataWithRect:(NSRect) { .origin = NSZeroPoint, .size = _viewBox.size }
error:error];
}
@@ -423,7 +505,8 @@
return [self PDFDataWithRect:rect error:nil];
}
- (NSData*)PDFDataWithRect:(NSRect)rect error:(NSError**)error
- (NSData*)PDFDataWithRect:(NSRect)rect
error:(NSError**)error
{
// create the data for the PDF
NSMutableData* data = [[[NSMutableData alloc] init] autorelease];
@@ -505,15 +588,21 @@
};
}
- (BOOL)drawAtPoint:(NSPoint)point size:(NSSize)aSize
- (BOOL)drawAtPoint:(NSPoint)point
size:(NSSize)aSize
{
return [self drawAtPoint:point size:aSize error:nil];
return [self drawAtPoint:point
size:aSize
error:nil];
}
- (BOOL)drawAtPoint:(NSPoint)point size:(NSSize)aSize error:(NSError**)error
- (BOOL)drawAtPoint:(NSPoint)point
size:(NSSize)aSize
error:(NSError**)error
{
return
[self drawInRect:NSMakeRect(point.x, point.y, aSize.width, aSize.height)
[self drawInRect:NSMakeRect(point.x, point.y,
aSize.width, aSize.height)
error:error];
}
@@ -522,10 +611,17 @@
return [self drawInRect:rect error:nil];
}
- (BOOL)drawInRect:(NSRect)rect error:(NSError**)error
- (BOOL)drawInRect:(NSRect)rect
error:(NSError**)error
{
CGContextRef currentCGContext;
if (@available(macOS 10.10, *)) {
currentCGContext = NSGraphicsContext.currentContext.CGContext;
} else {
currentCGContext = NSGraphicsContext.currentContext.graphicsPort;
}
return [self _drawInRect:rect
context:[[NSGraphicsContext currentContext] CGContext]
context:currentCGContext
error:error];
}
@@ -535,16 +631,18 @@
return (CGFloat)(_scale + actualScale);
}
- (NSRect)computeRectDrawingInRect:(NSRect)rect isValid:(BOOL*)valid
- (NSRect)computeRectDrawingInRect:(NSRect)rect
isValid:(BOOL*)valid
{
// we also need to calculate the viewport so we can clip
// the drawing if needed
NSRect viewPort = NSZeroRect;
viewPort.origin.x = round((rect.size.width / 2 - (_proposedViewSize.width / 2) * _clipScale) + rect.origin.x);
NSSize propSize = [self computeSVGSizeWithRenderSize:rect.size];
viewPort.origin.x = round((rect.size.width / 2 - (propSize.width / 2) * _clipScale) + rect.origin.x);
viewPort.origin.y = round(
(rect.size.height / 2 - (_proposedViewSize.height / 2) * _clipScale) + rect.origin.y);
viewPort.size.width = _proposedViewSize.width * _clipScale;
viewPort.size.height = _proposedViewSize.height * _clipScale;
(rect.size.height / 2 - (propSize.height / 2) * _clipScale) + rect.origin.y);
viewPort.size.width = propSize.width * _clipScale;
viewPort.size.height = propSize.height * _clipScale;
// check the viewport
if (NSEqualRects(_viewBox, NSZeroRect) || _viewBox.size.width <= 0 || _viewBox.size.height <= 0 || NSEqualRects(NSZeroRect, viewPort) || CGRectIsEmpty(viewPort) || CGRectIsNull(viewPort) || viewPort.size.width <= 0 || viewPort.size.height <= 0) {
@@ -556,7 +654,8 @@
return viewPort;
}
- (void)drawInRect:(NSRect)rect context:(CGContextRef)context
- (void)drawInRect:(NSRect)rect
context:(CGContextRef)context
{
[self _drawInRect:rect context:context error:nil];
}
@@ -595,14 +694,9 @@
CGContextTranslateCTM(ref, viewPort.origin.x, viewPort.origin.y);
CGContextScaleCTM(ref, _scale, _scale);
// render the layer, its really important we lock
// the transaction when drawing
IJSVGBeginTransactionLock();
// do we need to update the backing scales on the
// layers?
if (self.renderingBackingScaleHelper != nil) {
[self _askHelperForBackingScale];
}
[self backingScaleFactor:nil];
CGInterpolationQuality quality;
switch (self.renderQuality) {
@@ -619,8 +713,11 @@
}
}
CGContextSetInterpolationQuality(ref, quality);
BOOL hasTransaction = IJSVGBeginTransaction();
[self.layer renderInContext:ref];
IJSVGEndTransactionLock();
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
}
} @catch (NSException* exception) {
// just catch and give back a drawing error to the caller
@@ -634,12 +731,14 @@
return (error == nil);
}
- (void)_askHelperForBackingScale
- (CGFloat)backingScaleFactor:(CGFloat* _Nullable)proposedBackingScale
{
CGFloat scale = (self.renderingBackingScaleHelper)();
__block CGFloat scale = 1.f;
scale = (self.renderingBackingScaleHelper)();
if (scale < 1.f) {
scale = 1.f;
}
_backingScaleFactor = scale;
// make sure we multiple the scale by the scale of the rendered clip
// or it will be blurry for gradients and other bitmap drawing
@@ -648,12 +747,15 @@
// dont do anything, nothing has changed, no point of iterating over
// every layer for no reason!
if (scale == _lastProposedBackingScale && renderQuality == _lastProposedRenderQuality) {
return;
return _backingScaleFactor;
}
IJSVGRenderQuality quality = self.renderQuality;
_lastProposedBackingScale = scale;
_lastProposedRenderQuality = quality;
if (proposedBackingScale != nil && proposedBackingScale != NULL) {
*proposedBackingScale = scale;
}
// walk the tree
void (^block)(CALayer* layer, BOOL isMask) = ^void(CALayer* layer, BOOL isMask) {
@@ -665,20 +767,27 @@
};
// gogogo
BOOL hasTransaction = IJSVGBeginTransaction();
[IJSVGLayer recursivelyWalkLayer:self.layer withBlock:block];
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
return _backingScaleFactor;
}
- (IJSVGLayer*)layerWithTree:(IJSVGLayerTree*)tree
{
// clear memory
BOOL hasTransaction = IJSVGBeginTransaction();
if (_layerTree != nil) {
(void)([_layerTree release]), _layerTree = nil;
}
// force rebuild of the tree
IJSVGBeginTransactionLock();
_layerTree = [[tree layerForNode:_group] retain];
IJSVGEndTransactionLock();
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
return _layerTree;
}
@@ -692,16 +801,16 @@
// from this SVG object
IJSVGLayerTree* renderer = [[[IJSVGLayerTree alloc] init] autorelease];
renderer.viewBox = self.viewBox;
renderer.style = self.style;
renderer.style = self.renderingStyle;
// return the rendered layer
return [self layerWithTree:renderer];
}
- (void)setStyle:(IJSVGRenderingStyle*)style
- (void)setRenderingStyle:(IJSVGRenderingStyle*)style
{
(void)([_style release]), _style = nil;
_style = style.retain;
(void)([_renderingStyle release]), _renderingStyle = nil;
_renderingStyle = style.retain;
}
- (void)observeValueForKeyPath:(NSString*)keyPath
@@ -710,7 +819,7 @@
context:(void*)context
{
// invalidate the tree if a style is set
if (object == _style) {
if (object == _renderingStyle) {
[self invalidateLayerTree];
}
}
@@ -782,12 +891,13 @@
// to transform the paths into our viewbox
NSSize dest = rect.size;
NSSize source = _viewBox.size;
_clipScale = MIN(dest.width / _proposedViewSize.width,
dest.height / _proposedViewSize.height);
NSSize propSize = [self computeSVGSizeWithRenderSize:rect.size];
_clipScale = MIN(dest.width / propSize.width,
dest.height / propSize.height);
// work out the actual scale based on the clip scale
CGFloat w = _proposedViewSize.width * _clipScale;
CGFloat h = _proposedViewSize.height * _clipScale;
CGFloat w = propSize.width * _clipScale;
CGFloat h = propSize.height * _clipScale;
_scale = MIN(w / source.width, h / source.height);
}
@@ -813,7 +923,9 @@
withSVGString:(NSString*)string
{
if (_delegate != nil && _respondsTo.shouldHandleSubSVG == 1) {
[_delegate svg:self foundSubSVG:subSVG withSVGString:string];
[_delegate svg:self
foundSubSVG:subSVG
withSVGString:string];
}
}
@@ -821,7 +933,8 @@
shouldHandleForeignObject:(IJSVGForeignObject*)foreignObject
{
if (_delegate != nil && _respondsTo.shouldHandleForeignObject == 1) {
return [_delegate svg:self shouldHandleForeignObject:foreignObject];
return [_delegate svg:self
shouldHandleForeignObject:foreignObject];
}
return NO;
}
@@ -831,7 +944,9 @@
document:(NSXMLDocument*)document
{
if (_delegate != nil && _respondsTo.handleForeignObject == 1) {
[_delegate svg:self handleForeignObject:foreignObject document:document];
[_delegate svg:self
handleForeignObject:foreignObject
document:document];
}
}
@@ -104,7 +104,7 @@
+ (IJSVG*)convertIJSVGPathToSVG:(IJSVGPath*)path
{
CGPathRef cgPath = [IJSVGUtils newCGPathFromBezierPath:path.path];
CGPathRef cgPath = [path.path newCGPathRef:NO];
CGPathRef flippedPath = [IJSVGUtils newFlippedCGPath:cgPath];
IJSVG* svg = [self convertPathToSVG:flippedPath];
CGPathRelease(flippedPath);
@@ -115,7 +115,6 @@
+ (IJSVG*)convertPathToSVG:(CGPathRef)path
{
__block IJSVG* svg = nil;
IJSVGBeginTransactionLock();
IJSVGGroupLayer* layer = [[[IJSVGGroupLayer alloc] init] autorelease];
IJSVGShapeLayer* shape = [[[IJSVGShapeLayer alloc] init] autorelease];
[layer addSublayer:shape];
@@ -123,7 +122,6 @@
CGRect box = CGPathGetPathBoundingBox(path);
svg = [[IJSVG alloc] initWithSVGLayer:layer
viewBox:box];
IJSVGEndTransactionLock();
return [svg autorelease];
}
@@ -17,6 +17,9 @@
IJSVG* _svg;
}
- (instancetype)initWithData:(NSData*)data;
@property (nonatomic, readonly) CGRect viewBox;
@property (nonatomic, readonly) IJSVG* SVG;
@end
@@ -11,8 +11,6 @@
@implementation IJSVGImageRep
@synthesize viewBox = _viewBox;
+ (void)load
{
[NSBitmapImageRep registerImageRepClass:self];
@@ -25,12 +23,20 @@
+ (NSArray<NSString*>*)imageTypes
{
return @[ (NSString*)kUTTypeScalableVectorGraphics, @"svg" ];
if (@available(macOS 10.10, *)) {
return @[ (NSString*)kUTTypeScalableVectorGraphics, @"svg" ];
} else {
return @[ @"public.svg-image", @"svg" ];
}
}
+ (NSArray<NSString*>*)imageUnfilteredTypes
{
return @[ (NSString*)kUTTypeScalableVectorGraphics, @"svg" ];
if (@available(macOS 10.10, *)) {
return @[ (NSString*)kUTTypeScalableVectorGraphics, @"svg" ];
} else {
return @[ @"public.svg-image", @"svg" ];
}
}
+ (NSArray<NSImageRep*>*)imageRepsWithData:(NSData*)data
@@ -101,4 +107,9 @@
return _svg.viewBox;
}
- (IJSVG*)SVG
{
return _svg;
}
@end
@@ -42,7 +42,7 @@
if (imageName != nil) {
IJSVG* anSVG = [IJSVG svgNamed:imageName];
if (tintColor != nil) {
anSVG.style.fillColor = tintColor;
anSVG.renderingStyle.fillColor = tintColor;
}
self.SVG = anSVG;
}
@@ -7,6 +7,7 @@
//
#import <Foundation/Foundation.h>
#import "IJSVGUtils.h"
@class IJSVG;
@@ -32,11 +33,19 @@ typedef NS_OPTIONS(NSInteger, IJSVGExporterOptions) {
IJSVGExporterOptionRemoveWidthHeightAttributes = 1 << 13,
IJSVGExporterOptionColorAllowRRGGBBAA = 1 << 14,
IJSVGExporterOptionRemoveComments = 1 << 15,
IJSVGExporterOptionAll = IJSVGExporterOptionRemoveUselessDef | IJSVGExporterOptionRemoveUselessGroups | IJSVGExporterOptionCreateUseForPaths | IJSVGExporterOptionMoveAttributesToGroup | IJSVGExporterOptionSortAttributes | IJSVGExporterOptionCollapseGroups | IJSVGExporterOptionCleanupPaths | IJSVGExporterOptionRemoveHiddenElements | IJSVGExporterOptionScaleToSizeIfNecessary | IJSVGExporterOptionCompressOutput | IJSVGExporterOptionCollapseGradients | IJSVGExporterOptionRemoveWidthHeightAttributes | IJSVGExporterOptionColorAllowRRGGBBAA | IJSVGExporterOptionRemoveComments
IJSVGExporterOptionCenterWithinViewBox = 1 << 16,
IJSVGExporterOptionRemoveXMLDeclaration = 1 << 17,
IJSVGExporterOptionConvertArcs = 1 << 18,
IJSVGExporterOptionConvertShapesToPaths = 1 << 19,
IJSVGExporterOptionRoundTransforms = 1 << 20,
IJSVGExporterOptionRemoveDefaultValues = 1 << 21,
IJSVGExporterOptionAll = IJSVGExporterOptionRemoveUselessDef | IJSVGExporterOptionRemoveUselessGroups | IJSVGExporterOptionCreateUseForPaths | IJSVGExporterOptionMoveAttributesToGroup | IJSVGExporterOptionSortAttributes | IJSVGExporterOptionCollapseGroups | IJSVGExporterOptionCleanupPaths | IJSVGExporterOptionRemoveHiddenElements | IJSVGExporterOptionScaleToSizeIfNecessary | IJSVGExporterOptionCompressOutput | IJSVGExporterOptionCollapseGradients | IJSVGExporterOptionRemoveWidthHeightAttributes | IJSVGExporterOptionColorAllowRRGGBBAA | IJSVGExporterOptionRemoveComments | IJSVGExporterOptionCenterWithinViewBox | IJSVGExporterOptionRemoveXMLDeclaration | IJSVGExporterOptionConvertArcs | IJSVGExporterOptionConvertShapesToPaths | IJSVGExporterOptionRoundTransforms | IJSVGExporterOptionRemoveDefaultValues
};
BOOL IJSVGExporterHasOption(IJSVGExporterOptions options, NSInteger option);
void IJSVGEnumerateCGPathElements(CGPathRef path, IJSVGPathElementEnumerationBlock enumBlock);
const NSArray<NSString*>* IJSVGShortCharacterArray(void);
const NSDictionary<NSString*, NSString*>* IJSVGDefaultAttributes(void);
@interface IJSVGExporter : NSObject {
@@ -46,18 +55,25 @@ void IJSVGEnumerateCGPathElements(CGPathRef path, IJSVGPathElementEnumerationBlo
IJSVGExporterOptions _options;
NSXMLDocument* _dom;
NSXMLElement* _defElement;
NSXMLElement* _scaledRootNode;
NSInteger _idCount;
NSInteger _shortIdCount;
BOOL _appliedXLink;
}
@property (nonatomic, assign) IJSVGFloatingPointOptions floatingPointOptions;
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* description;
- (id)initWithSVG:(IJSVG*)svg
size:(CGSize)size
options:(IJSVGExporterOptions)options;
- (id)initWithSVG:(IJSVG*)svg
size:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (NSString*)SVGString;
- (NSData*)SVGData;
- (IJSVG*)SVG:(NSError**)error;
@end
File diff suppressed because it is too large Load Diff
@@ -8,25 +8,60 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef struct {
char instruction;
NSArray<NSString*>* params;
} IJSVGExporterPathInstructionCommand;
typedef struct {
CGPoint center;
CGFloat radius;
} IJSVGExporterPathInstructionCircle;
@interface IJSVGExporterPathInstruction : NSObject {
@private
NSInteger _dataCount;
char _instruction;
CGFloat* _data;
CGFloat* _base;
CGFloat* _coords;
}
+ (NSArray<IJSVGExporterPathInstruction*>*)instructionsFromPath:(CGPathRef)path;
@property (nonatomic, assign) char instruction;
void IJSVGExporterPathInstructionRoundData(CGFloat* data, NSInteger length, IJSVGFloatingPointOptions options);
CGFloat IJSVGExporterPathFloatToFixed(CGFloat number, int precision);
IJSVGExporterPathInstructionCommand* IJSVGExporterPathInstructionCommandCopy(IJSVGExporterPathInstructionCommand command);
void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand* _Nullable command);
+ (NSArray<IJSVGExporterPathInstruction*>*)instructionsFromPath:(CGPathRef)path
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (id)initWithInstruction:(char)instruction
dataCount:(NSInteger)floatCount;
- (void)setInstruction:(char)newInstruction;
- (char)instruction;
- (CGFloat*)data;
- (NSInteger)dataLength;
+ (void)convertInstructionsToRelativeCoordinates:(NSArray<IJSVGExporterPathInstruction*>*)instructions;
+ (NSString*)pathStringFromInstructions:(NSArray<IJSVGExporterPathInstruction*>*)instructions;
+ (NSArray<IJSVGExporterPathInstruction*>*)convertInstructionsCurves:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (void)convertInstructionsToMixedAbsoluteRelative:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (void)convertInstructionsDataToRounded:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (void)convertInstructionsToRelativeCoordinates:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (NSString*)pathStringFromInstructions:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (NSString*)pathStringWithInstructionSet:(NSArray<NSValue*>*)instructionSets
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
@end
NS_ASSUME_NONNULL_END
static NSInteger const kIJSVGExporterPathInstructionFloatPrecision = 3;
static CGFloat const kIJSVGExporterPathInstructionErrorThreshold = 1e-2;
#define IJ_SVG_EXPORT_ROUND(value) IJSVGExporterPathFloatToFixed(value, kIJSVGExporterPathInstructionFloatPrecision)
@@ -8,13 +8,23 @@
#import "IJSVGExporter.h"
#import "IJSVGExporterPathInstruction.h"
#import "IJSVGUtils.h"
#import <math.h>
@implementation IJSVGExporterPathInstruction
@synthesize instruction = _instruction;
- (void)dealloc
{
if (_data != NULL) {
free(_data);
(void)free(_data), _data = NULL;
}
if (_base != NULL) {
(void)free(_base), _base = NULL;
}
if (_coords != NULL) {
(void)free(_coords), _coords = NULL;
}
[super dealloc];
}
@@ -27,8 +37,13 @@
// only allocate if not zero
if (floatCount != 0) {
_dataCount = floatCount;
_data = (CGFloat*)calloc(sizeof(CGFloat), floatCount);
}
// setup base and coords
_base = (CGFloat*)malloc(sizeof(CGFloat) * 2);
_coords = (CGFloat*)malloc(sizeof(CGFloat) * 2);
}
return self;
}
@@ -38,108 +53,343 @@
return _dataCount;
}
- (void)setInstruction:(char)newInstruction
{
_instruction = newInstruction;
}
- (char)instruction
{
return _instruction;
}
- (CGFloat*)data
{
return _data;
}
+ (NSString*)pathStringFromInstructions:(NSArray<IJSVGExporterPathInstruction*>*)instructions
- (CGFloat*)base
{
NSMutableArray* pathData = [[[NSMutableArray alloc] init] autorelease];
return _base;
}
- (CGFloat*)coords
{
return _coords;
}
IJSVGExporterPathInstructionCommand* IJSVGExporterPathInstructionCommandCopy(IJSVGExporterPathInstructionCommand command)
{
IJSVGExporterPathInstructionCommand* copy = NULL;
copy = (IJSVGExporterPathInstructionCommand*)malloc(sizeof(IJSVGExporterPathInstructionCommand));
copy->instruction = command.instruction;
copy->params = command.params;
return copy;
}
void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand* _Nullable command)
{
if (command != NULL) {
free(command);
}
}
+ (NSString*)pathStringWithInstructionSet:(NSArray<NSValue*>*)instructionSets
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
IJSVGExporterPathInstructionCommand* lastCommand = NULL;
NSMutableString* string = [[[NSMutableString alloc] init] autorelease];
for (NSValue* value in instructionSets) {
// read back the bytes
IJSVGExporterPathInstructionCommand command;
[value getValue:&command];
// add on the instruction character only if there is no current command
// or the last command is not the same as the current command
// if they both are the same, we still need to seperate them via a space
if (lastCommand == nil || (lastCommand != nil && lastCommand->instruction != command.instruction)) {
[string appendFormat:@"%c", command.instruction];
} else {
[string appendString:@" "];
}
// compresses the floats
NSString* compressedFloats = IJSVGCompressFloatParameterArray(command.params);
[string appendString:compressedFloats];
// store last command
IJSVGExporterPathInstructionCommandFree(lastCommand);
lastCommand = IJSVGExporterPathInstructionCommandCopy(command);
}
IJSVGExporterPathInstructionCommandFree(lastCommand);
return string;
}
+ (NSString*)pathStringFromInstructions:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
NSMutableArray* pathInstructions = [[[NSMutableArray alloc] init] autorelease];
for (IJSVGExporterPathInstruction* instruction in instructions) {
CGFloat* data = instruction.data;
NSString* str = nil;
switch (instruction.instruction) {
// move
case 'M':
case 'm': {
char* buffer;
asprintf(&buffer, "%c%g,%g", instruction.instruction, data[0], data[1]);
str = [NSString stringWithCString:buffer
encoding:NSUTF8StringEncoding];
free(buffer);
[pathData addObject:str];
break;
}
// vertical and horizonal line
case 'V':
case 'v':
case 'H':
case 'h': {
char* buffer;
asprintf(&buffer, "%c%g", instruction.instruction, data[0]);
str = [NSString stringWithCString:buffer
encoding:NSUTF8StringEncoding];
free(buffer);
[pathData addObject:str];
break;
}
// line
case 'L':
const char lowerInstruction = tolower(instruction.instruction);
NSArray<NSString*>* set = nil;
switch (lowerInstruction) {
case 't':
case 'm':
case 'l': {
char* buffer;
asprintf(&buffer, "%c%g,%g", instruction.instruction, data[0], data[1]);
str = [NSString stringWithCString:buffer
encoding:NSUTF8StringEncoding];
free(buffer);
[pathData addObject:str];
set = @[
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions)
];
break;
}
case 'v':
case 'h': {
set = @[
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions)
];
break;
}
// curve
case 'C':
case 'c': {
char* buffer;
asprintf(&buffer, "%c%g,%g %g,%g %g,%g", instruction.instruction,
data[0], data[1], data[2], data[3], data[4], data[5]);
str = [NSString stringWithCString:buffer
encoding:NSUTF8StringEncoding];
free(buffer);
[pathData addObject:str];
set = @[
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[2], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[3], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[4], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[5], floatingPointOptions)
];
break;
}
// quadratic curve
case 'Q':
case 's':
case 'q': {
char* buffer;
asprintf(&buffer, "%c%g,%g %g,%g", instruction.instruction,
data[0], data[1], data[2], data[3]);
str = [NSString stringWithCString:buffer
encoding:NSUTF8StringEncoding];
free(buffer);
[pathData addObject:str];
set = @[
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[2], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[3], floatingPointOptions)
];
break;
}
case 'a': {
set = @[
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[2], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[3], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[4], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[5], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[6], floatingPointOptions),
];
break;
}
// close path
case 'Z':
case 'z': {
str = [NSString stringWithFormat:@"%c", instruction.instruction];
[pathData addObject:str];
set = @[];
}
}
// wrap into the command and give to the array
IJSVGExporterPathInstructionCommand wrapper;
wrapper.instruction = instruction.instruction;
wrapper.params = set ?: @[];
// encode and store
NSValue* value = [NSValue valueWithBytes:&wrapper
objCType:@encode(IJSVGExporterPathInstructionCommand)];
[pathInstructions addObject:value];
}
return [self pathStringWithInstructionSet:pathInstructions
floatingPointOptions:floatingPointOptions];
}
CGFloat IJSVGExporterPathFloatToFixed(CGFloat number, int precision)
{
return floorf(pow(10, precision) * number) / pow(10, precision);
}
void IJSVGExporterPathInstructionRoundData(CGFloat* data, NSInteger length,
IJSVGFloatingPointOptions options)
{
for (NSInteger i = length; i-- > 0;) {
CGFloat d = data[i];
CGFloat proposed = IJSVGExporterPathFloatToFixed(d, options.precision);
if (proposed != d) {
CGFloat rounded = +IJSVGExporterPathFloatToFixed(d, options.precision - 1);
data[i] = IJSVGExporterPathFloatToFixed(+fabs(rounded - d), options.precision + 1)
>= kIJSVGExporterPathInstructionErrorThreshold
? +IJSVGExporterPathFloatToFixed(d, options.precision)
: rounded;
}
}
return [pathData componentsJoinedByString:@""];
}
+ (void)convertInstructionsToRoundRelativeCoordinates:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
CGFloat relSubPoint[2] = { 0.f, 0.f };
for (IJSVGExporterPathInstruction* instruction in instructions) {
char instructionChar = instruction.instruction;
NSInteger length = instruction.dataLength;
CGFloat* data = instruction.data;
if (strchr("mltqsc", instructionChar) != NULL) {
for (NSInteger i = length; i--;) {
data[i] += instruction.base[i % 2] - relSubPoint[i % 2];
}
} else if (instructionChar == 'h') {
data[0] += instruction.base[0] - relSubPoint[0];
} else if (instructionChar == 'v') {
data[0] += instruction.base[1] - relSubPoint[1];
} else if (instructionChar == 'a') {
data[5] += instruction.base[0] - relSubPoint[0];
data[5] += instruction.base[1] - relSubPoint[1];
}
IJSVGExporterPathInstructionRoundData(data, length, floatingPointOptions);
if (instructionChar == 'h') {
relSubPoint[0] += data[0];
} else if (instructionChar == 'v') {
relSubPoint[1] += data[0];
} else {
relSubPoint[0] += data[length - 2];
relSubPoint[1] += data[length - 1];
}
IJSVGExporterPathInstructionRoundData(relSubPoint, 2, floatingPointOptions);
}
}
+ (void)convertInstructionsToMixedAbsoluteRelative:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
IJSVGExporterPathInstruction* prevInstruction = nil;
for (IJSVGExporterPathInstruction* instruction in instructions) {
if (prevInstruction == nil || instruction.dataLength == 0) {
prevInstruction = instruction;
continue;
}
char instructionChar = instruction.instruction;
CGFloat* data = instruction.data;
NSInteger length = instruction.dataLength;
CGFloat* adata = (CGFloat*)malloc(sizeof(CGFloat) * length);
memcpy(adata, data, sizeof(CGFloat) * length);
if (strchr("mltqsc", instructionChar) != NULL) {
for (NSInteger i = length; i--;) {
adata[i] += instruction.base[i % 2];
}
} else if (instructionChar == 'h') {
adata[0] += instruction.base[0];
} else if (instructionChar == 'v') {
adata[0] += instruction.base[1];
} else if (instructionChar == 'a') {
adata[5] += instruction.base[0];
adata[6] += instruction.base[1];
}
IJSVGExporterPathInstructionRoundData(adata, length, floatingPointOptions);
IJSVGExporterPathInstruction* ainstruction = nil;
ainstruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:instructionChar
dataCount:length] autorelease];
memcpy(ainstruction.data, adata, sizeof(CGFloat) * length);
// run these through our default string runner
// to compare the outputs
NSString* orig = [self pathStringFromInstructions:@[ instruction ]
floatingPointOptions:floatingPointOptions];
NSString* comp = [self pathStringFromInstructions:@[ ainstruction ]
floatingPointOptions:floatingPointOptions];
if (comp.length < orig.length && !(instructionChar == prevInstruction.instruction && prevInstruction.instruction > 96 && comp.length == orig.length - 1 && data[0] < 0.f && fmod(prevInstruction.data[prevInstruction.dataLength - 1], 1) != 0.f)) {
instruction.instruction = toupper(instructionChar);
memcpy(data, adata, sizeof(CGFloat) * length);
}
(void)free(adata), adata = NULL;
prevInstruction = instruction;
}
}
+ (void)convertInstructionsDataToRounded:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
for (IJSVGExporterPathInstruction* instruction in instructions) {
CGFloat* data = instruction.data;
IJSVGExporterPathInstructionRoundData(data, instruction.dataLength,
floatingPointOptions);
}
}
+ (NSArray<IJSVGExporterPathInstruction*>*)convertInstructionsCurves:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
NSMutableArray<IJSVGExporterPathInstruction*>* nInstructions = nil;
nInstructions = [[[NSMutableArray alloc] initWithCapacity:instructions.count] autorelease];
IJSVGExporterPathInstruction* lastInstruction = nil;
for (IJSVGExporterPathInstruction* instruction in instructions) {
lastInstruction = nInstructions.lastObject;
if (lastInstruction == nil) {
[nInstructions addObject:instruction];
continue;
}
if (instruction.instruction == 'c') {
if (lastInstruction.instruction == 'c' && instruction.data[0] == -(lastInstruction.data[2] - lastInstruction.data[4]) && instruction.data[1] == -(lastInstruction.data[3] - lastInstruction.data[5])) {
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'s'
dataCount:4] autorelease];
nInstruction.data[0] = instruction.data[2];
nInstruction.data[1] = instruction.data[3];
nInstruction.data[2] = instruction.data[4];
nInstruction.data[3] = instruction.data[5];
[nInstructions addObject:nInstruction];
continue;
} else if (lastInstruction.instruction == 's' && instruction.data[0] == -(lastInstruction.data[0] - lastInstruction.data[2]) && instruction.data[1] == -(lastInstruction.data[1] - lastInstruction.data[3])) {
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'s'
dataCount:4] autorelease];
nInstruction.data[0] = instruction.data[2];
nInstruction.data[1] = instruction.data[3];
nInstruction.data[2] = instruction.data[4];
nInstruction.data[3] = instruction.data[5];
[nInstructions addObject:nInstruction];
continue;
} else if (lastInstruction.instruction != 'c' && lastInstruction.instruction != 's' && instruction.data[0] == 0.f && instruction.data[1] == 0.f) {
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'s'
dataCount:4] autorelease];
nInstruction.data[0] = instruction.data[2];
nInstruction.data[1] = instruction.data[3];
nInstruction.data[2] = instruction.data[4];
nInstruction.data[3] = instruction.data[5];
[nInstructions addObject:nInstruction];
continue;
}
} else if (instruction.instruction == 'q') {
if (lastInstruction.instruction == 'q' && instruction.data[0] == (lastInstruction.data[2] - lastInstruction.data[0]) && instruction.data[1] == (lastInstruction.data[3] - lastInstruction.data[1])) {
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'t'
dataCount:2] autorelease];
nInstruction.data[0] = instruction.data[3];
nInstruction.data[1] = instruction.data[4];
[nInstructions addObject:nInstruction];
continue;
} else if (lastInstruction.instruction == 't' && instruction.data[2] == lastInstruction.data[0] && instruction.data[3] == lastInstruction.data[1]) {
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'t'
dataCount:2] autorelease];
nInstruction.data[0] = instruction.data[3];
nInstruction.data[1] = instruction.data[4];
[nInstructions addObject:nInstruction];
continue;
}
}
[nInstructions addObject:instruction];
}
return nInstructions;
}
+ (void)convertInstructionsToRelativeCoordinates:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
CGFloat point[2] = { 0, 0 };
CGFloat subpathPoint[2] = { 0, 0 };
IJSVGExporterPathInstruction* baseInstruction = nil;
IJSVGExporterPathInstruction* prevInstruction = nil;
NSInteger index = 0;
for (IJSVGExporterPathInstruction* anInstruction in instructions) {
@@ -158,6 +408,8 @@
if (instruction == 'm') {
subpathPoint[0] = point[0];
subpathPoint[1] = point[1];
baseInstruction = anInstruction;
}
} else if (instruction == 'h') {
@@ -178,8 +430,8 @@
subpathPoint[0] = point[0] += data[0];
subpathPoint[1] = point[1] += data[1];
baseInstruction = anInstruction;
} else if (instruction == 'L' || instruction == 'T') {
instruction = tolower(instruction);
data[0] -= point[0];
@@ -187,9 +439,7 @@
point[0] += data[0];
point[1] += data[1];
} else if (instruction == 'C') {
instruction = 'c';
data[0] -= point[0];
@@ -201,9 +451,7 @@
point[0] += data[4];
point[1] += data[5];
} else if (instruction == 'S' || instruction == 'Q') {
instruction = tolower(instruction);
data[0] -= point[0];
@@ -213,9 +461,7 @@
point[0] += data[2];
point[1] += data[3];
} else if (instruction == 'A') {
instruction = 'a';
data[5] -= point[0];
@@ -223,38 +469,51 @@
point[0] += data[5];
point[1] += data[6];
} else if (instruction == 'H') {
instruction = 'h';
data[0] -= point[0];
point[0] += data[0];
} else if (instruction == 'V') {
instruction = 'v';
data[0] -= point[1];
point[1] += data[0];
}
// reset the instruction
[anInstruction setInstruction:instruction];
anInstruction.instruction = instruction;
CGFloat* coords = anInstruction.coords;
coords[0] = point[0];
coords[1] = point[1];
} else if (instruction == 'Z' || instruction == 'z') {
if (baseInstruction != nil) {
CGFloat* coords = anInstruction.coords;
coords[0] = baseInstruction.coords[0];
coords[1] = baseInstruction.coords[1];
}
point[0] = subpathPoint[0];
point[1] = subpathPoint[1];
}
CGFloat* base = anInstruction.base;
if (prevInstruction != nil) {
base[0] = prevInstruction.coords[0];
base[1] = prevInstruction.coords[1];
} else {
base[0] = 0.f;
base[1] = 0.f;
}
// increment index
prevInstruction = anInstruction;
index++;
}
}
+ (NSArray<IJSVGExporterPathInstruction*>*)instructionsFromPath:(CGPathRef)path
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
// keep track of the current point
@@ -324,6 +583,7 @@
CGPoint controlPoint1 = pathElement->points[0];
CGPoint controlPoint2 = pathElement->points[1];
CGPoint point = pathElement->points[2];
currentPoint = point;
instruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'C'
dataCount:6] autorelease];
@@ -362,7 +622,7 @@
}
}
}
return instructions;
}
@@ -9,12 +9,13 @@
#import "IJSVGNode.h"
#import <Foundation/Foundation.h>
@interface IJSVGDef : IJSVGNode {
@interface IJSVGDef : NSObject {
@private
NSMutableDictionary* _dict;
}
- (void)addDef:(IJSVGNode*)aDef;
- (IJSVGDef*)defForID:(NSString*)anID;
@end
@@ -18,7 +18,7 @@
- (id)init
{
if ((self = [super initWithDef:NO]) != nil) {
if ((self = [super init]) != nil) {
_dict = [[NSMutableDictionary alloc] init];
}
return self;
@@ -29,13 +29,12 @@
if (aDef.identifier == nil) {
return;
}
[_dict setObject:aDef
forKey:aDef.identifier];
_dict[aDef.identifier] = aDef;
}
- (IJSVGDef*)defForID:(NSString*)anID
{
return [_dict objectForKey:anID];
return _dict[anID];
}
@end
@@ -43,6 +43,8 @@ typedef NS_ENUM(NSInteger, IJSVGNodeType) {
IJSVGNodeTypeTextSpan,
IJSVGNodeTypeStyle,
IJSVGNodeTypeSwitch,
IJSVGNodeTypeTitle,
IJSVGNodeTypeDesc,
IJSVGNodeTypeNotFound,
};
@@ -97,6 +99,8 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991;
@interface IJSVGNode : NSObject <NSCopying>
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* desc;
@property (nonatomic, assign) IJSVGNodeType type;
@property (nonatomic, copy) NSString* name;
@property (nonatomic, copy) NSString* className;
@@ -12,6 +12,8 @@
@implementation IJSVGNode
@synthesize title;
@synthesize desc;
@synthesize shouldRender;
@synthesize type;
@synthesize name;
@@ -73,6 +75,8 @@
(void)([identifier release]), identifier = nil;
(void)([def release]), def = nil;
(void)([name release]), name = nil;
(void)([title release]), title = nil;
(void)([desc release]), desc = nil;
(void)([className release]), className = nil;
(void)([classNameList release]), classNameList = nil;
(void)([fillPattern release]), fillPattern = nil;
@@ -133,6 +137,12 @@
if ([string isEqualToString:@"tspan"] || kind == NSXMLTextKind) {
return IJSVGNodeTypeTextSpan;
}
if([string isEqualToString:@"title"]) {
return IJSVGNodeTypeTitle;
}
if([string isEqualToString:@"desc"]) {
return IJSVGNodeTypeDesc;
}
// are we commong HTML? - if so just treat as a group
if (IJSVGIsCommonHTMLElementName(string) == YES) {
@@ -152,6 +162,9 @@
- (void)applyPropertiesFromNode:(IJSVGNode*)node
{
self.title = node.title;
self.desc = node.desc;
self.name = node.name;
self.type = node.type;
self.unicode = node.unicode;
+8 -13
View File
@@ -13,32 +13,27 @@
@class IJSVGGroup;
typedef NS_ENUM(NSInteger, IJSVGPrimitivePathType) {
IJSVGPrimitivePathTypePath,
IJSVGPrimitivePathTypeRect,
IJSVGPrimitivePathTypePolygon,
IJSVGPrimitivePathTypePolyLine,
IJSVGPrimitivePathTypeCircle,
IJSVGPrimitivePathTypeEllipse,
IJSVGPrimitivePathTypeLine
kIJSVGPrimitivePathTypePath,
kIJSVGPrimitivePathTypeRect,
kIJSVGPrimitivePathTypePolygon,
kIJSVGPrimitivePathTypePolyLine,
kIJSVGPrimitivePathTypeCircle,
kIJSVGPrimitivePathTypeEllipse,
kIJSVGPrimitivePathTypeLine
};
@interface IJSVGPath : IJSVGNode {
NSBezierPath* path;
NSBezierPath* subpath;
CGPoint lastControlPoint;
}
@property (nonatomic, assign) IJSVGPrimitivePathType primitiveType;
@property (nonatomic, readonly) NSBezierPath* path;
@property (nonatomic, readonly) NSBezierPath* subpath;
@property (nonatomic, retain) NSBezierPath* path;
@property (nonatomic, assign) CGPoint lastControlPoint;
@property (nonatomic, readonly) CGPathRef CGPath;
- (NSBezierPath*)currentSubpath;
- (void)close;
- (NSPoint)currentPoint;
- (void)overwritePath:(NSBezierPath*)aPath;
- (CGPathRef)newPathRefByAutoClosingPath:(BOOL)autoClose;
@end
+18 -87
View File
@@ -11,30 +11,26 @@
@implementation IJSVGPath
@synthesize path;
@synthesize subpath;
@synthesize path = _path;
@synthesize lastControlPoint;
@synthesize CGPath = _CGPath;
@synthesize primitiveType = _primitiveType;
- (void)dealloc
{
if(_CGPath != nil) {
if (_CGPath != nil) {
CGPathRelease(_CGPath);
_CGPath = nil;
}
if (subpath != nil) {
(void)([subpath release]), subpath = nil;
}
((void)[_path release]), _path = nil;
[super dealloc];
}
- (id)init
{
if ((self = [super init]) != nil) {
_primitiveType = IJSVGPrimitivePathTypePath;
subpath = NSBezierPath.bezierPath.retain;
path = subpath; // for legacy use
_primitiveType = kIJSVGPrimitivePathTypePath;
_path = NSBezierPath.bezierPath.retain;
}
return self;
}
@@ -42,99 +38,34 @@
- (id)copyWithZone:(NSZone*)zone
{
IJSVGPath* node = [super copyWithZone:zone];
[node overwritePath:self.path];
node.path = [self.path.copy autorelease];
return node;
}
- (NSPoint)currentPoint
{
return [subpath currentPoint];
}
- (NSBezierPath*)currentSubpath
{
return subpath;
return _path.currentPoint;
}
- (void)close
{
[subpath closePath];
[_path closePath];
}
- (void)invlidateCGPath
{
if (_CGPath != nil) {
CGPathRelease(_CGPath);
}
_CGPath = nil;
}
- (CGPathRef)CGPath
{
if(_CGPath == nil) {
_CGPath = [self newPathRefByAutoClosingPath:NO];
if (_CGPath == nil) {
_CGPath = [_path newCGPathRef:NO];
}
return _CGPath;
}
- (void)overwritePath:(NSBezierPath*)aPath
{
(void)([subpath release]), subpath = nil;
subpath = [aPath retain];
path = subpath;
}
- (CGPathRef)newPathRefByAutoClosingPath:(BOOL)autoClose
{
NSInteger i = 0;
NSInteger numElements = self.path.elementCount;
NSBezierPath* bezPath = self.path;
// nothing to return
if (numElements == 0) {
return NULL;
}
CGMutablePathRef aPath = CGPathCreateMutable();
NSPoint points[3];
BOOL didClosePath = YES;
for (i = 0; i < numElements; i++) {
switch ([bezPath elementAtIndex:i associatedPoints:points]) {
// move
case NSMoveToBezierPathElement: {
CGPathMoveToPoint(aPath, NULL, points[0].x, points[0].y);
break;
}
// line
case NSLineToBezierPathElement: {
CGPathAddLineToPoint(aPath, NULL, points[0].x, points[0].y);
didClosePath = NO;
break;
}
// curve
case NSCurveToBezierPathElement: {
CGPathAddCurveToPoint(aPath, NULL, points[0].x, points[0].y,
points[1].x, points[1].y,
points[2].x, points[2].y);
didClosePath = NO;
break;
}
// close
case NSClosePathBezierPathElement: {
CGPathCloseSubpath(aPath);
didClosePath = YES;
break;
}
}
}
if (!didClosePath && autoClose) {
CGPathCloseSubpath(aPath);
}
// create immutable and release
CGPathRef pathToReturn = CGPathCreateCopy(aPath);
CGPathRelease(aPath);
return pathToReturn;
}
@end
@@ -0,0 +1,42 @@
//
// IJSVGCommandParser.h
// IJSVG
//
// Created by Curtis Hard on 23/12/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
#include <xlocale.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, IJSVGPathDataSequence) {
kIJSVGPathDataSequenceTypeFloat,
kIJSVGPathDataSequenceTypeFlag
};
static NSUInteger const IJSVG_STREAM_FLOAT_BLOCK_SIZE = 50;
static NSUInteger const IJSVG_STREAM_CHAR_BLOCK_SIZE = 20;
typedef struct {
CGFloat* floatBuffer;
NSInteger floatCount;
char* charBuffer;
NSInteger charCount;
} IJSVGPathDataStream;
@interface IJSVGCommandParser : NSObject
IJSVGPathDataStream* IJSVGPathDataStreamCreateDefault(void);
IJSVGPathDataStream* IJSVGPathDataStreamCreate(NSUInteger floatCount, NSUInteger charCount);
void IJSVGPathDataStreamRelease(IJSVGPathDataStream* buffer);
IJSVGPathDataSequence* IJSVGPathDataSequenceCreateWithType(IJSVGPathDataSequence type, NSInteger length);
CGFloat* _Nullable IJSVGParsePathDataStreamSequence(const char* commandChars, NSInteger commandCharLength,
IJSVGPathDataStream* dataStream, IJSVGPathDataSequence* _Nullable sequence,
NSInteger commandLength, NSInteger* _Nullable commandsFound);
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,249 @@
//
// IJSVGCommandParser.m
// IJSVG
//
// Created by Curtis Hard on 23/12/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import "IJSVGCommandParser.h"
@implementation IJSVGCommandParser
#define VALID_DIGIT(c) ((c ^ '0') <= 9)
IJSVGPathDataSequence* IJSVGPathDataSequenceCreateWithType(IJSVGPathDataSequence type, NSInteger length)
{
size_t size = sizeof(IJSVGPathDataSequence) * length;
IJSVGPathDataSequence* sequence = (IJSVGPathDataSequence*)malloc(size);
memset(sequence, (int)type, size);
return sequence;
};
// Datastreams work by setting up one stream of bits/memory per SVG
// so that each SVG has a reusable memory block to read and parse paths into.
// As its all linear and one SVG per thread, this saves alot of memory allocation
// calls as we simple can just reuse the buffer that already exists - this also
// allows us to specify the default allocation size, so when parsing viewBox we
// can simply allocate (4*sizeof(CGFloat)) instead of the default 50 slots
IJSVGPathDataStream* IJSVGPathDataStreamCreateDefault(void)
{
return IJSVGPathDataStreamCreate(IJSVG_STREAM_FLOAT_BLOCK_SIZE,
IJSVG_STREAM_CHAR_BLOCK_SIZE);
}
IJSVGPathDataStream* IJSVGPathDataStreamCreate(NSUInteger floatCount, NSUInteger charCount)
{
floatCount = floatCount ?: IJSVG_STREAM_FLOAT_BLOCK_SIZE;
charCount = charCount ?: IJSVG_STREAM_CHAR_BLOCK_SIZE;
IJSVGPathDataStream* buffer = (IJSVGPathDataStream*)malloc(sizeof(IJSVGPathDataStream));
buffer->floatBuffer = (CGFloat*)malloc(sizeof(CGFloat) * floatCount);
buffer->floatCount = floatCount;
buffer->charBuffer = (char*)calloc(sizeof(char), charCount);
buffer->charCount = charCount;
return buffer;
}
void IJSVGPathDataStreamRelease(IJSVGPathDataStream* buffer)
{
free(buffer->charBuffer);
free(buffer->floatBuffer);
free(buffer);
};
CGFloat* _Nullable IJSVGParsePathDataStreamSequence(const char* commandChars, NSInteger commandCharLength,
IJSVGPathDataStream* dataStream, IJSVGPathDataSequence* _Nullable sequence,
NSInteger commandLength, NSInteger* _Nullable commandsFound)
{
// if no command length, its completely pointless function,
// so just return null and set commandsFound to 0, if we dont
// we get a arithmetic error later on due to zero
if (commandLength == 0) {
*commandsFound = 0;
return NULL;
}
// default memory size for the float
NSInteger i = 0;
NSInteger counter = 0;
const char* cString = commandChars;
const char* validChars = "+-.";
// this is much faster then doing strlen as it doesnt need
// to compute the length
NSInteger sLength = commandCharLength;
NSInteger sLengthMinusOne = sLength - 1;
bool isDecimal = false;
int bufferCount = 0;
while (i < sLength) {
char currentChar = *cString++;
// work out next char
char nextChar = (char)0;
if (i < sLengthMinusOne) {
nextChar = *cString++;
cString--;
}
// check for validator
bool isE = (currentChar | ('E' ^ 'e')) == 'e';
bool isValid = VALID_DIGIT(currentChar) || isE || strchr(validChars, currentChar) != NULL;
// in order to work out the split, its either because the next char is
// a hyphen or a plus, or next char is a decimal and the current number is a decimal
bool nIsSign = nextChar == '-' || nextChar == '+';
bool wantsEnd = nIsSign || (nextChar == '.' && isDecimal);
// work our what the sequence is...
IJSVGPathDataSequence seq = kIJSVGPathDataSequenceTypeFloat;
if (sequence != NULL) {
seq = sequence[counter % commandLength];
}
// is a flag, consists of one value
// if its invalid, make sure we free the memory
// and return null - or hell breaks lose
if (isValid == YES && seq == kIJSVGPathDataSequenceTypeFlag) {
if (bufferCount != 0 || (currentChar != '0' && currentChar != '1')) {
return NULL;
}
wantsEnd = YES;
}
// could be a float like 5.334e-5 so dont break on the hypen
if (wantsEnd && isE && nIsSign) {
wantsEnd = false;
}
// make sure its a valid string
if (isValid == YES) {
// alloc the buffer if needed
if ((bufferCount + 1) == dataStream->charCount) {
// realloc the buffer, incase the string is overflowing the
// allocated memory
dataStream->charCount += IJSVG_STREAM_CHAR_BLOCK_SIZE;
dataStream->charBuffer = (char*)realloc(dataStream->charBuffer,
sizeof(char) * dataStream->charCount);
}
// set the actual char against it
if (currentChar == '.') {
isDecimal = true;
}
dataStream->charBuffer[bufferCount++] = currentChar;
} else {
// if its an invalid char, just stop it
wantsEnd = true;
}
// is at end of string, or wants to be stopped
// buffer has to actually exist or its completly
// useless and will cause a crash
if (bufferCount != 0 && (wantsEnd || i == sLengthMinusOne)) {
// make sure there is enough room in the float pool
if ((counter + 1) == dataStream->floatCount) {
dataStream->floatCount += IJSVG_STREAM_FLOAT_BLOCK_SIZE;
dataStream->floatBuffer = (CGFloat*)realloc(dataStream->floatBuffer,
sizeof(CGFloat) * dataStream->floatCount);
}
// add the float - for performance reasons, we can simply set the
// null value of the end of the string instead of nulling out
// with memset \0 - huzzah!
dataStream->charBuffer[bufferCount] = '\0';
dataStream->floatBuffer[counter++] = IJSVGParseFloat(dataStream->charBuffer);
// reset
isDecimal = false;
bufferCount = 0;
}
i++;
}
// set commands found - only if there is one
if (commandsFound != NULL) {
*commandsFound = (NSInteger)round(counter / commandLength);
}
// allocate the new buffer from memory
CGFloat* floats = (CGFloat*)malloc(sizeof(CGFloat) * counter);
memcpy(floats, dataStream->floatBuffer, counter * sizeof(CGFloat));
// return the floats just set into the memory
return floats;
}
// this method is finely tuned to just handle the buffer
// that IJSVGParsePathDataSequence produces for each float
// it does not look or skip white space as the previous method
// handles this for us
// inspired and modified from http://www.leapsecond.com/tools/fast_atof.c
CGFloat IJSVGParseFloat(char* buffer)
{
int fraction;
double sign, value, scale;
// work out a sign, if any, might not be, who knows
sign = 1.f;
if (*buffer == '-') {
sign = -1.f;
buffer += 1;
} else if (*buffer == '+') {
buffer += 1;
}
// get numbers before decimal point or exponent
for (value = 0.f; VALID_DIGIT(*buffer); buffer += 1) {
value = value * 10.f + (*buffer - '0');
}
// get digits after decimal point
if (*buffer == '.') {
double pow10 = 10.f;
buffer += 1;
while (VALID_DIGIT(*buffer)) {
value += (*buffer - '0') / pow10;
pow10 *= 10.f;
buffer += 1;
}
}
// handle exponent
fraction = 0;
scale = 1.f;
if ((*buffer | ('E' ^ 'e')) == 'e') {
unsigned int exponent;
buffer += 1;
if (*buffer == '-') {
fraction = 1;
buffer += 1;
} else if (*buffer == '+') {
buffer += 1;
}
for (exponent = 0; VALID_DIGIT(*buffer); buffer += 1) {
exponent = exponent * 10 + (*buffer - '0');
}
if (exponent > 308) {
exponent = 308;
}
while (exponent >= 50) {
scale *= 1E50;
exponent -= 50;
}
while (exponent >= 8) {
scale *= 1E8;
exponent -= 8;
}
while (exponent > 0) {
scale *= 10.f;
exponent -= 1;
}
}
// make sure we cast this to a CGFloat before return
return (CGFloat)(sign * (fraction ? (value / scale) : (value * scale)));
}
@end
@@ -20,6 +20,7 @@
#import "IJSVGStyleSheet.h"
#import "IJSVGText.h"
#import "IJSVGTransform.h"
#import "IJSVGUnitRect.h"
#import "IJSVGUtils.h"
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
@@ -85,28 +86,28 @@ static NSString const* IJSVGAttributePoints = @"points";
@interface IJSVGParser : IJSVGGroup {
NSRect viewBox;
NSSize proposedViewSize;
IJSVGUnitSize* intrinsicSize;
@private
id<IJSVGParserDelegate> _delegate;
NSXMLDocument* _document;
NSMutableArray* _glyphs;
NSMutableArray<IJSVGNode*>* _glyphs;
IJSVGStyleSheet* _styleSheet;
NSMutableArray* _parsedNodes;
NSMutableDictionary* _defNodes;
NSMutableDictionary* _baseDefNodes;
NSMutableDictionary<NSString*, NSXMLElement*>* _defNodes;
NSMutableDictionary<NSString*, NSXMLElement*>* _baseDefNodes;
NSMutableArray<IJSVG*>* _svgs;
NSMutableArray* _definedGroups;
struct {
unsigned int shouldHandleForeignObject : 1;
unsigned int handleForeignObject : 1;
unsigned int handleSubSVG : 1;
} _respondsTo;
IJSVGPathDataStream* _commandDataStream;
}
@property (nonatomic, readonly) NSRect viewBox;
@property (nonatomic, readonly) NSSize proposedViewSize;
@property (nonatomic, readonly) IJSVGUnitSize* intrinsicSize;
+ (BOOL)isDataSVG:(NSData*)data;
+216 -141
View File
@@ -12,7 +12,36 @@
@implementation IJSVGParser
@synthesize viewBox;
@synthesize proposedViewSize;
@synthesize intrinsicSize = _intrinsicSize;
static NSDictionary* _IJSVGAttributeDictionaryFloats = nil;
static NSDictionary* _IJSVGAttributeDictionaryNodes = nil;
static NSDictionary* _IJSVGAttributeDictionaryUnits = nil;
static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
+ (void)load
{
_IJSVGAttributeDictionaryFloats = [@{
(NSString*)IJSVGAttributeX : @"x",
(NSString*)IJSVGAttributeY : @"y",
(NSString*)IJSVGAttributeWidth : @"width",
(NSString*)IJSVGAttributeHeight : @"height",
(NSString*)IJSVGAttributeOpacity : @"opacity",
(NSString*)IJSVGAttributeStrokeOpacity : @"strokeOpacity",
(NSString*)IJSVGAttributeStrokeWidth : @"strokeWidth",
(NSString*)IJSVGAttributeStrokeDashOffset : @"strokeDashOffset",
(NSString*)IJSVGAttributeFillOpacity : @"fillOpacity" } retain];
_IJSVGAttributeDictionaryNodes = [@{
(NSString*)IJSVGAttributeClipPath : @"clipPath",
(NSString*)IJSVGAttributeMask : @"mask" } retain];
_IJSVGAttributeDictionaryUnits = [@{
(NSString*)IJSVGAttributeGradientUnits : @"units",
(NSString*)IJSVGAttributeMaskUnits : @"units",
(NSString*)IJSVGAttributeMaskContentUnits : @"contentUnits"} retain];
_IJSVGAttributeDictionaryTransforms = [@{
(NSString*)IJSVGAttributeTransform : @"transforms",
(NSString*)IJSVGAttributeGradientTransform : @"transforms" } retain];
}
+ (IJSVGParser*)groupForFileURL:(NSURL*)aURL
{
@@ -42,11 +71,13 @@
{
(void)([_glyphs release]), _glyphs = nil;
(void)([_styleSheet release]), _styleSheet = nil;
(void)([_parsedNodes release]), _parsedNodes = nil;
(void)([_defNodes release]), _defNodes = nil;
(void)([_baseDefNodes release]), _baseDefNodes = nil;
(void)([_definedGroups release]), _definedGroups = nil;
(void)([_svgs release]), _svgs = nil;
(void)([_intrinsicSize release]), _intrinsicSize = nil;
if (_commandDataStream != NULL) {
(void)IJSVGPathDataStreamRelease(_commandDataStream), _commandDataStream = nil;
}
[super dealloc];
}
@@ -61,11 +92,8 @@
_respondsTo.shouldHandleForeignObject = [_delegate respondsToSelector:@selector(svgParser:shouldHandleForeignObject:)];
_respondsTo.handleSubSVG = [_delegate respondsToSelector:@selector(svgParser:foundSubSVG:withSVGString:)];
_glyphs = [[NSMutableArray alloc] init];
_parsedNodes = [[NSMutableArray alloc] init];
_commandDataStream = IJSVGPathDataStreamCreateDefault();
_defNodes = [[NSMutableDictionary alloc] init];
_baseDefNodes = [[NSMutableDictionary alloc] init];
_svgs = [[NSMutableArray alloc] init];
// load the document / file, assume its UTF8
@@ -199,8 +227,12 @@
free(box);
} else {
// there is no view box so find the width and height
CGFloat w = [svgElement attributeForName:(NSString*)IJSVGAttributeWidth].stringValue.floatValue;
CGFloat h = [svgElement attributeForName:(NSString*)IJSVGAttributeHeight].stringValue.floatValue;
NSString* wAtt = [svgElement attributeForName:(NSString*)IJSVGAttributeWidth].stringValue;
NSString* hAtt = [svgElement attributeForName:(NSString*)IJSVGAttributeHeight].stringValue;
IJSVGUnitLength* wLength = [IJSVGUnitLength unitWithString:wAtt];
IJSVGUnitLength* hLength = [IJSVGUnitLength unitWithString:hAtt];
CGFloat w = wLength.value;
CGFloat h = hLength.value;
if (h == 0.f && w != 0.f) {
h = w;
} else if (w == 0.f && h != 0.f) {
@@ -210,17 +242,22 @@
}
// parse the width and height....
CGFloat w = [svgElement attributeForName:(NSString*)IJSVGAttributeWidth].stringValue.floatValue;
CGFloat h = [svgElement attributeForName:(NSString*)IJSVGAttributeHeight].stringValue.floatValue;
if (w == 0.f && h == 0.f) {
w = viewBox.size.width;
h = viewBox.size.height;
} else if (w == 0 && h != 0.f) {
w = viewBox.size.width;
} else if (h == 0 && w != 0.f) {
h = viewBox.size.height;
NSString* w = [svgElement attributeForName:(NSString*)IJSVGAttributeWidth].stringValue;
NSString* h = [svgElement attributeForName:(NSString*)IJSVGAttributeHeight].stringValue;
// by default just the the width and height from the viewbox unless
// specified otherwise
IJSVGUnitLength* wl = [IJSVGUnitLength unitWithFloat:viewBox.size.width];
IJSVGUnitLength* hl = [IJSVGUnitLength unitWithFloat:viewBox.size.height];
if (w != nil) {
wl = [IJSVGUnitLength unitWithString:w];
}
proposedViewSize = NSMakeSize(w, h);
if (h != nil) {
hl = [IJSVGUnitLength unitWithString:h];
}
// store the width and height
_intrinsicSize = [IJSVGUnitSize sizeWithWidth:wl height:hl].retain;
// the root element is SVG, so iterate over its children
// recursively
@@ -229,17 +266,10 @@
intoGroup:self
def:NO];
// now everything has been done we need to compute the style tree
for (NSDictionary* dict in _parsedNodes) {
[self _postParseElementForCommonAttributes:dict[@"element"]
node:dict[@"node"]
ignoreAttributes:nil];
}
// dont need the style sheet or the parsed nodes as this point
(void)([_styleSheet release]), _styleSheet = nil;
(void)([_parsedNodes release]), _parsedNodes = nil;
(void)([_defNodes release]), _defNodes = nil;
(void)IJSVGPathDataStreamRelease(_commandDataStream), _commandDataStream = NULL;
}
- (void)_postParseElementForCommonAttributes:(NSXMLElement*)element
@@ -313,22 +343,13 @@
}
// floats
atts(@{ (NSString*)IJSVGAttributeX : @"x",
(NSString*)IJSVGAttributeY : @"y",
(NSString*)IJSVGAttributeWidth : @"width",
(NSString*)IJSVGAttributeHeight : @"height",
(NSString*)IJSVGAttributeOpacity : @"opacity",
(NSString*)IJSVGAttributeStrokeOpacity : @"strokeOpacity",
(NSString*)IJSVGAttributeStrokeWidth : @"strokeWidth",
(NSString*)IJSVGAttributeStrokeDashOffset : @"strokeDashOffset",
(NSString*)IJSVGAttributeFillOpacity : @"fillOpacity" },
atts(_IJSVGAttributeDictionaryFloats,
^id(NSString* value) {
return [IJSVGUnitLength unitWithString:value];
});
// nodes
atts(@{ (NSString*)IJSVGAttributeClipPath : @"clipPath",
(NSString*)IJSVGAttributeMask : @"mask" },
atts(_IJSVGAttributeDictionaryNodes,
^id(NSString* value) {
NSString* url = [IJSVGUtils defURL:value];
if (url != nil) {
@@ -338,16 +359,13 @@
});
// units
atts(@{ (NSString*)IJSVGAttributeGradientUnits : @"units",
(NSString*)IJSVGAttributeMaskUnits : @"units",
(NSString*)IJSVGAttributeMaskContentUnits : @"contentUnits" },
atts(_IJSVGAttributeDictionaryUnits,
^id(NSString* value) {
return @([IJSVGUtils unitTypeForString:value]);
});
// transforms
atts(@{ (NSString*)IJSVGAttributeTransform : @"transforms",
(NSString*)IJSVGAttributeGradientTransform : @"transforms" },
atts(_IJSVGAttributeDictionaryTransforms,
^(NSString* value) {
NSMutableArray* tempTransforms = [[[NSMutableArray alloc] init] autorelease];
[tempTransforms addObjectsFromArray:[IJSVGTransform transformsForString:value]];
@@ -450,6 +468,24 @@
node.shouldRender = NO;
}
});
// is there a title or desc?
for(NSXMLElement* childElement in element.children) {
IJSVGNodeType type = [IJSVGNode typeForString:childElement.localName
kind:childElement.kind];
switch(type) {
case IJSVGNodeTypeTitle: {
node.title = childElement.stringValue;
break;
}
case IJSVGNodeTypeDesc: {
node.desc = childElement.stringValue;
break;
}
default: {
}
}
}
}
- (id)definedObjectForID:(NSString*)anID
@@ -466,10 +502,6 @@
[self _parseBaseBlock:parseElement
intoGroup:group
def:NO];
if (_definedGroups == nil) {
_definedGroups = [[NSMutableArray alloc] init];
}
[_definedGroups addObject:group];
return [group defForID:anID];
}
return nil;
@@ -483,25 +515,28 @@
- (BOOL)isFont
{
return [_glyphs count] != 0;
return _glyphs != nil && [_glyphs count] != 0;
}
- (NSArray*)glyphs
{
return _glyphs;
return _glyphs ?: @[];
}
- (void)addSubSVG:(IJSVG*)anSVG
{
if (_svgs == nil) {
_svgs = [[NSMutableArray alloc] init];
}
[_svgs addObject:anSVG];
}
- (NSArray<IJSVG*>*)subSVGs:(BOOL)recursive
{
if (recursive == NO) {
return _svgs;
return _svgs ?: @[];
}
NSMutableArray* svgs = [[[NSMutableArray alloc] init] autorelease];
NSMutableArray<IJSVG*>* svgs = [[[NSMutableArray alloc] init] autorelease];
for (IJSVG* anSVG in svgs) {
[svgs addObject:anSVG];
[svgs addObjectsFromArray:[anSVG subSVGs:recursive]];
@@ -511,6 +546,9 @@
- (void)addGlyph:(IJSVGNode*)glyph
{
if (_glyphs == nil) {
_glyphs = [[NSMutableArray alloc] init];
}
[_glyphs addObject:glyph];
}
@@ -581,6 +619,9 @@
default: {
// just a default def, continue on, as we are a def element,
// store these seperately to the default ID string ones
if (_baseDefNodes == nil) {
_baseDefNodes = [[NSMutableDictionary alloc] init];
}
NSString* defID = [childDef attributeForName:@"id"].stringValue;
if (defID != nil) {
_baseDefNodes[defID] = childDef;
@@ -891,7 +932,7 @@
// use
case IJSVGNodeTypeUse: {
NSString* xlink = [[element attributeForName:(NSString*)IJSVGAttributeXLink] stringValue];
NSString* xlink = [[self resolveXLinkAttributeForElement:element] stringValue];
NSString* xlinkID = [xlink substringFromIndex:1];
IJSVGNode* node = [self definedObjectForID:xlinkID];
@@ -938,7 +979,7 @@
// linear gradient
case IJSVGNodeTypeLinearGradient: {
NSString* xlink = [[element attributeForName:(NSString*)IJSVGAttributeXLink] stringValue];
NSString* xlink = [[self resolveXLinkAttributeForElement:element] stringValue];
NSString* xlinkID = [xlink substringFromIndex:1];
NSXMLElement* referenceElement;
IJSVGNode* node = [self definedObjectForID:xlinkID
@@ -975,7 +1016,7 @@
// radial gradient
case IJSVGNodeTypeRadialGradient: {
NSString* xlink = [[element attributeForName:(NSString*)IJSVGAttributeXLink] stringValue];
NSString* xlink = [[self resolveXLinkAttributeForElement:element] stringValue];
NSString* xlinkID = [xlink substringFromIndex:1];
NSXMLElement* referenceElement;
IJSVGNode* node = [self definedObjectForID:xlinkID
@@ -1064,8 +1105,8 @@
ignoreAttributes:nil];
// from base64
NSString* string = [element attributeForName:(NSString*)IJSVGAttributeXLink].stringValue;
[image loadFromBase64EncodedString:string];
NSXMLNode* attributeNode = [self resolveXLinkAttributeForElement:element];
[image loadFromBase64EncodedString:attributeNode.stringValue];
// add to parent
[parentGroup addChild:image];
@@ -1075,6 +1116,17 @@
}
}
- (NSXMLNode*)resolveXLinkAttributeForElement:(NSXMLElement*)element
{
NSString* const namespaceURI = @"http://www.w3.org/1999/xlink";
NSXMLNode* attributeNode = [element attributeForLocalName:@"href"
URI:namespaceURI];
if (attributeNode == nil) {
attributeNode = [element attributeForName:(NSString*)IJSVGAttributeXLink];
}
return attributeNode;
}
- (NSXMLElement*)mergedElement:(NSXMLElement*)element
withReferenceElement:(NSXMLElement*)reference
{
@@ -1104,70 +1156,71 @@
#pragma mark Parser stuff!
- (void)_parsePathCommandData:(NSString*)command
intoPath:(IJSVGPath*)path
- (void)_parsePathCommandDataBuffer:(const char*)buffer
intoPath:(IJSVGPath*)path
{
// invalid command
if (command == nil || command.length == 0) {
return;
}
NSUInteger len = [command length];
// allocate memory for the string buffer for reading
const char* buffer = [command cStringUsingEncoding:NSUTF8StringEncoding];
int defaultBufferSize = 200;
int currentBufferSize = 0;
int currentSize = defaultBufferSize;
unichar* commandBuffer = NULL;
if (len != 0) {
commandBuffer = (unichar*)calloc(defaultBufferSize, sizeof(unichar));
}
NSUInteger len = strlen(buffer);
NSUInteger lastIndex = len - 1;
// make sure we plus 1 for the null byte
char* charBuffer = (char*)malloc(sizeof(char)*(len + 1));
NSInteger start = 0;
IJSVGCommand* _currentCommand = nil;
for (int i = 0; i < len; i++) {
unichar currentChar = buffer[i];
unichar nextChar = buffer[i + 1];
BOOL atEnd = i == len - 1;
for (NSInteger i = 0; i < len; i++) {
char nextChar = buffer[i + 1];
BOOL atEnd = i == lastIndex;
BOOL isStartCommand = IJSVGIsLegalCommandCharacter(nextChar);
if ((currentBufferSize + 1) == currentSize) {
currentSize += defaultBufferSize;
commandBuffer = (unichar*)realloc(commandBuffer, sizeof(unichar) * currentSize);
}
commandBuffer[currentBufferSize++] = currentChar;
if (isStartCommand == YES || atEnd == YES) {
NSString* commandString = [NSString stringWithCharacters:commandBuffer
length:currentBufferSize];
// copy memory from current buffer
NSInteger index = ((i + 1) - start);
memcpy(&charBuffer[0], &buffer[start], sizeof(char)*index);
charBuffer[index] = '\0';
// create the command from the substring
unsigned long length = index + 1;
size_t mlength = sizeof(char)*length;
char* commandString = (char*)malloc(mlength);
memcpy(commandString, &charBuffer[0], mlength);
// reset start position
start = (i + 1);
// previous command is actual subcommand
IJSVGCommand* previousCommand = [_currentCommand subCommands].lastObject;
IJSVGCommand* cCommand = [self _parseCommandString:commandString
previousCommand:previousCommand
intoPath:path];
IJSVGCommand* previousCommand = _currentCommand.subCommands.lastObject;
IJSVGCommand* cCommand = [self _parseCommandStringBuffer:commandString
previousCommand:previousCommand
intoPath:path];
// free the memory as at this point, we are done with it
(void)free(commandString), commandString = NULL;
// retain the current one
if (cCommand != nil) {
_currentCommand = cCommand;
}
if (atEnd == NO) {
currentBufferSize = 0;
memset(commandBuffer, '\0', sizeof(unichar) * currentSize);
}
}
}
// free the buffer
free(commandBuffer);
(void)free(charBuffer), charBuffer = NULL;
}
- (IJSVGCommand*)_parseCommandString:(NSString*)string
previousCommand:(IJSVGCommand*)previousCommand
intoPath:(IJSVGPath*)path
- (void)_parsePathCommandData:(NSString*)command
intoPath:(IJSVGPath*)path
{
// invalid command
if (command == nil || command.length == 0) {
return;
}
// allocate memory for the string buffer for reading
const char* buffer = command.UTF8String;
[self _parsePathCommandDataBuffer:buffer
intoPath:path];
}
- (IJSVGCommand*)_parseCommandStringBuffer:(const char*)buffer
previousCommand:(IJSVGCommand*)previousCommand
intoPath:(IJSVGPath*)path
{
// work out the last command - the reason this is so long is because the command
// could be a series of the same commands, so work it out by the number of parameters
@@ -1179,9 +1232,11 @@
// main commands
// Class commandClass = [IJSVGCommand classFor]
Class commandClass = [IJSVGCommand commandClassForCommandChar:[string characterAtIndex:0]];
IJSVGCommand* command = (IJSVGCommand*)[[[commandClass alloc] initWithCommandString:string] autorelease];
for (IJSVGCommand* subCommand in [command subCommands]) {
Class commandClass = [IJSVGCommand commandClassForCommandChar:buffer[0]];
IJSVGCommand* command = nil;
command = (IJSVGCommand*)[[[commandClass alloc] initWithCommandStringBuffer:buffer
dataStream:_commandDataStream] autorelease];
for (IJSVGCommand* subCommand in command.subCommands) {
[command.class runWithParams:subCommand.parameters
paramCount:subCommand.parameterCount
command:subCommand
@@ -1198,48 +1253,47 @@
{
// convert a line into a command,
// basically MX1 Y1LX2 Y2
path.primitiveType = IJSVGPrimitivePathTypeLine;
path.primitiveType = kIJSVGPrimitivePathTypeLine;
CGFloat x1 = [element attributeForName:(NSString*)IJSVGAttributeX1].stringValue.floatValue;
CGFloat y1 = [element attributeForName:(NSString*)IJSVGAttributeY1].stringValue.floatValue;
CGFloat x2 = [element attributeForName:(NSString*)IJSVGAttributeX2].stringValue.floatValue;
CGFloat y2 = [element attributeForName:(NSString*)IJSVGAttributeY2].stringValue.floatValue;
// use sprintf as its quicker then stringWithFormat...
char buffer[50];
sprintf(buffer, "M%.2f %.2fL%.2f %.2f", x1, y1, x2, y2);
NSString* command = [NSString stringWithCString:buffer
encoding:NSUTF8StringEncoding];
[self _parsePathCommandData:command
intoPath:path];
char* buffer;
asprintf(&buffer, "M%.2f %.2fL%.2f %.2f", x1, y1, x2, y2);
[self _parsePathCommandDataBuffer:buffer
intoPath:path];
(void)free(buffer);
}
- (void)_parseCircle:(NSXMLElement*)element
intoPath:(IJSVGPath*)path
{
path.primitiveType = IJSVGPrimitivePathTypeCircle;
path.primitiveType = kIJSVGPrimitivePathTypeCircle;
CGFloat cX = [element attributeForName:(NSString*)IJSVGAttributeCX].stringValue.floatValue;
CGFloat cY = [element attributeForName:(NSString*)IJSVGAttributeCY].stringValue.floatValue;
CGFloat r = [element attributeForName:(NSString*)IJSVGAttributeR].stringValue.floatValue;
NSRect rect = NSMakeRect(cX - r, cY - r, r * 2, r * 2);
[path overwritePath:[NSBezierPath bezierPathWithOvalInRect:rect]];
path.path = [NSBezierPath bezierPathWithOvalInRect:rect];
}
- (void)_parseEllipse:(NSXMLElement*)element
intoPath:(IJSVGPath*)path
{
path.primitiveType = IJSVGPrimitivePathTypeEllipse;
path.primitiveType = kIJSVGPrimitivePathTypeEllipse;
CGFloat cX = [element attributeForName:(NSString*)IJSVGAttributeCX].stringValue.floatValue;
CGFloat cY = [element attributeForName:(NSString*)IJSVGAttributeCY].stringValue.floatValue;
CGFloat rX = [element attributeForName:(NSString*)IJSVGAttributeRX].stringValue.floatValue;
CGFloat rY = [element attributeForName:(NSString*)IJSVGAttributeRY].stringValue.floatValue;
NSRect rect = NSMakeRect(cX - rX, cY - rY, rX * 2, rY * 2);
[path overwritePath:[NSBezierPath bezierPathWithOvalInRect:rect]];
path.path = [NSBezierPath bezierPathWithOvalInRect:rect];
}
- (void)_parsePolyline:(NSXMLElement*)element
intoPath:(IJSVGPath*)path
{
path.primitiveType = IJSVGPrimitivePathTypePolyLine;
path.primitiveType = kIJSVGPrimitivePathTypePolyLine;
[self _parsePoly:element
intoPath:path
closePath:NO];
@@ -1248,7 +1302,7 @@
- (void)_parsePolygon:(NSXMLElement*)element
intoPath:(IJSVGPath*)path
{
path.primitiveType = IJSVGPrimitivePathTypePolygon;
path.primitiveType = kIJSVGPrimitivePathTypePolygon;
[self _parsePoly:element
intoPath:path
closePath:YES];
@@ -1268,29 +1322,52 @@
free(params);
return;
}
// construct a command
NSInteger capacity = count / 2;
if (closePath == YES) {
capacity += 1;
const int defBufferSize = 5;
char* buffer;
asprintf(&buffer, "M%f %f L", params[0], params[1]);
// compute a default buffer
size_t bSize = strlen(buffer);
size_t strLength = bSize;
// for every pair of coordinates
for(int i = 2; i < count; i+= 2) {
char* subbuf;
asprintf(&subbuf, "%f %f ", params[i], params[i + 1]);
size_t sSize = strlen(subbuf);
// if the new size of the string is large than the buffer
// increase the buffer up another def size
if((strLength + sSize + 1) > bSize) {
size_t nLength = MAX(sSize, defBufferSize) + 2;
buffer = realloc(buffer, sizeof(char)*(bSize+nLength));
bSize += nLength;
}
// append thr string onto the buffer, increment the
// string length and free the subbuffer memory
strcat(buffer, subbuf);
strLength += sSize;
(void)free(subbuf), subbuf = NULL;
}
NSMutableString* str = [[[NSMutableString alloc] initWithCapacity:capacity] autorelease];
[str appendFormat:@"M%f,%f L", params[0], params[1]];
for (NSInteger i = 2; i < count; i += 2) {
[str appendFormat:@"%f,%f ", params[i], params[i + 1]];
if(closePath == YES) {
strcat(buffer, "z");
}
if (closePath) {
[str appendString:@"z"];
}
[self _parsePathCommandData:str
intoPath:path];
free(params);
// actually perform the parse
[self _parsePathCommandDataBuffer:buffer
intoPath:path];
// free the params
(void)free(buffer), buffer = NULL;
(void)free(params), params = NULL;
}
- (void)_parseRect:(NSXMLElement*)element
intoPath:(IJSVGPath*)path
{
path.primitiveType = IJSVGPrimitivePathTypeRect;
path.primitiveType = kIJSVGPrimitivePathTypeRect;
// width and height
CGFloat width = [IJSVGUtils floatValue:[element attributeForName:(NSString*)IJSVGAttributeWidth].stringValue
fallBackForPercent:self.viewBox.size.width];
@@ -1310,11 +1387,9 @@
if ([element attributeForName:(NSString*)IJSVGAttributeRY] == nil) {
rY = rX;
}
NSBezierPath* newPath = [NSBezierPath bezierPathWithRoundedRect:NSMakeRect(x, y, width, height)
xRadius:rX
yRadius:rY];
[path overwritePath:newPath];
path.path = [NSBezierPath bezierPathWithRoundedRect:NSMakeRect(x, y, width, height)
xRadius:rX
yRadius:rY];
}
@end
@@ -623,8 +623,6 @@
if (node.clipPath.units == IJSVGUnitObjectBoundingBox) {
[self adjustLayer:clip
toParentLayerFrame:layer];
} else {
clip.affineTransform = [self absoluteTransform:node];
}
// add the layer
@@ -639,8 +637,6 @@
if (node.mask.units == IJSVGUnitObjectBoundingBox) {
[self adjustLayer:mask
toParentLayerFrame:layer];
} else {
mask.affineTransform = [self absoluteTransform:node];
}
// add the layer
@@ -648,8 +644,9 @@
}
// recursive colourize for each item
NSColor* color = [IJSVGColor computeColorSpace:NSColor.whiteColor];
[self _recursiveColorLayersFromLayer:maskLayer
withColor:[IJSVGColor computeColorSpace:NSColor.whiteColor].CGColor];
withColor:color.CGColor];
// add the mask
layer.mask = maskLayer;
@@ -743,19 +740,4 @@
}
}
+ (void)log:(IJSVGLayer*)layer
depth:(NSInteger)depth
{
NSLog(@"%@%@: %@, Transforms: %@", [@"" stringByPaddingToLength:depth
withString:@"\t"
startingAtIndex:0],
layer,
NSStringFromRect(layer.frame),
[IJSVGTransform affineTransformToSVGTransformAttributeString:layer.affineTransform]);
for (IJSVGLayer* sublayer in layer.sublayers) {
[self log:(IJSVGLayer*)sublayer
depth:depth++];
}
}
@end
@@ -17,5 +17,6 @@ CGFloat IJSVGMathSin(CGFloat val);
CGFloat IJSVGMathAsin(CGFloat val);
CGFloat IJSVGMathTan(CGFloat val);
CGFloat IJSVGMathAtan(CGFloat val);
CGFloat IJSVGMathToFixed(CGFloat val, NSInteger decimalPlaces);
@end
@@ -7,6 +7,7 @@
//
#import "IJSVGMath.h"
#import "IJSVGCommandParser.h"
@implementation IJSVGMath
@@ -20,6 +21,12 @@ CGFloat IJSVGMathDeg(CGFloat val)
return val * 180.f / M_PI;
};
CGFloat IJSVGMathToFixed(CGFloat val, NSInteger decimalPlaces)
{
int p = pow(10, decimalPlaces);
return (CGFloat)floor(p * val) / p;
}
CGFloat IJSVGMathAcos(CGFloat val)
{
return IJSVGMathDeg(acosf(val));
@@ -10,5 +10,5 @@
#import <QuartzCore/QuartzCore.h>
BOOL IJSVGIsMainThread(void);
void IJSVGBeginTransactionLock(void);
void IJSVGEndTransactionLock(void);
BOOL IJSVGBeginTransaction(void);
void IJSVGEndTransaction(void);
@@ -7,22 +7,23 @@
//
#import "IJSVGTransaction.h"
#import <AppKit/AppKit.h>
BOOL IJSVGIsMainThread(void) { return NSThread.isMainThread; };
void IJSVGBeginTransactionLock(void)
BOOL IJSVGBeginTransaction(void)
{
if (IJSVGIsMainThread()) {
return;
if(IJSVGIsMainThread() == YES) {
return NO;
}
// use nsanimationcontext as this sets a private flag of 0x4
// of the catransaction for background composites
[CATransaction begin];
[CATransaction setDisableActions:YES];
return YES;
};
void IJSVGEndTransactionLock(void)
void IJSVGEndTransaction(void)
{
if (IJSVGIsMainThread()) {
return;
}
[CATransaction commit];
};
@@ -6,13 +6,13 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "IJSVGUtils.h"
#import <Foundation/Foundation.h>
@class IJSVGTransform;
typedef CGFloat (^IJSVGTransformParameterModifier)(NSInteger index, CGFloat value);
typedef void (^IJSVGTransformApplyBlock)(IJSVGTransform * transform);
typedef void (^IJSVGTransformApplyBlock)(IJSVGTransform* transform);
typedef NS_OPTIONS(NSInteger, IJSVGTransformCommand) {
IJSVGTransformCommandMatrix,
@@ -27,34 +27,39 @@ typedef NS_OPTIONS(NSInteger, IJSVGTransformCommand) {
};
@interface IJSVGTransform : NSObject {
IJSVGTransformCommand command;
CGFloat * parameters;
CGFloat* parameters;
NSInteger parameterCount;
NSInteger sort;
}
@property ( nonatomic, assign ) IJSVGTransformCommand command;
@property ( nonatomic, assign ) CGFloat * parameters;
@property ( nonatomic, assign ) NSInteger parameterCount;
@property ( nonatomic, assign ) NSInteger sort;
@property (nonatomic, assign) IJSVGTransformCommand command;
@property (nonatomic, assign) CGFloat* parameters;
@property (nonatomic, assign) NSInteger parameterCount;
@property (nonatomic, assign) NSInteger sort;
NSString * IJSVGDebugAffineTransform(CGAffineTransform transform);
NSString * IJSVGDebugTransforms(NSArray<IJSVGTransform *> * transforms);
void IJSVGApplyTransform(NSArray<IJSVGTransform *> * transforms, IJSVGTransformApplyBlock block);
CGAffineTransform IJSVGConcatTransforms(NSArray<IJSVGTransform *> * transforms);
void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApplyBlock block);
CGAffineTransform IJSVGConcatTransforms(NSArray<IJSVGTransform*>* transforms);
NSString* IJSVGTransformAttributeString(CGAffineTransform transform);
+ (NSArray<IJSVGTransform *> *)transformsFromAffineTransform:(CGAffineTransform)affineTransform;
+ (NSArray *)transformsForString:(NSString *)string;
+ (NSBezierPath *)transformedPath:(IJSVGPath *)path;
+ (NSArray<NSString *> *)affineTransformToSVGTransformAttributeString:(CGAffineTransform)affineTransform;
+ (NSString *)affineTransformToSVGMatrixString:(CGAffineTransform)affineTransform;
+ (NSArray<NSDictionary*>*)affineTransformToSVGTransformComponents:(CGAffineTransform)transform;
+ (NSString*)affineTransformToSVGTransformComponentString:(CGAffineTransform)transform
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (NSString*)affineTransformToSVGTransformComponentString:(CGAffineTransform)transform;
+ (NSArray<IJSVGTransform*>*)transformsFromAffineTransform:(CGAffineTransform)affineTransform;
+ (NSArray<IJSVGTransform*>*)transformsForString:(NSString*)string;
+ (NSBezierPath*)transformedPath:(IJSVGPath*)path;
+ (NSString*)affineTransformToSVGMatrixString:(CGAffineTransform)affineTransform;
+ (NSString*)affineTransformToSVGMatrixString:(CGAffineTransform)transform
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (CGAffineTransform)CGAffineTransform;
- (CGAffineTransform)CGAffineTransformWithModifier:(IJSVGTransformParameterModifier)modifier;
- (CGAffineTransform)stackIdentity:(CGAffineTransform)identity;
- (void)recalculateWithBounds:(CGRect)bounds;
+ (IJSVGTransform *)transformByTranslatingX:(CGFloat)x
y:(CGFloat)y;
+ (IJSVGTransform*)transformByTranslatingX:(CGFloat)x
y:(CGFloat)y;
+ (IJSVGTransform*)transformByScaleX:(CGFloat)x
y:(CGFloat)y;
@end
@@ -18,7 +18,7 @@
- (void)dealloc
{
free(parameters);
(void)free(parameters);
[super dealloc];
}
@@ -33,22 +33,6 @@
return trans;
}
NSString* IJSVGDebugAffineTransform(CGAffineTransform transform)
{
NSMutableArray* strings = [[[NSMutableArray alloc] init] autorelease];
[strings addObjectsFromArray:[IJSVGTransform affineTransformToSVGTransformAttributeString:transform]];
return [strings componentsJoinedByString:@" "];
}
NSString* IJSVGDebugTransforms(NSArray<IJSVGTransform*>* transforms)
{
NSMutableArray* strings = [[[NSMutableArray alloc] init] autorelease];
IJSVGApplyTransform(transforms, ^(IJSVGTransform* transform) {
[strings addObjectsFromArray:[IJSVGTransform affineTransformToSVGTransformAttributeString:transform.CGAffineTransform]];
});
return [strings componentsJoinedByString:@" "];
}
CGAffineTransform IJSVGConcatTransforms(NSArray<IJSVGTransform*>* transforms)
{
__block CGAffineTransform trans = CGAffineTransformIdentity;
@@ -58,6 +42,11 @@ CGAffineTransform IJSVGConcatTransforms(NSArray<IJSVGTransform*>* transforms)
return trans;
}
NSString* IJSVGTransformAttributeString(CGAffineTransform transform)
{
return [IJSVGTransform affineTransformToSVGMatrixString:transform];
}
void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApplyBlock block)
{
for (IJSVGTransform* transform in transforms) {
@@ -78,13 +67,27 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
return transform;
}
+ (IJSVGTransform*)transformByScaleX:(CGFloat)x
y:(CGFloat)y
{
IJSVGTransform* transform = [[[self alloc] init] autorelease];
transform.command = IJSVGTransformCommandScale;
transform.parameterCount = 2;
CGFloat* params = (CGFloat*)malloc(sizeof(CGFloat) * 2);
params[0] = x;
params[1] = y;
transform.parameters = params;
return transform;
}
- (void)recalculateWithBounds:(CGRect)bounds
{
CGFloat max = bounds.size.width > bounds.size.height ? bounds.size.width : bounds.size.height;
switch (self.command) {
case IJSVGTransformCommandRotate: {
if (self.parameterCount == 1)
if (self.parameterCount == 1) {
return;
}
self.parameters[1] = self.parameters[1] * max;
self.parameters[2] = self.parameters[2] * max;
}
@@ -134,53 +137,90 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
return 10;
}
+ (NSArray*)transformsForString:(NSString*)string
+ (NSArray<IJSVGTransform*>*)transformsForString:(NSString*)string
{
static NSRegularExpression* _reg = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_reg = [[NSRegularExpression alloc] initWithPattern:@"([a-zA-Z]+)\\(([^\\)]+)\\)"
options:0
error:nil];
});
NSMutableArray* transforms = [[[NSMutableArray alloc] init] autorelease];
@autoreleasepool {
[_reg enumerateMatchesInString:string
options:0
range:NSMakeRange(0, string.length)
usingBlock:^(NSTextCheckingResult* result, NSMatchingFlags flags, BOOL* stop) {
NSString* command = [string substringWithRange:[result rangeAtIndex:1]];
IJSVGTransformCommand commandType = [self.class commandForCommandString:command];
if (commandType == IJSVGTransformCommandNotImplemented) {
return;
}
// create the transform
NSString* params = [string substringWithRange:[result rangeAtIndex:2]];
IJSVGTransform* transform = [[[self.class alloc] init] autorelease];
NSInteger count = 0;
transform.command = commandType;
transform.parameters = [IJSVGUtils commandParameters:params
count:&count];
transform.parameterCount = count;
transform.sort = [self.class sortForTransformCommand:commandType];
[transforms addObject:transform];
}];
NSMutableArray<IJSVGTransform*>* array = nil;
array = [[[NSMutableArray alloc] init] autorelease];
// setup the buffer for the string to be stored in
const char* charString = string.UTF8String;
unsigned long length = strlen(charString);
char* buffer = (char*)calloc(sizeof(char), length);
char* originBuffer = buffer;
int bufferIndex = 0;
// each command requires a name and parameters, store for later use
NSString* commandName = nil;
NSString* params = nil;
for(int i = 0; i < length; i++) {
char currentChar = *charString++;
// start of params - store the command name as its current in the buffer
if(currentChar == '(') {
// rest the pointer to beginning
buffer = originBuffer;
IJSVGTrimCharBuffer(buffer);
commandName = [NSString stringWithUTF8String:buffer];
// write null up until the limit we reached
memset(buffer, '\0', bufferIndex);
bufferIndex = 0;
continue;
}
// end of params - store the params into the buffer
if(currentChar == ')') {
// rest the pointer to beginning
buffer = originBuffer;
params = [NSString stringWithUTF8String:buffer];
// write null up until the limit we reached
memset(buffer, '\0', bufferIndex);
bufferIndex = 0;
// at this point we can just add the command
IJSVGTransformCommand commandType = [self.class commandForCommandString:commandName];
if(commandType == IJSVGTransformCommandNotImplemented) {
continue;
}
// create a new transform object and parse the parameters
NSInteger count = 0;
IJSVGTransform* transform = [[[self.class alloc] init] autorelease];
transform.command = commandType;
transform.sort = [self.class sortForTransformCommand:commandType];
transform.parameters = [IJSVGUtils commandParameters:params
count:&count];
transform.parameterCount = count;
// add to the list of transforms to return
[array addObject:transform];
// reset these values
commandName = nil;
params = nil;
continue;
}
// increment the buffer count
*buffer++ = currentChar;
bufferIndex++;
}
return transforms;
buffer = originBuffer;
(void)free(buffer), buffer = NULL;
return array;
}
+ (NSBezierPath*)transformedPath:(IJSVGPath*)path
{
if (path.transforms.count == 0)
if (path.transforms.count == 0) {
return path.path;
}
NSBezierPath* cop = [[path.path copy] autorelease];
for (IJSVGTransform* transform in path.transforms) {
NSAffineTransform* at = NSAffineTransform.transform;
switch (transform.command) {
// matrix
case IJSVGTransformCommandMatrix: {
at.transformStruct = (NSAffineTransformStruct){
at.transformStruct = (NSAffineTransformStruct) {
.m11 = transform.parameters[0],
.m12 = transform.parameters[1],
.m21 = transform.parameters[2],
@@ -195,7 +235,7 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
case IJSVGTransformCommandSkewX: {
CGFloat degrees = transform.parameters[0];
CGFloat radians = degrees * M_PI / 180.f;
at.transformStruct = (NSAffineTransformStruct){
at.transformStruct = (NSAffineTransformStruct) {
.m11 = 1.f,
.m12 = 0.f,
.m21 = tan(radians),
@@ -210,7 +250,7 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
case IJSVGTransformCommandSkewY: {
CGFloat degrees = transform.parameters[0];
CGFloat radians = degrees * M_PI / 180.f;
at.transformStruct = (NSAffineTransformStruct){
at.transformStruct = (NSAffineTransformStruct) {
.m11 = 1.f,
.m12 = tan(radians),
.m21 = 0.f,
@@ -325,10 +365,10 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// scale
case IJSVGTransformCommandScale: {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
if (self.parameterCount == 1) {
return CGAffineTransformScale(identity, p0, p0);
}
CGFloat p1 = self.parameters[1];
return CGAffineTransformScale(identity, p0, p1);
}
@@ -389,13 +429,14 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// translate
case IJSVGTransformCommandTranslate: {
CGFloat p0 = self.parameters[0];
if (self.parameterCount == 1) {
return CGAffineTransformMakeTranslation(p0, 0);
}
CGFloat p1 = self.parameters[1];
if (modifier != nil) {
p0 = modifier(0, p0);
p1 = modifier(1, p1);
}
if (self.parameterCount == 1)
return CGAffineTransformMakeTranslation(p0, 0);
return CGAffineTransformMakeTranslation(p0, p1);
}
@@ -420,13 +461,14 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// scale
case IJSVGTransformCommandScale: {
CGFloat p0 = self.parameters[0];
if (self.parameterCount == 1) {
return CGAffineTransformMakeScale(p0, p0);
}
CGFloat p1 = self.parameters[1];
if (modifier != nil) {
p0 = modifier(0, p0);
p1 = modifier(1, p1);
}
if (self.parameterCount == 1)
return CGAffineTransformMakeScale(p0, p0);
return CGAffineTransformMakeScale(p0, p1);
}
@@ -452,9 +494,9 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// rotate
case IJSVGTransformCommandRotate: {
if (self.parameterCount == 1)
if (self.parameterCount == 1) {
return CGAffineTransformMakeRotation((self.parameters[0] / 180) * M_PI);
else {
} else {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
CGFloat p2 = self.parameters[2];
@@ -483,121 +525,209 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
+ (NSArray<IJSVGTransform*>*)transformsFromAffineTransform:(CGAffineTransform)affineTransform
{
NSArray* strings = [self affineTransformToSVGTransformAttributeString:affineTransform];
return [self transformsForString:[strings componentsJoinedByString:@" "]];
NSString* matrix = [self affineTransformToSVGMatrixString:affineTransform];
return [self transformsForString:matrix];
}
+ (NSString*)affineTransformToSVGTransformComponentString:(CGAffineTransform)transform
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
NSArray<NSDictionary*>* trans = [self affineTransformToSVGTransformComponents:transform];
trans = [self filterUselessAffineTransformComponents:trans];
NSMutableArray<NSString*>* strings = [[[NSMutableArray alloc] initWithCapacity:trans.count] autorelease];
for (NSDictionary* dict in trans) {
NSArray<NSNumber*>* data = dict[@"data"];
NSString* method = dict[@"name"];
NSMutableArray* dataStrings = [[[NSMutableArray alloc] initWithCapacity:data.count] autorelease];
for (NSNumber* number in data) {
[dataStrings addObject:IJSVGShortFloatStringWithOptions(number.floatValue,
floatingPointOptions)];
}
[strings addObject:[NSString stringWithFormat:@"%@(%@)", method,
IJSVGCompressFloatParameterArray(dataStrings)]];
}
NSString* componentsString = [strings componentsJoinedByString:@" "];
NSString* matrixString = [self affineTransformToSVGMatrixString:transform
floatingPointOptions:floatingPointOptions];
return componentsString.length < matrixString.length ? componentsString : matrixString;
}
+ (NSString*)affineTransformToSVGTransformComponentString:(CGAffineTransform)transform
{
NSArray<NSDictionary*>* trans = [self affineTransformToSVGTransformComponents:transform];
trans = [self filterUselessAffineTransformComponents:trans];
NSMutableArray<NSString*>* strings = [[[NSMutableArray alloc] initWithCapacity:trans.count] autorelease];
for (NSDictionary* dict in trans) {
NSArray<NSNumber*>* data = dict[@"data"];
NSString* method = dict[@"name"];
NSMutableArray* dataStrings = [[[NSMutableArray alloc] initWithCapacity:data.count] autorelease];
for (NSNumber* number in data) {
[dataStrings addObject:IJSVGShortFloatString(number.floatValue)];
}
[strings addObject:[NSString stringWithFormat:@"%@(%@)", method,
IJSVGCompressFloatParameterArray(dataStrings)]];
}
NSString* componentsString = [strings componentsJoinedByString:@" "];
NSString* matrixString = [self affineTransformToSVGMatrixString:transform];
return componentsString.length < matrixString.length ? componentsString : matrixString;
}
+ (NSString*)affineTransformToSVGMatrixString:(CGAffineTransform)transform
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
NSArray<NSString*>* numbers = @[
IJSVGShortFloatStringWithOptions(transform.a, floatingPointOptions),
IJSVGShortFloatStringWithOptions(transform.b, floatingPointOptions),
IJSVGShortFloatStringWithOptions(transform.c, floatingPointOptions),
IJSVGShortFloatStringWithOptions(transform.d, floatingPointOptions),
IJSVGShortFloatStringWithOptions(transform.tx, floatingPointOptions),
IJSVGShortFloatStringWithOptions(transform.ty, floatingPointOptions)
];
return [NSString stringWithFormat:@"matrix(%@)", IJSVGCompressFloatParameterArray(numbers)];
}
+ (NSString*)affineTransformToSVGMatrixString:(CGAffineTransform)transform
{
return [NSString stringWithFormat:@"matrix(%g,%g,%g,%g,%g,%g)",
transform.a, transform.b, transform.c, transform.d,
transform.tx, transform.ty];
NSArray<NSString*>* numbers = @[
IJSVGShortFloatString(transform.a),
IJSVGShortFloatString(transform.b),
IJSVGShortFloatString(transform.c),
IJSVGShortFloatString(transform.d),
IJSVGShortFloatString(transform.tx),
IJSVGShortFloatString(transform.ty)
];
return [NSString stringWithFormat:@"matrix(%@)",
IJSVGCompressFloatParameterArray(numbers)];
}
// this is an Object-C version of the matrixToTransform method from SVGO
+ (NSArray<NSString*>*)affineTransformToSVGTransformAttributeString:(CGAffineTransform)affineTransform
+ (NSArray<NSDictionary*>*)filterUselessAffineTransformComponents:(NSArray<NSDictionary*>*)components
{
const CGFloat data[6] = {
affineTransform.a,
affineTransform.b,
affineTransform.c,
affineTransform.d,
affineTransform.tx,
affineTransform.ty
NSMutableArray* comps = [[[NSMutableArray alloc] initWithCapacity:components.count] autorelease];
NSArray<NSString*>* names = @[ @"translate", @"rotate", @"skewX", @"skewY" ];
for (NSDictionary* transform in components) {
NSString* name = transform[@"name"];
NSArray<NSNumber*>* data = transform[@"data"];
if ([names containsObject:name] && (data.count == 1 || [name isEqualToString:@"rotate"]) && data[0].floatValue == 0.f) {
continue;
} else if ([name isEqualToString:@"translate"] && data[0].floatValue == 0.f && data[1].floatValue == 0.f) {
continue;
} else if ([name isEqualToString:@"scale"] && data[0].floatValue == 1.f && (data.count < 2 || (data.count == 2 && data[1].floatValue == 1.f))) {
continue;
} else if ([name isEqualToString:@"matrix"] && data[0].floatValue == 1.f && data[3].floatValue == 1.f && !(data[1].floatValue != 0.f || data[2].floatValue != 0.f || data[4].floatValue != 0.f || data[5].floatValue != 0.f)) {
continue;
}
[comps addObject:transform];
}
return comps;
}
+ (NSArray<NSDictionary*>*)affineTransformToSVGTransformComponents:(CGAffineTransform)transform
{
const NSUInteger precision = 5;
CGFloat data[6] = {
IJSVGMathToFixed(transform.a, precision),
IJSVGMathToFixed(transform.b, precision),
IJSVGMathToFixed(transform.c, precision),
IJSVGMathToFixed(transform.d, precision),
IJSVGMathToFixed(transform.tx, precision),
IJSVGMathToFixed(transform.ty, precision)
};
CGFloat sx = sqrtf(data[0] * data[0] + data[1] * data[1]);
CGFloat sy = (data[0] * data[3] - data[1] * data[2]) / sx;
CGFloat sx = IJSVGMathToFixed(hypotf(data[0], data[1]), precision);
CGFloat sy = IJSVGMathToFixed(((data[0] * data[3] - data[1] * data[2]) / sx), precision);
CGFloat colSum = data[0] * data[2] + data[1] * data[3];
CGFloat rowSum = data[0] * data[1] + data[2] * data[3];
BOOL scaleBefore = rowSum != 0.f || (sx == sy);
BOOL scaleBefore = rowSum != 0.f || sx == sy;
NSMutableArray* trans = [[[NSMutableArray alloc] init] autorelease];
NSMutableArray* transforms = [[[NSMutableArray alloc] init] autorelease];
// translate
// tx, ty -> translate
if (data[4] != 0.f || data[5] != 0.f) {
NSString* str = [NSString stringWithFormat:@"translate(%g, %g)", data[4], data[5]];
[trans addObject:str];
[transforms addObject:@{
@"name" : @"translate",
@"data" : @[ @(data[4]), @(data[5]) ]
}];
}
// skewX
// [sx, 0, tan(a).sy, sy, 0, 0] -> skewX(a).scale(sx,sy)
if (data[1] == 0.f && data[2] != 0.f) {
NSString* str = [NSString stringWithFormat:@"skewX(%g)", IJSVGMathAtan(data[2] / sy)];
[trans addObject:str];
[transforms addObject:@{
@"name" : @"skewX",
@"data" : @[ @(IJSVGMathToFixed(IJSVGMathAtan(data[2] / sy), precision)) ]
}];
// skewY
// [sx, sy.tan(a), 0, sy, 0, 0] -> skewX(a).scale(sx, sy)
} else if (data[1] != 0.f && data[2] == 0.f) {
NSString* str = [NSString stringWithFormat:@"skewY(%g)", IJSVGMathAtan(data[1] / data[0])];
[trans addObject:str];
[transforms addObject:@{
@"name" : @"skewY",
@"data" : @[ @(IJSVGMathToFixed(IJSVGMathAtan(data[1] / data[0]), precision)) ]
}];
sx = data[0];
sy = data[3];
} else if (colSum == 0.f || (sx == 1.f && sy == 1.f) || scaleBefore == NO) {
if (scaleBefore == NO) {
sx = (data[0] < 0.f ? -1.f : 1.f) * sqrtf(data[0] * data[0] + data[2] * data[2]);
sy = (data[3] < 0.f ? -1.f : 1.f) * sqrtf(data[1] * data[1] + data[3] * data[3]);
NSString* str = nil;
if (sx == sy) {
str = [NSString stringWithFormat:@"scale(%g)", sx];
} else {
str = [NSString stringWithFormat:@"scale(%g, %g)", sx, sy];
} else if (colSum == 0.f || (sx == 1.f && sy == 1.f) || !scaleBefore) {
if (!scaleBefore) {
sx = (data[0] < 0.f ? -1.f : 1.f) * hypotf(data[0], data[2]);
sy = (data[3] < 0.f ? -1.f : 1.f) * hypotf(data[1], data[3]);
if (sx != 1.f || sy != 1.f) {
[transforms addObject:@{
@"name" : @"scale",
@"data" : (sx == sy) ? @[ @(sx) ] : @[ @(sx), @(sy) ]
}];
}
[trans addObject:str];
}
// rotate
CGFloat rotate = IJSVGMathAcos(data[0] / sx) * (data[1] * sy < 0.f ? -1.f : 1.f);
NSString* rotateString = nil;
if (rotate != 0.f) {
rotateString = [NSString stringWithFormat:@"rotate(%g)", rotate];
CGFloat angle = MIN(MAX(-1.f, data[0] / sx), 1.f);
NSMutableArray<NSNumber*>* rotate = [[[NSMutableArray alloc] initWithCapacity:3] autorelease];
[rotate addObject:@(IJSVGMathToFixed(IJSVGMathAcos(angle), precision) * ((scaleBefore ? 1.f : sy) * data[1] < 0.f ? -1.f : 1.f))];
if (rotate[0].floatValue != 0.f) {
[transforms addObject:@{
@"name" : @"rotate",
@"data" : rotate
}];
}
// skewX
if (rowSum != 0.f && colSum != 0.f) {
NSString* str = [NSString stringWithFormat:@"skewX(%g)", IJSVGMathAtan(colSum / (sx * sx))];
[trans addObject:str];
[transforms addObject:@{
@"name" : @"skewX",
@"data" : @[ @(IJSVGMathToFixed(IJSVGMathAtan(colSum / (sx * sx)), precision)) ]
}];
}
// rotate around center
if (rotate != 0.f && (data[4] != 0.f || data[5] != 0.f)) {
[trans removeObjectAtIndex:0];
// rotate can consume translate
if (rotate[0].floatValue != 0.f && (data[4] != 0.f || data[5] != 0.f)) {
[transforms removeObjectAtIndex:0];
CGFloat cos = data[0] / sx;
CGFloat sin = data[1] / (scaleBefore ? sx : sy);
CGFloat x = data[4] * (scaleBefore ? 1.f : sy);
CGFloat y = data[5] * (scaleBefore ? 1.f : sx);
CGFloat denom = (powf(1.f - cos, 2.f) + powf(sin, 2.f)) * (scaleBefore ? 1.f : sx * sy);
CGFloat r1 = rotate;
CGFloat r2 = ((1.f - cos) * x - sin * y) / denom;
CGFloat r3 = ((1.f - cos) * y + sin * x) / denom;
rotateString = [NSString stringWithFormat:@"rotate(%g, %g, %g)", r1, r2, r3];
}
if (rotateString != nil) {
[trans addObject:rotateString];
CGFloat denom = (powf(1.f - cos, 2.f) + powf(sin, 2.f)) * (scaleBefore ? 1.f : (sx * sy));
[rotate addObject:@(((1.f - cos) * x - sin * y) / denom)];
[rotate addObject:@(((1.f - cos) * y + sin * x) / denom)];
}
} else if (data[1] != 0.f || data[2] != 0.f) {
NSDictionary* trans = @{
@"name" : @"matrix",
@"data" : @[ @(data[0]), @(data[1]), @(data[2]), @(data[3]), @(data[4]), @(data[5]) ]
};
return @[ trans ];
}
// scale
if ((scaleBefore && (sx != 1.f || sy != 1.f)) || trans.count == 0.f) {
NSString* str = nil;
if (sx == sy) {
str = [NSString stringWithFormat:@"scale(%g)", sx];
} else {
str = [NSString stringWithFormat:@"scale(%g, %g)", sx, sy];
}
[trans addObject:str];
if (scaleBefore == YES && ((sx != 1.f || sy != 1.f) || transforms.count == 0)) {
NSDictionary* trans = @{
@"name" : @"scale",
@"data" : (sx == sy) ? @[ @(sx) ] : @[ @(sx), @(sy) ]
};
[transforms addObject:trans];
}
return trans;
return transforms;
}
- (NSString*)description
{
return [NSString stringWithFormat:@"%@ %@", [super description],
[self.class affineTransformToSVGTransformAttributeString:self.CGAffineTransform]];
[self.class affineTransformToSVGMatrixString:self.CGAffineTransform]];
}
@end
@@ -8,9 +8,20 @@
#import <Foundation/Foundation.h>
typedef struct {
BOOL round;
int precision;
} IJSVGFloatingPointOptions;
typedef NS_ENUM(NSInteger, IJSVGUnitLengthType) {
IJSVGUnitLengthTypeNumber,
IJSVGUnitLengthTypePercentage
IJSVGUnitLengthTypePercentage,
IJSVGUnitLengthTypeCM,
IJSVGUnitLengthTypeMM,
IJSVGUnitLengthTypeIN,
IJSVGUnitLengthTypePT,
IJSVGUnitLengthTypePC,
IJSVGUnitLengthTypePX
};
typedef NS_ENUM(NSInteger, IJSVGUnitType) {
@@ -22,6 +33,7 @@ typedef NS_ENUM(NSInteger, IJSVGUnitType) {
@interface IJSVGUnitLength : NSObject
@property (nonatomic, assign) IJSVGUnitLengthType type;
@property (nonatomic, assign) IJSVGUnitLengthType originalType;
@property (nonatomic, assign) CGFloat value;
@property (nonatomic, assign) BOOL inherit;
@@ -38,5 +50,6 @@ typedef NS_ENUM(NSInteger, IJSVGUnitType) {
- (CGFloat)valueAsPercentage;
- (CGFloat)computeValue:(CGFloat)anotherValue;
- (NSString*)stringValue;
- (NSString*)stringValueWithFloatingPointOptions:(IJSVGFloatingPointOptions)options;
@end
@@ -8,12 +8,14 @@
#import "IJSVGNode.h"
#import "IJSVGUnitLength.h"
#import "IJSVGUtils.h"
@implementation IJSVGUnitLength
@synthesize value;
@synthesize type;
@synthesize inherit;
@synthesize originalType;
+ (IJSVGUnitLength*)unitWithFloat:(CGFloat)number
{
@@ -54,6 +56,64 @@
return unit;
}
+ (IJSVGUnitLengthType)typeForString:(NSString*)string
{
if([string hasSuffix:@"%"] == YES) {
return IJSVGUnitLengthTypePercentage;
}
if([string hasSuffix:@"cm"] == YES) {
return IJSVGUnitLengthTypeCM;
}
if([string hasSuffix:@"mm"] == YES) {
return IJSVGUnitLengthTypeMM;
}
if([string hasSuffix:@"in"] == YES) {
return IJSVGUnitLengthTypeIN;
}
if([string hasSuffix:@"pt"] == YES) {
return IJSVGUnitLengthTypePT;
}
if([string hasSuffix:@"pc"] == YES) {
return IJSVGUnitLengthTypePC;
}
if([string hasSuffix:@"px"] == YES) {
return IJSVGUnitLengthTypePX;
}
return IJSVGUnitLengthTypeNumber;
}
+ (CGFloat)convertUnitValue:(CGFloat)unit
toBaseFromUnitLengthType:(IJSVGUnitLengthType)type
{
switch(type) {
case IJSVGUnitLengthTypeCM: {
return unit * (96.f / 2.54f);
}
case IJSVGUnitLengthTypeMM: {
return [self convertUnitValue:unit
toBaseFromUnitLengthType:IJSVGUnitLengthTypeCM] / 10.f;
}
case IJSVGUnitLengthTypePercentage: {
return unit / 100.f;
}
case IJSVGUnitLengthTypeIN: {
// 1in = 96px
return unit * 96.f;
}
case IJSVGUnitLengthTypePT: {
// 1pt = 1.333...px
return unit * 1.3333333f;
}
case IJSVGUnitLengthTypePC: {
// 1pc = 16px
return unit * 16.f;
}
default:
break;
}
return unit;
}
+ (IJSVGUnitLength*)unitWithString:(NSString*)string
{
// just return noting for inherit, node will deal
@@ -68,9 +128,20 @@
IJSVGUnitLength* unit = [[[self alloc] init] autorelease];
unit.value = string.floatValue;
unit.type = IJSVGUnitLengthTypeNumber;
if ([string hasSuffix:@"%"] == YES) {
unit.value /= 100.f;
unit.type = IJSVGUnitLengthTypePercentage;
IJSVGUnitLengthType type = [self typeForString:string];
unit.originalType = type;
switch(type) {
case IJSVGUnitLengthTypePercentage: {
unit.value = [self convertUnitValue:unit.value
toBaseFromUnitLengthType:type];
unit.type = IJSVGUnitLengthTypePercentage;
break;
}
default:
unit.value = [self convertUnitValue:unit.value
toBaseFromUnitLengthType:type];
break;
}
return unit;
}
@@ -91,14 +162,25 @@
- (NSString*)stringValue
{
if (self.type == IJSVGUnitLengthTypePercentage) {
return [NSString stringWithFormat:@"%g%%", (self.value * 100.f)];
return [NSString stringWithFormat:@"%@%%",
IJSVGShortFloatString(self.value * 100.f)];
}
return [NSString stringWithFormat:@"%g", self.value];
return IJSVGShortFloatString(self.value);
}
- (NSString*)stringValueWithFloatingPointOptions:(IJSVGFloatingPointOptions)options
{
if (self.type == IJSVGUnitLengthTypePercentage) {
return [NSString stringWithFormat:@"%@%%",
IJSVGShortFloatStringWithOptions(self.value * 100.f, options)];
}
return IJSVGShortFloatStringWithOptions(self.value, options);
}
- (NSString*)description
{
return [NSString stringWithFormat:@"%f%@", self.value, (self.type == IJSVGUnitLengthTypePercentage ? @"%" : @"")];
return [NSString stringWithFormat:@"%f%@",
self.value, (self.type == IJSVGUnitLengthTypePercentage ? @"%" : @"")];
}
@end
@@ -0,0 +1,17 @@
//
// IJSVGUnitPoint.h
// IJSVG
//
// Created by Curtis Hard on 12/02/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import "IJSVGUnitLength.h"
#import <Foundation/Foundation.h>
@interface IJSVGUnitPoint : NSObject
@property (nonatomic, retain) IJSVGUnitLength* x;
@property (nonatomic, retain) IJSVGUnitLength* y;
@end
@@ -0,0 +1,32 @@
//
// IJSVGUnitPoint.m
// IJSVG
//
// Created by Curtis Hard on 12/02/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import "IJSVGUnitPoint.h"
@implementation IJSVGUnitPoint
@synthesize x = _x;
@synthesize y = _y;
- (void)dealloc
{
(void)[_x release], _x = nil;
(void)[_y release], _y = nil;
[super dealloc];
}
+ (IJSVGUnitPoint*)pointWithX:(IJSVGUnitLength*)x
y:(IJSVGUnitLength*)y
{
IJSVGUnitPoint* point = [[[self alloc] init] autorelease];
point.x = x;
point.y = y;
return point;
}
@end
@@ -0,0 +1,21 @@
//
// IJSVGUnitRect.h
// IJSVG
//
// Created by Curtis Hard on 12/02/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import "IJSVGUnitPoint.h"
#import "IJSVGUnitSize.h"
#import <Foundation/Foundation.h>
@interface IJSVGUnitRect : NSObject
@property (nonatomic, retain) IJSVGUnitSize* size;
@property (nonatomic, retain) IJSVGUnitPoint* origin;
+ (IJSVGUnitRect*)rectWithOrigin:(IJSVGUnitPoint*)origin
size:(IJSVGUnitSize*)size;
@end
@@ -0,0 +1,32 @@
//
// IJSVGUnitRect.m
// IJSVG
//
// Created by Curtis Hard on 12/02/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import "IJSVGUnitRect.h"
@implementation IJSVGUnitRect
@synthesize size = _size;
@synthesize origin = _origin;
- (void)dealloc
{
(void)[_size release], _size = nil;
(void)[_origin release], _origin = nil;
[super dealloc];
}
+ (IJSVGUnitRect*)rectWithOrigin:(IJSVGUnitPoint*)origin
size:(IJSVGUnitSize*)size
{
IJSVGUnitRect* rect = [[[self alloc] init] autorelease];
rect.origin = origin;
rect.size = size;
return rect;
}
@end
@@ -0,0 +1,20 @@
//
// IJSVGUnitSize.h
// IJSVG
//
// Created by Curtis Hard on 12/02/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import "IJSVGUnitLength.h"
#import <Foundation/Foundation.h>
@interface IJSVGUnitSize : NSObject
@property (nonatomic, retain) IJSVGUnitLength* width;
@property (nonatomic, retain) IJSVGUnitLength* height;
+ (IJSVGUnitSize*)sizeWithWidth:(IJSVGUnitLength*)width
height:(IJSVGUnitLength*)height;
@end
@@ -0,0 +1,32 @@
//
// IJSVGUnitSize.m
// IJSVG
//
// Created by Curtis Hard on 12/02/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import "IJSVGUnitSize.h"
@implementation IJSVGUnitSize
@synthesize width = _width;
@synthesize height = _height;
- (void)dealloc
{
(void)[_width release], _width = nil;
(void)[_height release], _height = nil;
[super dealloc];
}
+ (IJSVGUnitSize*)sizeWithWidth:(IJSVGUnitLength*)width
height:(IJSVGUnitLength*)height
{
IJSVGUnitSize* size = [[[self alloc] init] autorelease];
size.width = width;
size.height = height;
return size;
}
@end
@@ -10,7 +10,8 @@
#import "IJSVGGradientUnitLength.h"
#import "IJSVGStringAdditions.h"
#import <Foundation/Foundation.h>
#include <xlocale.h>
NS_ASSUME_NONNULL_BEGIN
@interface IJSVGUtils : NSObject
@@ -20,20 +21,24 @@ CGFloat angle(CGPoint a, CGPoint b);
CGFloat radians_to_degrees(CGFloat radians);
CGFloat degrees_to_radians(CGFloat degrees);
void IJSVGTrimCharBuffer(char* buffer);
BOOL IJSVGIsCommonHTMLElementName(NSString* str);
NSArray* IJSVGCommonHTMLElementNames(void);
IJSVGFloatingPointOptions IJSVGFloatingPointOptionsDefault(void);
IJSVGFloatingPointOptions IJSVGFloatingPointOptionsMake(BOOL round, int precision);
NSString* IJSVGCompressFloatParameterArray(NSArray<NSString*>* stringToCompress);
NSString* IJSVGShortFloatStringWithOptions(CGFloat f, IJSVGFloatingPointOptions options);
NSString* IJSVGShortenFloatString(NSString* string);
NSString* IJSVGPointToCommandString(CGPoint point);
NSString* IJSVGShortFloatString(CGFloat f);
NSString* IJSVGShortFloatStringWithPrecision(CGFloat f, NSInteger precision);
IJSVGPathDataSequence* IJSVGPathDataSequenceCreateWithType(IJSVGPathDataSequence type, NSInteger length);
CGFloat* IJSVGParsePathDataSequence(NSString* string, IJSVGPathDataSequence* sequence,
NSInteger commandLength, NSInteger* commandsFound);
BOOL IJSVGIsLegalCommandCharacter(unichar aChar);
BOOL IJSVGIsSVGLayer(CALayer* layer);
+ (IJSVGCommandType)typeForCommandString:(NSString*)string;
+ (IJSVGCommandType)typeForCommandChar:(char)commandChar;
+ (CGFloat*)commandParameters:(NSString*)command
count:(NSInteger*)count;
+ (CGFloat*)parseViewBox:(NSString*)string;
@@ -42,7 +47,7 @@ BOOL IJSVGIsSVGLayer(CALayer* layer);
+ (IJSVGLineCapStyle)lineCapStyleForString:(NSString*)string;
+ (IJSVGUnitType)unitTypeForString:(NSString*)string;
+ (IJSVGBlendMode)blendModeForString:(NSString*)string;
+ (NSString*)mixBlendingModeForBlendMode:(IJSVGBlendMode)blendMode;
+ (NSString* _Nullable)mixBlendingModeForBlendMode:(IJSVGBlendMode)blendMode;
+ (NSRange)rangeOfParentheses:(NSString*)string;
+ (void)logParameters:(CGFloat*)param
@@ -50,7 +55,7 @@ BOOL IJSVGIsSVGLayer(CALayer* layer);
+ (CGFloat)floatValue:(NSString*)string;
+ (CGFloat)angleBetweenPointA:(NSPoint)point
pointb:(NSPoint)point;
+ (NSString*)defURL:(NSString*)string;
+ (NSString* _Nullable)defURL:(NSString*)string;
+ (CGFloat)floatValue:(NSString*)string
fallBackForPercent:(CGFloat)viewBox;
+ (CGFloat*)scanFloatsFromString:(NSString*)string
@@ -60,5 +65,5 @@ BOOL IJSVGIsSVGLayer(CALayer* layer);
weight:(CGFloat*)weight;
+ (CGPathRef)newFlippedCGPath:(CGPathRef)path;
+ (CGPathRef)newCGPathFromBezierPath:(NSBezierPath*)bezPath;
@end
NS_ASSUME_NONNULL_END
+104 -201
View File
@@ -9,9 +9,24 @@
#import "IJSVGLayer.h"
#import "IJSVGShapeLayer.h"
#import "IJSVGUtils.h"
#import "IJSVGExporterPathInstruction.h"
@implementation IJSVGUtils
void IJSVGTrimCharBuffer(char* buffer)
{
char* ptr = buffer;
unsigned long length = strlen(ptr);
while(length-1 > 0 && isspace(ptr[length-1])) {
ptr[--length] = '\0';
}
while(*ptr && isspace(*ptr)) {
++ptr;
--length;
}
memmove(buffer, ptr, length+1);
}
BOOL IJSVGIsCommonHTMLElementName(NSString* str)
{
str = str.lowercaseString;
@@ -160,166 +175,98 @@ NSArray* IJSVGCommonHTMLElementNames(void)
return names;
};
NSString* IJSVGShortenFloatString(NSString* string)
{
const char* chars = string.UTF8String;
if (chars[0] == '-' && chars[1] == '0' && strstr(chars, ".") != NULL) {
return [NSString stringWithFormat:@"-%@", [string substringFromIndex:2]];
} else if (chars[0] == '0' && chars[1] == '.') {
return [string substringFromIndex:1];
}
return string;
}
IJSVGFloatingPointOptions IJSVGFloatingPointOptionsDefault(void)
{
return IJSVGFloatingPointOptionsMake(NO, kIJSVGExporterPathInstructionFloatPrecision);
}
IJSVGFloatingPointOptions IJSVGFloatingPointOptionsMake(BOOL round, int precision)
{
return (IJSVGFloatingPointOptions) {
.round = round,
.precision = precision
};
}
NSString* IJSVGShortFloatStringWithOptions(CGFloat f, IJSVGFloatingPointOptions options)
{
if (options.round == YES) {
f = IJSVGExporterPathFloatToFixed(f, options.precision);
}
return IJSVGShortFloatString(f);
};
NSString* IJSVGShortFloatString(CGFloat f)
{
return [NSString stringWithFormat:@"%g", f];
return IJSVGShortenFloatString([NSString stringWithFormat:@"%g", f]);
};
NSString* IJSVGCompressFloatParameterArray(NSArray<NSString*>* strings)
{
char* lastCommandChars = NULL;
NSInteger index = 0;
NSMutableString* string = [[[NSMutableString alloc] init] autorelease];
for (NSString* dataString in strings) {
const char* chars = dataString.UTF8String;
// work out if the command is signed and or decimal
BOOL isSigned = chars[0] == '-';
BOOL isDecimal = (isSigned == NO && chars[0] == '.') || (isSigned == YES && chars[1] == '.');
// we also need to know if the previous command was a decimal or not
BOOL lastWasDecimal = NO;
if (lastCommandChars != NULL) {
lastWasDecimal = strchr(lastCommandChars, '.') != NULL;
}
// we only need a space if the current command is not signed
// a decimal and the previous command was decimal too
if (index++ == 0 || isSigned || (isDecimal == YES && lastWasDecimal == YES)) {
[string appendString:dataString];
} else {
[string appendFormat:@" %@", dataString];
}
// store last command chars
lastCommandChars = (char*)chars;
}
return string;
};
NSString* IJSVGShortFloatStringWithPrecision(CGFloat f, NSInteger precision)
{
NSString* format = [NSString stringWithFormat:@"%@.%ld%@", @"%", precision, @"f"];
NSString* ret = [NSString stringWithFormat:format, f];
// can it be reduced even more?
if (ret.floatValue == (float)ret.integerValue) {
ret = [NSString stringWithFormat:@"%ld", ret.integerValue];
}
return ret;
return IJSVGShortenFloatString(ret);
};
IJSVGPathDataSequence* IJSVGPathDataSequenceCreateWithType(IJSVGPathDataSequence type, NSInteger length)
{
size_t size = sizeof(IJSVGPathDataSequence) * length;
IJSVGPathDataSequence* sequence = (IJSVGPathDataSequence*)malloc(size);
memset(sequence, type, size);
return sequence;
};
CGFloat* _Nullable IJSVGParsePathDataSequence(NSString* string, IJSVGPathDataSequence* _Nullable sequence,
NSInteger commandLength, NSInteger* commandsFound)
{
// if no command length, its completely pointless function,
// so just return null and set commandsFound to 0, if we dont
// we get a arithmetic error later on due to zero
if (commandLength == 0) {
*commandsFound = 0;
return NULL;
}
// default sizes and memory
// sizes for the string buffer
const NSInteger defFloatSize = 20;
const NSInteger defSize = 10;
// default memory size for the float
NSInteger size = defSize;
NSInteger floatSize = defFloatSize;
NSInteger i = 0;
NSInteger counter = 0;
const char* cString = string.UTF8String;
const char* validChars = "eE+-.";
// this is much faster then doing strlen as it doesnt need
// to compute the length
NSInteger sLength = string.length;
// buffer for the returned floats
CGFloat* floats = (CGFloat*)malloc(sizeof(CGFloat) * defFloatSize);
char* buffer = NULL;
bool isDecimal = false;
int bufferCount = 0;
while (i < sLength) {
char currentChar = cString[i];
// work out next char
char nextChar = (char)0;
if (i < (sLength - 1)) {
nextChar = cString[i + 1];
}
// check for validator
bool isValid = (currentChar >= '0' && currentChar <= '9') || strchr(validChars, currentChar) != NULL;
// in order to work out the split, its either because the next char is
// a hyphen or a plus, or next char is a decimal and the current number is a decimal
bool isE = currentChar == 'e' || currentChar == 'E';
bool wantsEnd = nextChar == '-' || nextChar == '+' || (nextChar == '.' && isDecimal);
// work our what the sequence is...
IJSVGPathDataSequence seq = kIJSVGPathDataSequenceTypeFloat;
if (sequence != NULL) {
seq = sequence[counter % commandLength];
}
// is a flag, consists of one value
// if its invalid, make sure we free the memory
// and return null - or hell breaks lose
if (isValid == YES && seq == kIJSVGPathDataSequenceTypeFlag) {
if (bufferCount != 0 || (currentChar != '0' && currentChar != '1')) {
if (buffer) {
(void)free(buffer), buffer = nil;
}
(void)free(floats), floats = nil;
return NULL;
}
wantsEnd = YES;
}
// could be a float like 5.334e-5 so dont break on the hypen
if (wantsEnd && isE && (nextChar == '-' || nextChar == '+')) {
wantsEnd = false;
}
// make sure its a valid string
if (isValid) {
// alloc the buffer if needed
if (buffer == NULL) {
buffer = (char*)calloc(sizeof(char), size);
} else if ((bufferCount + 1) == size) {
// realloc the buffer, incase the string is overflowing the
// allocated memory
size += defSize;
buffer = (char*)realloc(buffer, sizeof(char) * size);
}
// set the actual char against it
if (currentChar == '.') {
isDecimal = true;
}
buffer[bufferCount++] = currentChar;
} else {
// if its an invalid char, just stop it
wantsEnd = true;
}
// is at end of string, or wants to be stopped
// buffer has to actually exist or its completly
// useless and will cause a crash
if ((buffer != NULL && bufferCount != 0) && (wantsEnd || i == sLength - 1)) {
// make sure there is enough room in the float pool
if ((counter + 1) == floatSize) {
floatSize += defFloatSize;
floats = (CGFloat*)realloc(floats, sizeof(CGFloat) * floatSize);
}
// add the float
floats[counter++] = strtod_l(buffer, NULL, NULL);
// memory clean and counter resets
memset(buffer, '\0', sizeof(*buffer) * size);
isDecimal = false;
bufferCount = 0;
}
i++;
}
if (buffer != NULL) {
free(buffer);
}
*commandsFound = (NSInteger)round(counter / commandLength);
return floats;
}
NSString* IJSVGPointToCommandString(CGPoint point)
{
return [NSString stringWithFormat:@"%@,%@", IJSVGShortFloatString(point.x), IJSVGShortFloatString(point.y)];
return [NSString stringWithFormat:@"%@ %@",
IJSVGShortFloatString(point.x),
IJSVGShortFloatString(point.y)];
};
BOOL IJSVGIsLegalCommandCharacter(unichar aChar)
{
const char* validChars = "MmZzLlHhVvCcSsQqTtAa";
return strchr(validChars, aChar) != NULL;
if ((aChar | ('M' ^ 'm')) == 'm' || (aChar | ('Z' ^ 'z')) == 'z' || (aChar | ('C' ^ 'c')) == 'c' || (aChar | ('L' ^ 'l')) == 'l' || (aChar | ('S' ^ 's')) == 's' || (aChar | ('Q' ^ 'q')) == 'q' || (aChar | ('H' ^ 'h')) == 'h' || (aChar | ('V' ^ 'v')) == 'v' || (aChar | ('T' ^ 't')) == 't' || (aChar | ('A' ^ 'a')) == 'a') {
return YES;
}
return NO;
}
BOOL IJSVGIsSVGLayer(CALayer* layer)
@@ -354,9 +301,9 @@ CGFloat degrees_to_radians(CGFloat degrees)
return ((degrees) / 180.0 * M_PI);
}
+ (IJSVGCommandType)typeForCommandString:(NSString*)string
+ (IJSVGCommandType)typeForCommandChar:(char)commandChar
{
return isupper([string characterAtIndex:0]) ? kIJSVGCommandTypeAbsolute : kIJSVGCommandTypeRelative;
return isupper(commandChar) ? kIJSVGCommandTypeAbsolute : kIJSVGCommandTypeRelative;
}
+ (NSRange)rangeOfParentheses:(NSString*)string
@@ -365,7 +312,7 @@ CGFloat degrees_to_radians(CGFloat degrees)
const char* characters = string.UTF8String;
unsigned long length = strlen(characters);
for (NSInteger i = 0; i < length; i++) {
char c = characters[i];
char c = *characters++;
if (c == '(') {
range.location = i + 1;
} else if (c == ')') {
@@ -375,7 +322,7 @@ CGFloat degrees_to_radians(CGFloat degrees)
return range;
}
+ (NSString*)defURL:(NSString*)string
+ (NSString* _Nullable)defURL:(NSString*)string
{
// insta check for URL
NSCharacterSet* set = NSCharacterSet.whitespaceCharacterSet;
@@ -505,7 +452,7 @@ CGFloat degrees_to_radians(CGFloat degrees)
return IJSVGBlendModeNormal;
}
+ (NSString*)mixBlendingModeForBlendMode:(IJSVGBlendMode)blendMode
+ (NSString* _Nullable)mixBlendingModeForBlendMode:(IJSVGBlendMode)blendMode
{
switch (blendMode) {
case IJSVGBlendModeMultiply: {
@@ -570,14 +517,21 @@ CGFloat degrees_to_radians(CGFloat degrees)
+ (CGFloat*)scanFloatsFromString:(NSString*)string
size:(NSInteger*)length
{
return IJSVGParsePathDataSequence(string, NULL, 1, length);
IJSVGPathDataStream* stream = IJSVGPathDataStreamCreateDefault();
CGFloat* floats = IJSVGParsePathDataStreamSequence(string.UTF8String, string.length,
stream, NULL, 1, length);
IJSVGPathDataStreamRelease(stream);
return floats;
}
+ (CGFloat*)parseViewBox:(NSString*)string
{
NSInteger size = 0;
return [self.class scanFloatsFromString:string
size:&size];
IJSVGPathDataStream* stream = IJSVGPathDataStreamCreate(4,
IJSVG_STREAM_CHAR_BLOCK_SIZE);
CGFloat* floats = IJSVGParsePathDataStreamSequence(string.UTF8String,
string.length, stream, NULL, 1, NULL);
IJSVGPathDataStreamRelease(stream);
return floats;
}
+ (CGFloat)floatValue:(NSString*)string
@@ -621,55 +575,4 @@ CGFloat degrees_to_radians(CGFloat degrees)
return transformPath;
}
+ (CGPathRef)newCGPathFromBezierPath:(NSBezierPath*)bezPath
{
CGPathRef immutablePath = NULL;
// Then draw the path elements.
NSInteger numElements = bezPath.elementCount;
if (numElements > 0) {
CGMutablePathRef path = CGPathCreateMutable();
NSPoint points[3];
BOOL didClosePath = YES;
for (NSInteger i = 0; i < numElements; i++) {
switch ([bezPath elementAtIndex:i associatedPoints:points]) {
case NSMoveToBezierPathElement: {
CGPathMoveToPoint(path, NULL, points[0].x, points[0].y);
break;
}
case NSLineToBezierPathElement: {
CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y);
didClosePath = NO;
break;
}
case NSCurveToBezierPathElement: {
CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y,
points[1].x, points[1].y,
points[2].x, points[2].y);
didClosePath = NO;
break;
}
case NSClosePathBezierPathElement: {
CGPathCloseSubpath(path);
didClosePath = YES;
break;
}
}
}
// Be sure the path is closed or Quartz may not do valid hit detection.
if (didClosePath == NO) {
CGPathCloseSubpath(path);
}
// memory clean
immutablePath = CGPathCreateCopy(path);
CGPathRelease(path);
}
return immutablePath;
}
@end
-24
View File
@@ -1,24 +0,0 @@
//
// IJSVGCache.h
// IconJar
//
// Created by Curtis Hard on 02/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVG.h"
#import <Foundation/Foundation.h>
#import <sys/stat.h>
@interface IJSVGCache : NSObject {
}
+ (IJSVG*)cachedSVGForFileURL:(NSURL*)aURL;
+ (void)cacheSVG:(IJSVG*)svg fileURL:(NSURL*)aURL;
+ (void)flushCache;
+ (BOOL)enabled;
+ (void)setEnabled:(BOOL)flag;
+ (void)purgeCachedSVGForFileURL:(NSURL*)aURL;
+ (void)setEvictItemsAfter:(NSInteger)count;
@end
-77
View File
@@ -1,77 +0,0 @@
//
// IJSVGCache.m
// IconJar
//
// Created by Curtis Hard on 02/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGCache.h"
#import <malloc/malloc.h>
@implementation IJSVGCache
static NSInteger _maxCacheItems = 20;
static NSCache* _cache = nil;
static BOOL _enabled = YES;
+ (void)load
{
[self setEnabled:_enabled];
}
+ (void)setEvictItemsAfter:(NSInteger)count
{
_maxCacheItems = count;
[_cache setTotalCostLimit:_maxCacheItems];
}
+ (IJSVG*)cachedSVGForFileURL:(NSURL*)aURL
{
if (![self.class enabled] || _cache == nil)
return nil;
IJSVG* svg = nil;
if ((svg = [_cache objectForKey:aURL]) == nil)
return nil;
return svg;
}
+ (void)purgeCachedSVGForFileURL:(NSURL*)aURL
{
[_cache removeObjectForKey:aURL];
}
+ (void)cacheSVG:(IJSVG*)svg
fileURL:(NSURL*)aURL
{
[_cache setObject:svg
forKey:aURL
cost:1];
}
+ (void)setEnabled:(BOOL)flag
{
_enabled = flag;
if (!flag) {
[self.class flushCache];
return;
}
// create a new cache if allowed
if (_cache == nil) {
_cache = [[NSCache alloc] init];
[_cache setTotalCostLimit:_maxCacheItems];
}
}
+ (BOOL)enabled
{
return _enabled;
}
+ (void)flushCache
{
[_cache removeAllObjects];
}
@end