Compare commits

...

90 Commits

Author SHA1 Message Date
Curtis Hard 717198457b Adds new method to create a trimmed string
- adds `const` in various places
- uses the new trimmed string method
- adds various memory releases in
2022-01-24 14:33:54 +00:00
Curtis Hard e2991ab8b0 Fixes smooth quad / curves having incorrect path data on export 2022-01-04 14:04:23 +00:00
Curtis Hard dda3e5ed8c Merge branch 'feature/ijsvgcolorsub' 2022-01-04 11:17:38 +00:00
Curtis Hard c3a0fb5b91 Fixes 10.9 issue with const 2022-01-04 11:17:15 +00:00
Curtis Hard 7ce4520a56 Adds currentColor as a predefined constant 2021-12-29 16:25:52 +00:00
Curtis Hard 368d19ea81 Nullable types 2021-12-28 22:33:12 +00:00
Curtis Hard d26c2f489f Adds delegate methods for color and ID 2021-12-28 19:28:32 +00:00
Curtis Hard 75b0d55b63 #oops, wrong list being used 2021-12-26 18:50:51 +00:00
Curtis Hard 310308cd8a Fixes stroke colorList stop colors not being identified correctly 2021-12-26 18:11:30 +00:00
Curtis Hard 278f405a41 Possible fix for older OS's 2021-12-15 15:16:59 +00:00
Curtis Hard 7addd97d0c Fixes issue with clipPath not using correct units
- adds support for overflow attribute
2021-11-24 15:15:54 +00:00
Curtis Hard d01ca0f3bb Fixes issue with wrong colors being used 2021-10-19 20:49:57 +01:00
Curtis Hard 6d7ad17e2a Fixes threading race condition 2021-05-11 22:06:56 +01:00
Curtis Hard deb09eaf04 Merge branch 'feature/color-stroke-checking' 2021-04-27 09:45:29 +01:00
Curtis Hard 1e7b8b38a5 Fixes the double quotes being used instead of angled brackets 2021-04-26 13:45:32 +01:00
Curtis Hard 58e1a8c45d fixes tempalte void things 2021-04-26 10:35:08 +01:00
Curtis Hard a525e4351a Fixes computeColorList for gradient export 2021-04-24 13:41:05 +01:00
Curtis Hard c87716b311 Code style 2021-04-21 20:18:07 +01:00
Curtis Hard 583a930bf7 Fixes multithread issue with strtok 2021-04-21 15:24:51 +01:00
Curtis Hard ec2473d450 Adds transaction to fix memory issue 2021-04-21 13:39:31 +01:00
Curtis Hard 3cc477955a Fixes enums and options 2021-04-20 20:36:37 +01:00
Curtis Hard 1f0ff52dff Adds color types 2021-04-20 16:31:52 +01:00
Curtis Hard e96bfa54b0 Make sure to check it has alpha 2021-04-20 12:15:23 +01:00
Curtis Hard cd3b540274 Initial commit 2021-04-20 09:31:10 +01:00
Curtis Hard 1cfbaf14e4 added utils for converting CGLineCap and Joi nto IJSVG counterparts 2021-04-11 17:13:45 +01:00
Curtis Hard 5a389f88d3 Added initWithDataAssetNamed:bundle:error:
Added support for loading SVG’s from the data asset catalogue
2021-04-07 21:42:00 +01:00
Curtis Hard 9f43832ff3 Fixes elliptical arc 2021-02-17 21:32:13 +00:00
Curtis Hard 65623ec552 Fixes colour forced from a style 2021-02-16 21:11:26 +00:00
Curtis Hard 5192520b7c Fixes memory crash 2021-02-16 09:37:22 +00:00
Curtis Hard 5123ee7308 Fixes crash 2021-02-11 21:39:31 +00:00
Curtis Hard c886a6a80d trolled by CGPath not being identical to bezierpath 2021-02-10 22:21:13 +00:00
Curtis Hard 4cd2895703 Seems to improve performance 2021-02-10 19:27:08 +00:00
Curtis Hard a98d84f209 Removed bezier path additions as its now useless
- more perfromance increases
2021-02-08 20:56:08 +00:00
Curtis Hard 8faab28823 Performance increases 2021-02-08 17:23:43 +00:00
Curtis Hard cca5524a11 Converts all bezier path storage over to CGPath
There was no need to use NSBezierPath, we only used to to convert to CGPath eventually, so swapped it all overm much faster
2021-02-06 18:33:23 +00:00
Curtis Hard c14e2367a0 Formatting 2021-02-06 13:21:46 +00:00
Curtis Hard db4b0e9a18 Fixes crash if string is nil 2021-02-06 12:25:16 +00:00
Curtis Hard 93d6a868a9 reformat and use of strtok - did not realise it was already built into C 2021-02-05 21:28:48 +00:00
Curtis Hard 60fa627d99 Perf 2021-02-05 15:51:15 +00:00
Curtis Hard ccaa350190 Fixes issue with mem crash when no width or height is specified as a fallback 2021-02-03 22:03:38 +00:00
Curtis Hard 727f8a2dea Modern Object-C refactor
This refactors most of the old style synthesized properties and lets the compiler do it for us.

This also changes how unit lengths works (now does correct parsing and using cstrings instead of nsstring where applicable)
2021-01-30 18:40:29 +00:00
Curtis Hard 972812b3f6 Fixes arc commands 2021-01-26 08:56:04 +00:00
Curtis Hard ad8f440c72 Merge branch 'feature/cm-mm-units' 2021-01-25 19:32:47 +00:00
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
104 changed files with 3971 additions and 2083 deletions
+55 -19
View File
@@ -7,10 +7,20 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* 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 */; };
591A3E4D25CC91F800AD45B7 /* IJSVGParsing.h in Headers */ = {isa = PBXBuildFile; fileRef = 591A3E4B25CC91F800AD45B7 /* IJSVGParsing.h */; settings = {ATTRIBUTES = (Public, ); }; };
591A3E4E25CC91F800AD45B7 /* IJSVGParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = 591A3E4C25CC91F800AD45B7 /* IJSVGParsing.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 */; }; 594CF55F238FF462009B251B /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF55E238FF462009B251B /* AppKit.framework */; };
594CF561238FF46C009B251B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF560238FF46C009B251B /* Foundation.framework */; }; 594CF561238FF46C009B251B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF560238FF46C009B251B /* Foundation.framework */; };
594CF563238FF473009B251B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF562238FF473009B251B /* QuartzCore.framework */; }; 594CF563238FF473009B251B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF562238FF473009B251B /* QuartzCore.framework */; };
599EB4D3238FF570004CB6BC /* libobjc.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 599EB4D2238FF535004CB6BC /* libobjc.tbd */; }; 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, ); }; }; 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 */; }; 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, ); }; }; 59EB75D623905F7300F5AE63 /* IJSVGLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB756523905F6B00F5AE63 /* IJSVGLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -22,7 +32,6 @@
59EB75DC23905F7300F5AE63 /* IJSVGText.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB756B23905F6B00F5AE63 /* IJSVGText.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59EB75DC23905F7300F5AE63 /* IJSVGText.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB756B23905F6B00F5AE63 /* IJSVGText.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB75DD23905F7300F5AE63 /* IJSVGCommandQuadraticCurve.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB756C23905F6B00F5AE63 /* IJSVGCommandQuadraticCurve.m */; }; 59EB75DD23905F7300F5AE63 /* IJSVGCommandQuadraticCurve.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB756C23905F6B00F5AE63 /* IJSVGCommandQuadraticCurve.m */; };
59EB75DE23905F7300F5AE63 /* IJSVGCommandVerticalLine.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB756D23905F6C00F5AE63 /* IJSVGCommandVerticalLine.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59EB75DE23905F7300F5AE63 /* IJSVGCommandVerticalLine.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB756D23905F6C00F5AE63 /* IJSVGCommandVerticalLine.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB75DF23905F7300F5AE63 /* IJSVGWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB756E23905F6C00F5AE63 /* IJSVGWriter.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB75E023905F7300F5AE63 /* IJSVGStyleSheet.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB756F23905F6C00F5AE63 /* IJSVGStyleSheet.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59EB75E023905F7300F5AE63 /* IJSVGStyleSheet.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB756F23905F6C00F5AE63 /* IJSVGStyleSheet.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB75E123905F7300F5AE63 /* IJSVGCommandSmoothCurve.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB757023905F6C00F5AE63 /* IJSVGCommandSmoothCurve.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59EB75E123905F7300F5AE63 /* IJSVGCommandSmoothCurve.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB757023905F6C00F5AE63 /* IJSVGCommandSmoothCurve.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB75E223905F7300F5AE63 /* IJSVGColorList.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB757123905F6C00F5AE63 /* IJSVGColorList.m */; }; 59EB75E223905F7300F5AE63 /* IJSVGColorList.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB757123905F6C00F5AE63 /* IJSVGColorList.m */; };
@@ -49,12 +58,10 @@
59EB75F723905F7300F5AE63 /* IJSVGDef.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB758623905F6C00F5AE63 /* IJSVGDef.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59EB75F723905F7300F5AE63 /* IJSVGDef.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB758623905F6C00F5AE63 /* IJSVGDef.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB75F823905F7300F5AE63 /* IJSVGCommandHorizontalLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB758723905F6C00F5AE63 /* IJSVGCommandHorizontalLine.m */; }; 59EB75F823905F7300F5AE63 /* IJSVGCommandHorizontalLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB758723905F6C00F5AE63 /* IJSVGCommandHorizontalLine.m */; };
59EB75F923905F7300F5AE63 /* IJSVGFontConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB758823905F6D00F5AE63 /* IJSVGFontConverter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59EB75F923905F7300F5AE63 /* IJSVGFontConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB758823905F6D00F5AE63 /* IJSVGFontConverter.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB75FA23905F7300F5AE63 /* IJSVGWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB758923905F6D00F5AE63 /* IJSVGWriter.m */; };
59EB75FB23905F7300F5AE63 /* IJSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB758A23905F6D00F5AE63 /* IJSVGPath.m */; }; 59EB75FB23905F7300F5AE63 /* IJSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB758A23905F6D00F5AE63 /* IJSVGPath.m */; };
59EB75FC23905F7300F5AE63 /* IJSVGGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB758B23905F6D00F5AE63 /* IJSVGGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59EB75FC23905F7300F5AE63 /* IJSVGGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB758B23905F6D00F5AE63 /* IJSVGGroup.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB75FD23905F7300F5AE63 /* IJSVGPatternLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB758C23905F6D00F5AE63 /* IJSVGPatternLayer.m */; }; 59EB75FD23905F7300F5AE63 /* IJSVGPatternLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB758C23905F6D00F5AE63 /* IJSVGPatternLayer.m */; };
59EB75FE23905F7300F5AE63 /* IJSVGRenderingStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB758D23905F6D00F5AE63 /* IJSVGRenderingStyle.m */; }; 59EB75FE23905F7300F5AE63 /* IJSVGRenderingStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB758D23905F6D00F5AE63 /* IJSVGRenderingStyle.m */; };
59EB75FF23905F7300F5AE63 /* IJSVGBezierPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB758E23905F6D00F5AE63 /* IJSVGBezierPathAdditions.m */; };
59EB760023905F7300F5AE63 /* IJSVGGradientLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB758F23905F6D00F5AE63 /* IJSVGGradientLayer.m */; }; 59EB760023905F7300F5AE63 /* IJSVGGradientLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB758F23905F6D00F5AE63 /* IJSVGGradientLayer.m */; };
59EB760123905F7300F5AE63 /* IJSVGLayerTree.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB759023905F6D00F5AE63 /* IJSVGLayerTree.m */; }; 59EB760123905F7300F5AE63 /* IJSVGLayerTree.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB759023905F6D00F5AE63 /* IJSVGLayerTree.m */; };
59EB760223905F7300F5AE63 /* IJSVGCommandVerticalLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB759123905F6D00F5AE63 /* IJSVGCommandVerticalLine.m */; }; 59EB760223905F7300F5AE63 /* IJSVGCommandVerticalLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB759123905F6D00F5AE63 /* IJSVGCommandVerticalLine.m */; };
@@ -105,7 +112,6 @@
59EB763123905F7300F5AE63 /* IJSVGText.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C023905F7100F5AE63 /* IJSVGText.m */; }; 59EB763123905F7300F5AE63 /* IJSVGText.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C023905F7100F5AE63 /* IJSVGText.m */; };
59EB763223905F7300F5AE63 /* IJSVGView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C123905F7100F5AE63 /* IJSVGView.m */; }; 59EB763223905F7300F5AE63 /* IJSVGView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C123905F7100F5AE63 /* IJSVGView.m */; };
59EB763323905F7300F5AE63 /* IJSVGLinearGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C223905F7100F5AE63 /* IJSVGLinearGradient.m */; }; 59EB763323905F7300F5AE63 /* IJSVGLinearGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C223905F7100F5AE63 /* IJSVGLinearGradient.m */; };
59EB763423905F7300F5AE63 /* IJSVGBezierPathAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB75C323905F7100F5AE63 /* IJSVGBezierPathAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB763523905F7300F5AE63 /* IJSVGStyleSheetSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C423905F7100F5AE63 /* IJSVGStyleSheetSelector.m */; }; 59EB763523905F7300F5AE63 /* IJSVGStyleSheetSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C423905F7100F5AE63 /* IJSVGStyleSheetSelector.m */; };
59EB763623905F7300F5AE63 /* IJSVGImageLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C523905F7100F5AE63 /* IJSVGImageLayer.m */; }; 59EB763623905F7300F5AE63 /* IJSVGImageLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C523905F7100F5AE63 /* IJSVGImageLayer.m */; };
59EB763723905F7300F5AE63 /* IJSVGCommandClose.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C623905F7100F5AE63 /* IJSVGCommandClose.m */; }; 59EB763723905F7300F5AE63 /* IJSVGCommandClose.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C623905F7100F5AE63 /* IJSVGCommandClose.m */; };
@@ -124,15 +130,27 @@
59EB764423905F7300F5AE63 /* IJSVGRendering.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB75D323905F7300F5AE63 /* IJSVGRendering.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59EB764423905F7300F5AE63 /* IJSVGRendering.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB75D323905F7300F5AE63 /* IJSVGRendering.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB764523905F7300F5AE63 /* IJSVGExporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB75D423905F7300F5AE63 /* IJSVGExporter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59EB764523905F7300F5AE63 /* IJSVGExporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB75D423905F7300F5AE63 /* IJSVGExporter.h */; settings = {ATTRIBUTES = (Public, ); }; };
59EB764623905F7300F5AE63 /* IJSVGRendering.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75D523905F7300F5AE63 /* IJSVGRendering.m */; }; 59EB764623905F7300F5AE63 /* IJSVGRendering.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75D523905F7300F5AE63 /* IJSVGRendering.m */; };
59F36508262F1ABB00BCE3FD /* IJSVGColorType.h in Headers */ = {isa = PBXBuildFile; fileRef = 59F36506262F1ABB00BCE3FD /* IJSVGColorType.h */; settings = {ATTRIBUTES = (Public, ); }; };
59F36509262F1ABB00BCE3FD /* IJSVGColorType.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F36507262F1ABB00BCE3FD /* IJSVGColorType.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference 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>"; };
591A3E4B25CC91F800AD45B7 /* IJSVGParsing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGParsing.h; sourceTree = "<group>"; };
591A3E4C25CC91F800AD45B7 /* IJSVGParsing.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGParsing.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; }; 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>"; }; 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; }; 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; }; 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; }; 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; }; 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; }; 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; }; 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>"; }; 59EB756523905F6B00F5AE63 /* IJSVGLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGLayer.h; sourceTree = "<group>"; };
@@ -144,7 +162,6 @@
59EB756B23905F6B00F5AE63 /* IJSVGText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGText.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>"; }; 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>"; }; 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; path = IJSVGWriter.h; sourceTree = "<group>"; };
59EB756F23905F6C00F5AE63 /* IJSVGStyleSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGStyleSheet.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>"; }; 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>"; }; 59EB757123905F6C00F5AE63 /* IJSVGColorList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGColorList.m; sourceTree = "<group>"; };
@@ -171,12 +188,10 @@
59EB758623905F6C00F5AE63 /* IJSVGDef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGDef.h; 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>"; }; 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; path = IJSVGFontConverter.h; 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>"; }; 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>"; }; 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>"; }; 59EB758C23905F6D00F5AE63 /* IJSVGPatternLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGPatternLayer.m; sourceTree = "<group>"; };
59EB758D23905F6D00F5AE63 /* IJSVGRenderingStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGRenderingStyle.m; sourceTree = "<group>"; }; 59EB758D23905F6D00F5AE63 /* IJSVGRenderingStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGRenderingStyle.m; sourceTree = "<group>"; };
59EB758E23905F6D00F5AE63 /* IJSVGBezierPathAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGBezierPathAdditions.m; sourceTree = "<group>"; };
59EB758F23905F6D00F5AE63 /* IJSVGGradientLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGGradientLayer.m; sourceTree = "<group>"; }; 59EB758F23905F6D00F5AE63 /* IJSVGGradientLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGGradientLayer.m; sourceTree = "<group>"; };
59EB759023905F6D00F5AE63 /* IJSVGLayerTree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGLayerTree.m; sourceTree = "<group>"; }; 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>"; }; 59EB759123905F6D00F5AE63 /* IJSVGCommandVerticalLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGCommandVerticalLine.m; sourceTree = "<group>"; };
@@ -227,7 +242,6 @@
59EB75C023905F7100F5AE63 /* IJSVGText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGText.m; 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; path = 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>"; }; 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>"; }; 59EB75C423905F7100F5AE63 /* IJSVGStyleSheetSelector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGStyleSheetSelector.m; sourceTree = "<group>"; };
59EB75C523905F7100F5AE63 /* IJSVGImageLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGImageLayer.m; sourceTree = "<group>"; }; 59EB75C523905F7100F5AE63 /* IJSVGImageLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGImageLayer.m; sourceTree = "<group>"; };
59EB75C623905F7100F5AE63 /* IJSVGCommandClose.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGCommandClose.m; sourceTree = "<group>"; }; 59EB75C623905F7100F5AE63 /* IJSVGCommandClose.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGCommandClose.m; sourceTree = "<group>"; };
@@ -246,6 +260,8 @@
59EB75D323905F7300F5AE63 /* IJSVGRendering.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGRendering.h; sourceTree = "<group>"; }; 59EB75D323905F7300F5AE63 /* IJSVGRendering.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGRendering.h; sourceTree = "<group>"; };
59EB75D423905F7300F5AE63 /* IJSVGExporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGExporter.h; sourceTree = "<group>"; }; 59EB75D423905F7300F5AE63 /* IJSVGExporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGExporter.h; sourceTree = "<group>"; };
59EB75D523905F7300F5AE63 /* IJSVGRendering.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGRendering.m; sourceTree = "<group>"; }; 59EB75D523905F7300F5AE63 /* IJSVGRendering.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGRendering.m; sourceTree = "<group>"; };
59F36506262F1ABB00BCE3FD /* IJSVGColorType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGColorType.h; sourceTree = "<group>"; };
59F36507262F1ABB00BCE3FD /* IJSVGColorType.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGColorType.m; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -316,6 +332,8 @@
59EB758523905F6C00F5AE63 /* IJSVGColor.m */, 59EB758523905F6C00F5AE63 /* IJSVGColor.m */,
59EB75A923905F6F00F5AE63 /* IJSVGColorList.h */, 59EB75A923905F6F00F5AE63 /* IJSVGColorList.h */,
59EB757123905F6C00F5AE63 /* IJSVGColorList.m */, 59EB757123905F6C00F5AE63 /* IJSVGColorList.m */,
59F36506262F1ABB00BCE3FD /* IJSVGColorType.h */,
59F36507262F1ABB00BCE3FD /* IJSVGColorType.m */,
); );
path = Colors; path = Colors;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -333,8 +351,6 @@
59EB757B23905F6C00F5AE63 /* IJSVGLayer.m */, 59EB757B23905F6C00F5AE63 /* IJSVGLayer.m */,
59EB75BF23905F7000F5AE63 /* IJSVGPatternLayer.h */, 59EB75BF23905F7000F5AE63 /* IJSVGPatternLayer.h */,
59EB758C23905F6D00F5AE63 /* IJSVGPatternLayer.m */, 59EB758C23905F6D00F5AE63 /* IJSVGPatternLayer.m */,
59EB757223905F6C00F5AE63 /* IJSVGRadialGradient.h */,
59EB75A623905F6F00F5AE63 /* IJSVGRadialGradient.m */,
59EB759723905F6D00F5AE63 /* IJSVGShapeLayer.h */, 59EB759723905F6D00F5AE63 /* IJSVGShapeLayer.h */,
59EB757A23905F6C00F5AE63 /* IJSVGShapeLayer.m */, 59EB757A23905F6C00F5AE63 /* IJSVGShapeLayer.m */,
59EB75A423905F6E00F5AE63 /* IJSVGStrokeLayer.h */, 59EB75A423905F6E00F5AE63 /* IJSVGStrokeLayer.h */,
@@ -363,6 +379,8 @@
59EB756923905F6B00F5AE63 /* IJSVGDef.m */, 59EB756923905F6B00F5AE63 /* IJSVGDef.m */,
59EB759F23905F6E00F5AE63 /* IJSVGLinearGradient.h */, 59EB759F23905F6E00F5AE63 /* IJSVGLinearGradient.h */,
59EB75C223905F7100F5AE63 /* IJSVGLinearGradient.m */, 59EB75C223905F7100F5AE63 /* IJSVGLinearGradient.m */,
59EB757223905F6C00F5AE63 /* IJSVGRadialGradient.h */,
59EB75A623905F6F00F5AE63 /* IJSVGRadialGradient.m */,
59EB75AB23905F6F00F5AE63 /* IJSVGGradient.h */, 59EB75AB23905F6F00F5AE63 /* IJSVGGradient.h */,
59EB75D223905F7300F5AE63 /* IJSVGGradient.m */, 59EB75D223905F7300F5AE63 /* IJSVGGradient.m */,
59EB758B23905F6D00F5AE63 /* IJSVGGroup.h */, 59EB758B23905F6D00F5AE63 /* IJSVGGroup.h */,
@@ -396,6 +414,14 @@
59EB75BA23905F7000F5AE63 /* IJSVGUnitLength.m */, 59EB75BA23905F7000F5AE63 /* IJSVGUnitLength.m */,
59EB759423905F6D00F5AE63 /* IJSVGUtils.h */, 59EB759423905F6D00F5AE63 /* IJSVGUtils.h */,
59EB758023905F6C00F5AE63 /* IJSVGUtils.m */, 59EB758023905F6C00F5AE63 /* IJSVGUtils.m */,
5919E65523F47FF60051873A /* IJSVGUnitRect.h */,
5919E65623F47FF60051873A /* IJSVGUnitRect.m */,
5919E65923F480330051873A /* IJSVGUnitPoint.h */,
5919E65A23F480330051873A /* IJSVGUnitPoint.m */,
59A24EBA23F480EA0090C374 /* IJSVGUnitSize.h */,
59A24EBB23F480EA0090C374 /* IJSVGUnitSize.m */,
591A3E4B25CC91F800AD45B7 /* IJSVGParsing.h */,
591A3E4C25CC91F800AD45B7 /* IJSVGParsing.m */,
); );
path = Utils; path = Utils;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -427,10 +453,10 @@
592ABBEB2397A11800F44380 /* Additions */ = { 592ABBEB2397A11800F44380 /* Additions */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
59EB75C323905F7100F5AE63 /* IJSVGBezierPathAdditions.h */,
59EB758E23905F6D00F5AE63 /* IJSVGBezierPathAdditions.m */,
59EB75B823905F7000F5AE63 /* IJSVGStringAdditions.h */, 59EB75B823905F7000F5AE63 /* IJSVGStringAdditions.h */,
59EB757823905F6C00F5AE63 /* IJSVGStringAdditions.m */, 59EB757823905F6C00F5AE63 /* IJSVGStringAdditions.m */,
594A10D8248D7C90001A3181 /* NSImage+IJSVGAdditions.h */,
594A10D9248D7C90001A3181 /* NSImage+IJSVGAdditions.m */,
); );
path = Additions; path = Additions;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -484,8 +510,6 @@
59EB757D23905F6C00F5AE63 /* IJSVGImageRep.m */, 59EB757D23905F6C00F5AE63 /* IJSVGImageRep.m */,
59EB75CD23905F7200F5AE63 /* IJSVGView.h */, 59EB75CD23905F7200F5AE63 /* IJSVGView.h */,
59EB75C123905F7100F5AE63 /* IJSVGView.m */, 59EB75C123905F7100F5AE63 /* IJSVGView.m */,
59EB756E23905F6C00F5AE63 /* IJSVGWriter.h */,
59EB758923905F6D00F5AE63 /* IJSVGWriter.m */,
); );
path = Core; path = Core;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -517,7 +541,6 @@
files = ( files = (
59EB762623905F7300F5AE63 /* IJSVGImage.h in Headers */, 59EB762623905F7300F5AE63 /* IJSVGImage.h in Headers */,
59EB760823905F7300F5AE63 /* IJSVGShapeLayer.h in Headers */, 59EB760823905F7300F5AE63 /* IJSVGShapeLayer.h in Headers */,
59EB75DF23905F7300F5AE63 /* IJSVGWriter.h in Headers */,
59EB760F23905F7300F5AE63 /* IJSVGCommandEllipticalArc.h in Headers */, 59EB760F23905F7300F5AE63 /* IJSVGCommandEllipticalArc.h in Headers */,
59EB75F523905F7300F5AE63 /* IJSVGError.h in Headers */, 59EB75F523905F7300F5AE63 /* IJSVGError.h in Headers */,
59EB763E23905F7300F5AE63 /* IJSVGView.h in Headers */, 59EB763E23905F7300F5AE63 /* IJSVGView.h in Headers */,
@@ -562,14 +585,19 @@
59EB75D623905F7300F5AE63 /* IJSVGLayer.h in Headers */, 59EB75D623905F7300F5AE63 /* IJSVGLayer.h in Headers */,
59EB763023905F7300F5AE63 /* IJSVGPatternLayer.h in Headers */, 59EB763023905F7300F5AE63 /* IJSVGPatternLayer.h in Headers */,
59EB75F023905F7300F5AE63 /* IJSVGStyleSheetSelectorRaw.h in Headers */, 59EB75F023905F7300F5AE63 /* IJSVGStyleSheetSelectorRaw.h in Headers */,
59EB763423905F7300F5AE63 /* IJSVGBezierPathAdditions.h in Headers */,
59EB761823905F7300F5AE63 /* IJSVGParser.h in Headers */, 59EB761823905F7300F5AE63 /* IJSVGParser.h in Headers */,
59EB761E23905F7300F5AE63 /* IJSVGGroupLayer.h in Headers */, 59EB761E23905F7300F5AE63 /* IJSVGGroupLayer.h in Headers */,
59EB761D23905F7300F5AE63 /* IJSVGStyle.h in Headers */, 59EB761D23905F7300F5AE63 /* IJSVGStyle.h in Headers */,
5919E65723F47FF60051873A /* IJSVGUnitRect.h in Headers */,
5919E65B23F480330051873A /* IJSVGUnitPoint.h in Headers */,
591A3E4D25CC91F800AD45B7 /* IJSVGParsing.h in Headers */,
59A24EBC23F480EA0090C374 /* IJSVGUnitSize.h in Headers */,
59EB764523905F7300F5AE63 /* IJSVGExporter.h in Headers */, 59EB764523905F7300F5AE63 /* IJSVGExporter.h in Headers */,
59F36508262F1ABB00BCE3FD /* IJSVGColorType.h in Headers */,
59E7CFAF23B148600077D599 /* IJSVGCommandParser.h in Headers */, 59E7CFAF23B148600077D599 /* IJSVGCommandParser.h in Headers */,
59EB762823905F7300F5AE63 /* IJSVGCommandClose.h in Headers */, 59EB762823905F7300F5AE63 /* IJSVGCommandClose.h in Headers */,
59EB75E423905F7300F5AE63 /* IJSVGGradientUnitLength.h in Headers */, 59EB75E423905F7300F5AE63 /* IJSVGGradientUnitLength.h in Headers */,
594A10DA248D7C90001A3181 /* NSImage+IJSVGAdditions.h in Headers */,
59EB762D23905F7300F5AE63 /* IJSVGLayerTree.h in Headers */, 59EB762D23905F7300F5AE63 /* IJSVGLayerTree.h in Headers */,
59EB760B23905F7300F5AE63 /* IJSVGStyleSheetSelector.h in Headers */, 59EB760B23905F7300F5AE63 /* IJSVGStyleSheetSelector.h in Headers */,
); );
@@ -602,7 +630,7 @@
594CF466238FF38E009B251B /* Project object */ = { 594CF466238FF38E009B251B /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 1100; LastUpgradeCheck = 1220;
ORGANIZATIONNAME = "Curtis Hard"; ORGANIZATIONNAME = "Curtis Hard";
TargetAttributes = { TargetAttributes = {
594CF46E238FF38E009B251B = { 594CF46E238FF38E009B251B = {
@@ -644,16 +672,19 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
59EB763123905F7300F5AE63 /* IJSVGText.m in Sources */, 59EB763123905F7300F5AE63 /* IJSVGText.m in Sources */,
59A24EBD23F480EA0090C374 /* IJSVGUnitSize.m in Sources */,
59EB760223905F7300F5AE63 /* IJSVGCommandVerticalLine.m in Sources */, 59EB760223905F7300F5AE63 /* IJSVGCommandVerticalLine.m in Sources */,
59EB75FD23905F7300F5AE63 /* IJSVGPatternLayer.m in Sources */, 59EB75FD23905F7300F5AE63 /* IJSVGPatternLayer.m in Sources */,
59EB763923905F7300F5AE63 /* IJSVGCommandSmoothQuadraticCurve.m in Sources */, 59EB763923905F7300F5AE63 /* IJSVGCommandSmoothQuadraticCurve.m in Sources */,
59EB75D823905F7300F5AE63 /* IJSVGStyleSheetRule.m in Sources */, 59EB75D823905F7300F5AE63 /* IJSVGStyleSheetRule.m in Sources */,
59EB75FB23905F7300F5AE63 /* IJSVGPath.m in Sources */, 59EB75FB23905F7300F5AE63 /* IJSVGPath.m in Sources */,
59F36509262F1ABB00BCE3FD /* IJSVGColorType.m in Sources */,
59EB760123905F7300F5AE63 /* IJSVGLayerTree.m in Sources */, 59EB760123905F7300F5AE63 /* IJSVGLayerTree.m in Sources */,
59EB763A23905F7300F5AE63 /* IJSVGForeignObject.m in Sources */, 59EB763A23905F7300F5AE63 /* IJSVGForeignObject.m in Sources */,
59EB761123905F7300F5AE63 /* IJSVGCommand.m in Sources */, 59EB761123905F7300F5AE63 /* IJSVGCommand.m in Sources */,
59EB760923905F7300F5AE63 /* IJSVGCommandCurve.m in Sources */, 59EB760923905F7300F5AE63 /* IJSVGCommandCurve.m in Sources */,
59EB763323905F7300F5AE63 /* IJSVGLinearGradient.m in Sources */, 59EB763323905F7300F5AE63 /* IJSVGLinearGradient.m in Sources */,
594A10DB248D7C90001A3181 /* NSImage+IJSVGAdditions.m in Sources */,
59EB761923905F7300F5AE63 /* IJSVGCommandSmoothCurve.m in Sources */, 59EB761923905F7300F5AE63 /* IJSVGCommandSmoothCurve.m in Sources */,
59EB761323905F7300F5AE63 /* IJSVG.m in Sources */, 59EB761323905F7300F5AE63 /* IJSVG.m in Sources */,
59EB763623905F7300F5AE63 /* IJSVGImageLayer.m in Sources */, 59EB763623905F7300F5AE63 /* IJSVGImageLayer.m in Sources */,
@@ -663,22 +694,23 @@
59EB763523905F7300F5AE63 /* IJSVGStyleSheetSelector.m in Sources */, 59EB763523905F7300F5AE63 /* IJSVGStyleSheetSelector.m in Sources */,
59E7CFB023B148600077D599 /* IJSVGCommandParser.m in Sources */, 59E7CFB023B148600077D599 /* IJSVGCommandParser.m in Sources */,
59EB760723905F7300F5AE63 /* IJSVGNode.m in Sources */, 59EB760723905F7300F5AE63 /* IJSVGNode.m in Sources */,
5919E65C23F480330051873A /* IJSVGUnitPoint.m in Sources */,
59EB75E923905F7300F5AE63 /* IJSVGStringAdditions.m in Sources */, 59EB75E923905F7300F5AE63 /* IJSVGStringAdditions.m in Sources */,
59EB761723905F7300F5AE63 /* IJSVGRadialGradient.m in Sources */, 59EB761723905F7300F5AE63 /* IJSVGRadialGradient.m in Sources */,
59EB75E223905F7300F5AE63 /* IJSVGColorList.m in Sources */, 59EB75E223905F7300F5AE63 /* IJSVGColorList.m in Sources */,
59EB75ED23905F7300F5AE63 /* IJSVGFontConverter.m in Sources */, 59EB75ED23905F7300F5AE63 /* IJSVGFontConverter.m in Sources */,
59EB763C23905F7300F5AE63 /* IJSVGCommandEllipticalArc.m in Sources */, 59EB763C23905F7300F5AE63 /* IJSVGCommandEllipticalArc.m in Sources */,
59EB75E723905F7300F5AE63 /* IJSVGImage.m in Sources */, 59EB75E723905F7300F5AE63 /* IJSVGImage.m in Sources */,
59EB75FF23905F7300F5AE63 /* IJSVGBezierPathAdditions.m in Sources */,
59EB75E623905F7300F5AE63 /* IJSVGStyle.m in Sources */, 59EB75E623905F7300F5AE63 /* IJSVGStyle.m in Sources */,
59EB75EB23905F7300F5AE63 /* IJSVGShapeLayer.m in Sources */, 59EB75EB23905F7300F5AE63 /* IJSVGShapeLayer.m in Sources */,
59EB75F623905F7300F5AE63 /* IJSVGColor.m in Sources */, 59EB75F623905F7300F5AE63 /* IJSVGColor.m in Sources */,
59EB75F323905F7300F5AE63 /* IJSVGGroupLayer.m in Sources */, 59EB75F323905F7300F5AE63 /* IJSVGGroupLayer.m in Sources */,
591A3E4E25CC91F800AD45B7 /* IJSVGParsing.m in Sources */,
5919E65823F47FF60051873A /* IJSVGUnitRect.m in Sources */,
59EB762523905F7300F5AE63 /* IJSVGTransform.m in Sources */, 59EB762523905F7300F5AE63 /* IJSVGTransform.m in Sources */,
59EB760023905F7300F5AE63 /* IJSVGGradientLayer.m in Sources */, 59EB760023905F7300F5AE63 /* IJSVGGradientLayer.m in Sources */,
59EB762B23905F7300F5AE63 /* IJSVGUnitLength.m in Sources */, 59EB762B23905F7300F5AE63 /* IJSVGUnitLength.m in Sources */,
59EB75F823905F7300F5AE63 /* IJSVGCommandHorizontalLine.m in Sources */, 59EB75F823905F7300F5AE63 /* IJSVGCommandHorizontalLine.m in Sources */,
59EB75FA23905F7300F5AE63 /* IJSVGWriter.m in Sources */,
59EB764623905F7300F5AE63 /* IJSVGRendering.m in Sources */, 59EB764623905F7300F5AE63 /* IJSVGRendering.m in Sources */,
59EB762E23905F7300F5AE63 /* IJSVGExporter.m in Sources */, 59EB762E23905F7300F5AE63 /* IJSVGExporter.m in Sources */,
59EB761623905F7300F5AE63 /* IJSVGParser.m in Sources */, 59EB761623905F7300F5AE63 /* IJSVGParser.m in Sources */,
@@ -731,6 +763,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -746,6 +779,7 @@
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0; GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREFIX_HEADER = "";
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1", "DEBUG=1",
"$(inherited)", "$(inherited)",
@@ -792,6 +826,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -805,6 +840,7 @@
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_PREFIX_HEADER = "";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1130" LastUpgradeVersion = "1250"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -15,7 +15,7 @@
<key>594CF46E238FF38E009B251B</key> <key>594CF46E238FF38E009B251B</key>
<dict> <dict>
<key>primary</key> <key>primary</key>
<true/> <true />
</dict> </dict>
</dict> </dict>
</dict> </dict>
+1 -1
View File
@@ -19,6 +19,6 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 Curtis Hard. All rights reserved.</string> <string>Copyright © 2020 Curtis Hard. All rights reserved.</string>
</dict> </dict>
</plist> </plist>
@@ -1,19 +0,0 @@
//
// IJSVGBezierPathAdditions.h
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
@interface NSBezierPath (IJSVGAdditions)
- (void)addQuadCurveToPoint:(NSPoint)aPoint
controlPoint:(NSPoint)cp;
- (CGPathRef)newCGPathRef:(BOOL)autoClose;
@end
@@ -1,87 +0,0 @@
//
// IJSVGBezierPathAdditions.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGBezierPathAdditions.h"
@implementation NSBezierPath (IJSVGAdditions)
- (void)addQuadCurveToPoint:(CGPoint)QP2
controlPoint:(CGPoint)QP1
{
CGPoint QP0 = [self currentPoint];
CGPoint CP3 = QP2;
CGPoint CP1 = CGPointMake(QP0.x + ((2.0 / 3.0) * (QP1.x - QP0.x)), QP0.y + ((2.0 / 3.0) * (QP1.y - QP0.y)));
CGPoint CP2 = CGPointMake(QP2.x + (2.0 / 3.0) * (QP1.x - QP2.x), QP2.y + (2.0 / 3.0) * (QP1.y - QP2.y));
[self curveToPoint:CP3
controlPoint1:CP1
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
@@ -14,5 +14,6 @@
- (BOOL)ijsvg_isNumeric; - (BOOL)ijsvg_isNumeric;
- (BOOL)ijsvg_containsAlpha; - (BOOL)ijsvg_containsAlpha;
- (NSArray*)ijsvg_componentsSplitByWhiteSpace; - (NSArray*)ijsvg_componentsSplitByWhiteSpace;
- (BOOL)ijsvg_isHexString;
@end @end
@@ -7,51 +7,38 @@
// //
#import "IJSVGStringAdditions.h" #import "IJSVGStringAdditions.h"
#import "IJSVGUtils.h"
@implementation NSString (IJSVGAdditions) @implementation NSString (IJSVGAdditions)
- (NSArray<NSString*>*)ijsvg_componentsSeparatedByChars:(const char*)aChar - (NSArray<NSString*>*)ijsvg_componentsSeparatedByChars:(const char*)aChar
{ {
NSMutableArray* comp = [[[NSMutableArray alloc] init] autorelease]; char* chars = (char*)self.UTF8String;
NSInteger length = self.length; if(chars == NULL || strlen(chars) == 0) {
unichar* chars = (unichar*)calloc(sizeof(unichar), self.length); return @[];
NSInteger ind = 0;
BOOL startedString = NO;
const char* buffer = self.UTF8String;
for (NSInteger i = 0; i < length; i++) {
unichar theChar = buffer[i];
// start the buffer
BOOL isEqualToChar = strchr(aChar, theChar) != NULL;
if (isEqualToChar == NO) {
startedString = YES;
chars[ind++] = theChar;
}
// has started and char is the search char, or its at end
if ((startedString == YES && isEqualToChar) || (i == (length - 1) && startedString == YES)) {
startedString = NO;
// append the comp
[comp addObject:[NSString stringWithCharacters:chars length:ind]];
// restart and realloc the memory
ind = 0;
chars = memset(chars, '\0', sizeof(unichar) * ind);
}
} }
free(chars); NSMutableArray<NSString*>* strings = nil;
return comp; strings = [[[NSMutableArray alloc] init] autorelease];
char* copy = strdup(chars);
char* spt = NULL;
char* ptr = strtok_r(copy, aChar, &spt);
while(ptr != NULL) {
NSString* possibleString = nil;
if((possibleString = [NSString stringWithUTF8String:ptr]) != nil) {
[strings addObject:possibleString];
}
ptr = strtok_r(NULL, aChar, &spt);
}
(void)free(copy), copy = NULL;
return strings;
} }
- (BOOL)ijsvg_containsAlpha - (BOOL)ijsvg_containsAlpha
{ {
const char* buffer = self.UTF8String; const char* buffer = self.UTF8String;
unsigned long length = strlen(buffer); char currentChar;
for (int i = 0; i < length; i++) { while((currentChar = *buffer++) ) {
if (isalpha(buffer[i])) { if (isalpha(currentChar)) {
return YES; return YES;
} }
} }
@@ -61,9 +48,9 @@
- (BOOL)ijsvg_isNumeric - (BOOL)ijsvg_isNumeric
{ {
const char* buffer = self.UTF8String; const char* buffer = self.UTF8String;
unsigned long length = strlen(buffer); char currentChar;
for (int i = 0; i < length; i++) { while((currentChar = *buffer++) ) {
if (!isnumber(buffer[i])) { if (!isnumber(currentChar)) {
return NO; return NO;
} }
} }
@@ -75,4 +62,10 @@
return [self ijsvg_componentsSeparatedByChars:"\t\n\r "]; return [self ijsvg_componentsSeparatedByChars:"\t\n\r "];
} }
- (BOOL)ijsvg_isHexString
{
const char* chars = self.UTF8String;
return IJSVGCharBufferIsHEX((char*)chars);
}
@end @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
@@ -167,6 +167,8 @@ typedef NS_ENUM(NSInteger, IJSVGPredefinedColor) {
IJSVGColorYellowgreen IJSVGColorYellowgreen
}; };
extern NSString* const IJSVGColorCurrentColorName;
@interface IJSVGColor : NSObject @interface IJSVGColor : NSObject
CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightness); CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightness);
@@ -8,11 +8,16 @@
#import "IJSVGColor.h" #import "IJSVGColor.h"
#import "IJSVGUtils.h" #import "IJSVGUtils.h"
#import "IJSVGStringAdditions.h"
#import "IJSVGParsing.h"
NSString* const IJSVGColorCurrentColorName = @"currentColor";
@implementation IJSVGColor @implementation IJSVGColor
static NSDictionary* _colorTree = nil; static NSDictionary* _colorTree = nil;
CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightness) CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightness)
{ {
hue *= (1.f / 360.f); hue *= (1.f / 360.f);
@@ -241,37 +246,51 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
+ (NSColor*)colorFromString:(NSString*)string + (NSColor*)colorFromString:(NSString*)string
{ {
NSCharacterSet* set = NSCharacterSet.whitespaceAndNewlineCharacterSet; // swap over to C for performance
string = [string stringByTrimmingCharactersInSet:set]; if(string == nil) {
if ([string length] < 3) {
return nil; return nil;
} }
NSColor* color = nil; const char* oString = string.UTF8String;
string = [string lowercaseString]; if(strlen(oString) == 0) {
if ([self.class isHex:string] == NO) { return nil;
color = [self.class colorFromPredefinedColorName:string];
if (color != nil) {
return color;
}
} }
// is simply a clear color, dont fill char* str = IJSVGTimmedCharBufferCreate(oString);
if ([string.lowercaseString isEqualToString:@"none"] || if (IJSVGCharBufferIsHEX(str) == YES) {
[string.lowercaseString isEqualToString:@"transparent"]) { (void)free(str), str = NULL;
return [self computeColorSpace:NSColor.clearColor]; return [self.class colorFromHEXString:string];
} }
// is it RGB? // is it RGB?
if ([string hasPrefix:@"rgb"] == YES) { if (IJSVGCharBufferHasPrefix(str, "rgb") == YES) {
NSRange range = [IJSVGUtils rangeOfParentheses:string]; NSUInteger count = 0;
NSString* rgbString = [string substringWithRange:range]; IJSVGParsingStringMethod** methods = NULL;
NSArray* parts = [rgbString ijsvg_componentsSeparatedByChars:","]; methods = IJSVGParsingMethodParseString(str, &count);
IJSVGParsingStringMethod* method = methods[0];
// memory clean for the string
(void)free(str), str = NULL;
// nothing to return, just mem clean and get out of here
if(count == 0 || methods == NULL) {
if(methods != NULL) {
IJSVGParsingStringMethodsRelease(methods, count);
methods = NULL;
}
return nil;
}
// parse the parameters
NSString* parameters = [NSString stringWithUTF8String:method->parameters];
NSArray* parts = [parameters ijsvg_componentsSeparatedByChars:","];
NSString* alpha = @"100%"; NSString* alpha = @"100%";
if (parts.count == 4) { if (parts.count == 4) {
alpha = parts[3]; alpha = parts[3];
} }
IJSVGParsingStringMethodsRelease(methods, count);
methods = NULL;
return [self colorFromRString:parts[0] return [self colorFromRString:parts[0]
gString:parts[1] gString:parts[1]
bString:parts[2] bString:parts[2]
@@ -279,10 +298,10 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
} }
// is it HSL? // is it HSL?
if ([string hasPrefix:@"hsl"]) { if (IJSVGCharBufferHasPrefix(str, "hsl")) {
NSInteger count = 0; NSInteger count = 0;
CGFloat* params = [IJSVGUtils commandParameters:string CGFloat* params = [IJSVGUtils scanFloatsFromCString:str
count:&count]; size:&count];
CGFloat alpha = 1; CGFloat alpha = 1;
if (count == 4) { if (count == 4) {
alpha = params[3]; alpha = params[3];
@@ -290,21 +309,30 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
// convert HSL to HSB // convert HSL to HSB
CGFloat* hsb = IJSVGColorCSSHSLToHSB(params[0], params[1], params[2]); CGFloat* hsb = IJSVGColorCSSHSLToHSB(params[0], params[1], params[2]);
color = [NSColor colorWithDeviceHue:hsb[0] NSColor* color = [NSColor colorWithDeviceHue:hsb[0]
saturation:hsb[1] saturation:hsb[1]
brightness:hsb[2] brightness:hsb[2]
alpha:alpha]; alpha:alpha];
color = [self computeColorSpace:color]; color = [self computeColorSpace:color];
// memory clean! // memory clean!
free(hsb); (void)free(str), str = NULL;
free(params); (void)free(hsb), hsb = NULL;
(void)free(params), params = NULL;
return color; return color;
} }
color = [self.class colorFromHEXString:string]; // is simply a clear color, dont fill
return color; if (strcmp(str, "none") == 0 ||
strcmp(str, "transparent") == 0) {
(void)free(str), str = NULL;
return [self computeColorSpace:NSColor.clearColor];
}
// could return nil
(void)free(str), str = NULL;
return [self.class colorFromPredefinedColorName:string];
} }
+ (NSColor*)colorFromPredefinedColorName:(NSString*)name + (NSColor*)colorFromPredefinedColorName:(NSString*)name
@@ -707,14 +735,7 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
+ (BOOL)isHex:(NSString*)string + (BOOL)isHex:(NSString*)string
{ {
const char* validList = "0123456789ABCDEFabcdef#"; return string.ijsvg_isHexString;
for (NSInteger i = 0; i < string.length; i++) {
char c = [string characterAtIndex:i];
if (strchr(validList, c) == NULL) {
return NO;
}
}
return YES;
} }
+ (unsigned long)lengthOfHEXInteger:(NSUInteger)hex + (unsigned long)lengthOfHEXInteger:(NSUInteger)hex
@@ -760,28 +781,38 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
containsAlphaComponent:(BOOL*)containsAlphaComponent containsAlphaComponent:(BOOL*)containsAlphaComponent
{ {
// absolutely no string // absolutely no string
if (string == nil || string.length == 0 || ![self.class isHex:string]) { if(string == nil) {
return nil; return nil;
} }
if ([string hasPrefix:@"#"] == YES) { char* str = (char*)string.UTF8String;
string = [string substringFromIndex:1]; size_t length = strlen(str);
if (length == 0 || IJSVGCharBufferIsHEX(str) == NO) {
return nil;
} }
// whats the length? // remove the hash from the front of the string
NSUInteger length = string.length; if(str[0] == '#') {
length--;
str++;
}
unsigned long hex;
// we need to work out if its shorthand
// if it is, the length needs to be length*2
if (length == 3 || length == 4) { if (length == 3 || length == 4) {
// shorthand... char* chars = NULL;
NSMutableString* str = [[[NSMutableString alloc] init] autorelease]; chars = (char*)calloc(sizeof(char),length*2+1);
for (NSInteger i = 0; i < length; i++) { for(int i = 0; i < length; i++) {
NSString* sub = [string substringWithRange:NSMakeRange(i, 1)]; chars[i*2] = chars[i*2+1] = str[i];
[str appendFormat:@"%@%@", sub, sub];
} }
string = str; hex = strtoul(chars, NULL, 16);
(void)free(chars), chars = NULL;
} else {
hex = strtoul(str, NULL, 16);
} }
// now convert rest to hex // now convert rest to hex
unsigned long hex = [self HEXFromArbitraryHexString:string];
if (containsAlphaComponent != nil) { if (containsAlphaComponent != nil) {
*containsAlphaComponent = [self HEXContainsAlphaComponent:hex]; *containsAlphaComponent = [self HEXContainsAlphaComponent:hex];
} }
@@ -6,16 +6,19 @@
// Copyright © 2019 Curtis Hard. All rights reserved. // Copyright © 2019 Curtis Hard. All rights reserved.
// //
#import "IJSVGColor.h" #import <IJSVG/IJSVGColor.h>
#import <IJSVG/IJSVGColorType.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface IJSVGColorList : NSObject <NSCopying> { @interface IJSVGColorList : NSObject <NSCopying> {
@private @private
NSMutableDictionary<NSColor*, NSColor*>* _replacementColorTree; NSMutableDictionary<NSColor*, NSColor*>* _replacementColorTree;
NSMutableSet<NSColor*>* _colors; NSMutableSet<IJSVGColorType*>* _colors;
} }
@property (nonatomic, assign, readonly) NSUInteger count;
- (NSColor*)proposedColorForColor:(NSColor*)color; - (NSColor*)proposedColorForColor:(NSColor*)color;
- (void)removeAllReplacementColors; - (void)removeAllReplacementColors;
- (void)removeReplacementColor:(NSColor*)color; - (void)removeReplacementColor:(NSColor*)color;
@@ -25,7 +28,8 @@
clearExistingColors:(BOOL)clearExistingColors; clearExistingColors:(BOOL)clearExistingColors;
- (void)addColorsFromList:(IJSVGColorList*)sheet; - (void)addColorsFromList:(IJSVGColorList*)sheet;
- (NSSet<NSColor*>*)colors; - (NSSet<IJSVGColorType*>*)colors;
- (void)addColor:(NSColor*)color; - (void)addColor:(IJSVGColorType*)color;
- (NSDictionary<NSColor*, NSColor*>*)replacementColors;
@end @end
@@ -89,24 +89,48 @@
} }
} }
- (NSSet<NSColor*>*)colors - (NSSet<IJSVGColorType*>*)colors
{ {
return [NSSet setWithSet:_colors]; return [NSSet setWithSet:_colors];
} }
- (void)addColorsFromList:(IJSVGColorList*)sheet - (void)addColorsFromList:(IJSVGColorList*)sheet
{ {
[_colors addObjectsFromArray:sheet.colors.allObjects]; for(IJSVGColorType* color in sheet.colors) {
[self addColor:color];
}
} }
- (void)addColor:(NSColor*)color - (void)addColor:(IJSVGColorType*)color
{ {
[_colors addObject:[IJSVGColor computeColorSpace:color]]; // we just need to update its bit mask
if([_colors containsObject:color] == YES) {
void (^handler)(IJSVGColorType * _Nonnull obj, BOOL * _Nonnull stop) =
^(IJSVGColorType * _Nonnull obj, BOOL * _Nonnull stop) {
if([obj isEqual:color] == YES) {
obj.flags |= color.flags;
*stop = YES;
}
};
[_colors enumerateObjectsUsingBlock:handler];
return;
}
[_colors addObject:color];
} }
- (void)removeColor:(NSColor*)color - (NSDictionary<NSColor*,NSColor*>*)replacementColors
{ {
[_colors removeObject:[IJSVGColor computeColorSpace:color]]; return _replacementColorTree;
}
- (void)removeColor:(IJSVGColorType*)color
{
[_colors removeObject:color];
}
- (NSUInteger)count
{
return _colors.count;
} }
@end @end
@@ -0,0 +1,30 @@
//
// IJSVGColorType.h
// IJSVG
//
// Created by Curtis Hard on 20/04/2021.
// Copyright © 2021 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
typedef NS_OPTIONS(NSInteger, IJSVGColorTypeFlags) {
IJSVGColorTypeNone = 0,
IJSVGColorTypeFlagUnknown = 1 << 0,
IJSVGColorTypeFlagFill = 1 << 1,
IJSVGColorTypeFlagStroke = 1 << 2,
IJSVGColorTypeFlagStop = 1 << 3
};
@interface IJSVGColorType : NSObject {
}
@property (nonatomic, retain) NSColor* color;
@property (nonatomic, assign) IJSVGColorTypeFlags flags;
+ (IJSVGColorType*)typeWithColor:(NSColor*)color
flags:(IJSVGColorTypeFlags)mask;
@end
@@ -0,0 +1,41 @@
//
// IJSVGColorType.m
// IJSVG
//
// Created by Curtis Hard on 20/04/2021.
// Copyright © 2021 Curtis Hard. All rights reserved.
//
#import "IJSVGColorType.h"
@implementation IJSVGColorType
- (void)dealloc
{
(void)[_color release], _color = nil;
[super dealloc];
}
+ (IJSVGColorType*)typeWithColor:(NSColor*)color
flags:(IJSVGColorTypeFlags)mask
{
IJSVGColorType* type = [[[self alloc] init] autorelease];
type.color = color;
type.flags = mask;
return type;
}
- (BOOL)isEqual:(id)object
{
if([object isKindOfClass:IJSVGColorType.class] == NO) {
return NO;
}
return [self.color isEqual:((IJSVGColorType*)object).color];
}
- (NSUInteger)hash
{
return self.color.hash;
}
@end
@@ -6,8 +6,8 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved. // Copyright (c) 2014 Curtis Hard. All rights reserved.
// //
#import "IJSVGCommandParser.h" #import <IJSVG/IJSVGCommandParser.h>
#import "IJSVGPath.h" #import <IJSVG/IJSVGPath.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
static const NSInteger IJSVGCustomVariableParameterCount = NSNotFound; static const NSInteger IJSVGCustomVariableParameterCount = NSNotFound;
@@ -18,16 +18,8 @@ typedef NS_ENUM(NSInteger, IJSVGCommandType) {
}; };
@interface IJSVGCommand : NSObject { @interface IJSVGCommand : NSObject {
NSString* commandString; @private
char command;
CGFloat* parameters;
NSInteger parameterCount;
NSArray<IJSVGCommand*>* subCommands;
NSInteger requiredParameters;
IJSVGCommandType type;
IJSVGCommand* previousCommand;
NSInteger _currentIndex; NSInteger _currentIndex;
BOOL isSubCommand;
} }
@property (nonatomic, copy) NSString* commandString; @property (nonatomic, copy) NSString* commandString;
@@ -55,8 +47,8 @@ typedef NS_ENUM(NSInteger, IJSVGCommandType) {
intoArray:(NSMutableArray<IJSVGCommand*>*)commands intoArray:(NSMutableArray<IJSVGCommand*>*)commands
parentCommand:(IJSVGCommand*)parentCommand; parentCommand:(IJSVGCommand*)parentCommand;
- (id)initWithCommandString:(NSString*)str - (id)initWithCommandStringBuffer:(const char*)str
dataStream:(IJSVGPathDataStream*)dataStream; dataStream:(IJSVGPathDataStream*)dataStream;
- (IJSVGCommand*)subcommandWithParameters:(CGFloat*)subParams - (IJSVGCommand*)subcommandWithParameters:(CGFloat*)subParams
paramCount:(NSInteger)paramCount paramCount:(NSInteger)paramCount
previousCommand:(IJSVGCommand*)command; previousCommand:(IJSVGCommand*)command;
@@ -22,15 +22,6 @@
@implementation IJSVGCommand @implementation IJSVGCommand
@synthesize commandString;
@synthesize command;
@synthesize parameterCount;
@synthesize parameters;
@synthesize subCommands;
@synthesize type;
@synthesize previousCommand;
@synthesize isSubCommand;
+ (BOOL)requiresCustomParameterParsing + (BOOL)requiresCustomParameterParsing
{ {
return NO; return NO;
@@ -103,26 +94,26 @@
- (void)dealloc - (void)dealloc
{ {
(void)([commandString release]), commandString = nil; (void)([_commandString release]), _commandString = nil;
(void)([subCommands release]), subCommands = nil; (void)([_subCommands release]), _subCommands = nil;
if (parameters) { if (_parameters) {
(void)(free(parameters)), parameters = nil; (void)(free(_parameters)), _parameters = nil;
} }
[super dealloc]; [super dealloc];
} }
- (id)initWithCommandString:(NSString*)str - (id)initWithCommandStringBuffer:(const char*)str
dataStream:(IJSVGPathDataStream*)dataStream dataStream:(IJSVGPathDataStream*)dataStream
{ {
if ((self = [super init]) != nil) { if ((self = [super init]) != nil) {
// work out the basics // work out the basics
_currentIndex = 0; _currentIndex = 0;
command = [str characterAtIndex:0]; _command = str[0];
type = [IJSVGUtils typeForCommandChar:command]; _type = [IJSVGUtils typeForCommandChar:_command];
NSInteger sets = 0; NSInteger sets = 0;
NSInteger paramCount = [self.class requiredParameterCount]; NSInteger paramCount = [self.class requiredParameterCount];
IJSVGPathDataSequence* sequence = [self.class pathDataSequence]; IJSVGPathDataSequence* sequence = [self.class pathDataSequence];
parameters = IJSVGParsePathDataStreamSequence(str.UTF8String, str.length, _parameters = IJSVGParsePathDataStreamSequence(str, strlen(str),
dataStream, sequence, [self.class requiredParameterCount], &sets); dataStream, sequence, [self.class requiredParameterCount], &sets);
if (sets <= 1) { if (sets <= 1) {
@@ -130,7 +121,7 @@
IJSVGCommand* command = [self subcommandWithParameters:subParams IJSVGCommand* command = [self subcommandWithParameters:subParams
paramCount:paramCount paramCount:paramCount
previousCommand:nil]; previousCommand:nil];
subCommands = @[ command ].retain; _subCommands = @[ command ].retain;
} else { } else {
NSMutableArray<IJSVGCommand*>* subCommandArray = nil; NSMutableArray<IJSVGCommand*>* subCommandArray = nil;
subCommandArray = [[NSMutableArray alloc] initWithCapacity:sets].autorelease; subCommandArray = [[NSMutableArray alloc] initWithCapacity:sets].autorelease;
@@ -154,7 +145,7 @@
} }
// store the retained value // store the retained value
subCommands = subCommandArray.copy; _subCommands = subCommandArray.copy;
} }
} }
return self; return self;
@@ -188,15 +179,15 @@
- (CGFloat)readFloat - (CGFloat)readFloat
{ {
CGFloat f = parameters[_currentIndex]; CGFloat f = _parameters[_currentIndex];
_currentIndex++; _currentIndex++;
return f; return f;
} }
- (NSPoint)readPoint - (NSPoint)readPoint
{ {
CGFloat x = parameters[_currentIndex]; CGFloat x = _parameters[_currentIndex];
CGFloat y = parameters[_currentIndex + 1]; CGFloat y = _parameters[_currentIndex + 1];
_currentIndex += 2; _currentIndex += 2;
return NSMakePoint(x, y); return NSMakePoint(x, y);
} }
@@ -211,4 +202,16 @@
_currentIndex = 0; _currentIndex = 0;
} }
- (NSString *)description
{
NSMutableString* str = [[[NSMutableString alloc] init] autorelease];
[str appendFormat:@"%c ",_command];
NSMutableArray* args = [[[NSMutableArray alloc] initWithCapacity:_parameterCount] autorelease];
for(int i = 0; i < _parameterCount; i++) {
[args addObject:[NSString stringWithFormat:@"%f",_parameters[i]]];
}
[str appendString:[args componentsJoinedByString:@", "]];
return str;
}
@end @end
@@ -23,14 +23,16 @@
path:(IJSVGPath*)path path:(IJSVGPath*)path
{ {
if (type == kIJSVGCommandTypeAbsolute) { if (type == kIJSVGCommandTypeAbsolute) {
[path.path curveToPoint:NSMakePoint(params[4], params[5]) CGPathAddCurveToPoint(path.path, NULL, params[0], params[1],
controlPoint1:NSMakePoint(params[0], params[1]) params[2], params[3],
controlPoint2:NSMakePoint(params[2], params[3])]; params[4], params[5]);
return; return;
} }
[path.path relativeCurveToPoint:NSMakePoint(params[4], params[5]) CGPoint currentPoint = path.currentPoint;
controlPoint1:NSMakePoint(params[0], params[1]) CGPathAddCurveToPoint(path.path, NULL,
controlPoint2:NSMakePoint(params[2], params[3])]; currentPoint.x + params[0], currentPoint.y + params[1],
currentPoint.x + params[2], currentPoint.y + params[3],
currentPoint.x + params[4], currentPoint.y + params[5]);
} }
@end @end
@@ -20,7 +20,8 @@ static IJSVGPathDataSequence* _sequence;
+ (IJSVGPathDataSequence*)pathDataSequence + (IJSVGPathDataSequence*)pathDataSequence
{ {
if(_sequence == NULL) { static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sequence = (IJSVGPathDataSequence*)malloc(sizeof(IJSVGPathDataSequence) * 7); _sequence = (IJSVGPathDataSequence*)malloc(sizeof(IJSVGPathDataSequence) * 7);
_sequence[0] = kIJSVGPathDataSequenceTypeFloat; _sequence[0] = kIJSVGPathDataSequenceTypeFloat;
_sequence[1] = kIJSVGPathDataSequenceTypeFloat; _sequence[1] = kIJSVGPathDataSequenceTypeFloat;
@@ -29,10 +30,11 @@ static IJSVGPathDataSequence* _sequence;
_sequence[4] = kIJSVGPathDataSequenceTypeFlag; _sequence[4] = kIJSVGPathDataSequenceTypeFlag;
_sequence[5] = kIJSVGPathDataSequenceTypeFloat; _sequence[5] = kIJSVGPathDataSequenceTypeFloat;
_sequence[6] = kIJSVGPathDataSequenceTypeFloat; _sequence[6] = kIJSVGPathDataSequenceTypeFloat;
} });
return _sequence; return _sequence;
} }
// modified from https://github.com/SVGKit/SVGKit/blob/880c94a5b77b6f22beb491a7a7e02ace220c32af/Source/Parsers/SVGKPointsAndPathsParser.m
+ (void)runWithParams:(CGFloat*)params + (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand command:(IJSVGCommand*)currentCommand
@@ -42,10 +44,10 @@ static IJSVGPathDataSequence* _sequence;
{ {
CGPoint radii = CGPointZero; CGPoint radii = CGPointZero;
CGPoint arcEndPoint = CGPointZero; CGPoint arcEndPoint = CGPointZero;
CGPoint arcStartPoint = path.currentPoint; CGPoint pathCurrentPoint = path.currentPoint;
CGFloat xAxisRotation = 0; CGFloat xAxisRotation = 0.f;
BOOL largeArcFlag = 0; BOOL largeArcFlag = NO;
BOOL sweepFlag = 0; BOOL sweepFlag = NO;
radii = [currentCommand readPoint]; radii = [currentCommand readPoint];
xAxisRotation = [currentCommand readFloat]; xAxisRotation = [currentCommand readFloat];
@@ -53,72 +55,85 @@ static IJSVGPathDataSequence* _sequence;
sweepFlag = [currentCommand readBOOL]; sweepFlag = [currentCommand readBOOL];
arcEndPoint = [currentCommand readPoint]; arcEndPoint = [currentCommand readPoint];
if (type == kIJSVGCommandTypeRelative) { CGFloat rx = fabs(radii.x);
arcEndPoint.x += path.currentPoint.x; CGFloat ry = fabs(radii.y);
arcEndPoint.y += path.currentPoint.y;
}
xAxisRotation *= M_PI / 180.f; xAxisRotation *= M_PI / 180.f;
CGPoint currentPoint = CGPointMake(cos(xAxisRotation) * (arcStartPoint.x - arcEndPoint.x) / 2.0 + sin(xAxisRotation) * (arcStartPoint.y - arcEndPoint.y) / 2.0, -sin(xAxisRotation) * (arcStartPoint.x - arcEndPoint.x) / 2.0 + cos(xAxisRotation) * (arcStartPoint.y - arcEndPoint.y) / 2.0); xAxisRotation = fmod(xAxisRotation, 2.f * M_PI);
CGFloat radiiAdjustment = pow(currentPoint.x, 2) / pow(radii.x, 2) + pow(currentPoint.y, 2) / pow(radii.y, 2); if (type == kIJSVGCommandTypeRelative) {
radii.x *= (radiiAdjustment > 1) ? sqrt(radiiAdjustment) : 1; arcEndPoint.x += pathCurrentPoint.x;
radii.y *= (radiiAdjustment > 1) ? sqrt(radiiAdjustment) : 1; arcEndPoint.y += pathCurrentPoint.y;
}
CGFloat sweep = (largeArcFlag == sweepFlag ? -1 : 1) * sqrt(((pow(radii.x, 2) * pow(radii.y, 2)) - (pow(radii.x, 2) * pow(currentPoint.y, 2)) - (pow(radii.y, 2) * pow(currentPoint.x, 2))) / (pow(radii.x, 2) * pow(currentPoint.y, 2) + pow(radii.y, 2) * pow(currentPoint.x, 2))); CGFloat x1 = pathCurrentPoint.x;
sweep = (sweep != sweep) ? 0 : sweep; CGFloat y1 = pathCurrentPoint.y;
CGPoint preCenterPoint = CGPointMake(sweep * radii.x * currentPoint.y / radii.y, sweep * -radii.y * currentPoint.x / radii.x);
CGPoint centerPoint = CGPointMake((arcStartPoint.x + arcEndPoint.x) / 2.0 + cos(xAxisRotation) * preCenterPoint.x - sin(xAxisRotation) * preCenterPoint.y, (arcStartPoint.y + arcEndPoint.y) / 2.0 + sin(xAxisRotation) * preCenterPoint.x + cos(xAxisRotation) * preCenterPoint.y); CGFloat x2 = arcEndPoint.x;
CGFloat y2 = arcEndPoint.y;
CGFloat startAngle = angle(CGPointMake(1, 0), CGPointMake((currentPoint.x - preCenterPoint.x) / radii.x, (currentPoint.y - preCenterPoint.y) / radii.y)); if (rx == 0.f || ry == 0.f) {
CGPathAddLineToPoint(path.path, NULL, x2, y2);
CGPoint deltaU = CGPointMake((currentPoint.x - preCenterPoint.x) / radii.x,
(currentPoint.y - preCenterPoint.y) / radii.y);
CGPoint deltaV = CGPointMake((-currentPoint.x - preCenterPoint.x) / radii.x,
(-currentPoint.y - preCenterPoint.y) / radii.y);
CGFloat angleDelta = (deltaU.x * deltaV.y < deltaU.y * deltaV.x ? -1 : 1) * acos(ratio(deltaU, deltaV));
angleDelta = (ratio(deltaU, deltaV) <= -1) ? M_PI : (ratio(deltaU, deltaV) >= 1) ? 0 : angleDelta;
// check for actually numbers, if this is not valid
// kill it, blame WWDC 2017 SVG background for this...
if (isnan(startAngle) || isnan(angleDelta)) {
return; return;
} }
CGFloat radius = MAX(radii.x, radii.y); CGFloat cosPhi = cos(xAxisRotation);
CGPoint scale = (radii.x > radii.y) ? CGPointMake(1, radii.y / radii.x) : CGPointMake(radii.x / radii.y, 1); CGFloat sinPhi = sin(xAxisRotation);
NSAffineTransform* trans = NSAffineTransform.transform; CGFloat x1p = cosPhi * (x1 - x2) / 2.f + sinPhi * (y1 - y2) / 2.f;
[trans translateXBy:-centerPoint.x yBy:-centerPoint.y]; CGFloat y1p = -sinPhi * (x1 - x2) / 2.f + cosPhi * (y1 - y2) / 2.f;
[path.path transformUsingAffineTransform:trans];
trans = NSAffineTransform.transform; CGFloat rx_2 = rx * rx;
[trans rotateByRadians:-xAxisRotation]; CGFloat ry_2 = ry * ry;
[path.path transformUsingAffineTransform:trans]; CGFloat xp_2 = x1p * x1p;
CGFloat yp_2 = y1p * y1p;
trans = NSAffineTransform.transform; CGFloat delta = xp_2 / rx_2 + yp_2 / ry_2;
[trans scaleXBy:(1 / scale.x) yBy:(1 / scale.y)];
[path.path transformUsingAffineTransform:trans];
[path.path appendBezierPathWithArcWithCenter:NSZeroPoint if (delta > 1.f) {
radius:radius rx *= sqrt(delta);
startAngle:radians_to_degrees(startAngle) ry *= sqrt(delta);
endAngle:radians_to_degrees(startAngle + angleDelta) rx_2 = rx * rx;
clockwise:!sweepFlag]; ry_2 = ry * ry;
}
trans = NSAffineTransform.transform; CGFloat sign = (largeArcFlag == sweepFlag) ? -1.f : 1.f;
[trans scaleXBy:scale.x yBy:scale.y]; CGFloat numerator = MAX(0.f, rx_2 * ry_2 - rx_2 * yp_2 - ry_2 * xp_2);
[path.path transformUsingAffineTransform:trans]; CGFloat denom = rx_2 * yp_2 + ry_2 * xp_2;
CGFloat lhs = denom == 0.f ? 0.f : sign * sqrt(numerator / denom);
trans = NSAffineTransform.transform; CGFloat cxp = lhs * (rx * y1p) / ry;
[trans rotateByRadians:xAxisRotation]; CGFloat cyp = lhs * -((ry * x1p) / rx);
[path.path transformUsingAffineTransform:trans];
trans = NSAffineTransform.transform; CGFloat cx = cosPhi * cxp + -sinPhi * cyp + (x1 + x2) / 2.f;
[trans translateXBy:centerPoint.x yBy:centerPoint.y]; CGFloat cy = cxp * sinPhi + cyp * cosPhi + (y1 + y2) / 2.f;
[path.path transformUsingAffineTransform:trans];
CGAffineTransform transform = CGAffineTransformMakeScale(1.f / rx, 1.f / ry);
transform = CGAffineTransformRotate(transform, -xAxisRotation);
transform = CGAffineTransformTranslate(transform, -cx, -cy);
CGPoint arcPt1 = CGPointApplyAffineTransform(CGPointMake(x1, y1), transform);
CGPoint arcPt2 = CGPointApplyAffineTransform(CGPointMake(x2, y2), transform);
CGFloat startAngle = atan2(arcPt1.y, arcPt1.x);
CGFloat endAngle = atan2(arcPt2.y, arcPt2.x);
CGFloat angleDelta = endAngle - startAngle;;
if (sweepFlag == YES) {
if (angleDelta < 0.f) {
angleDelta += 2.f * M_PI;
}
} else if (angleDelta > 0.f) {
angleDelta = angleDelta - 2.f * M_PI;
}
transform = CGAffineTransformMakeTranslation(cx, cy);
transform = CGAffineTransformRotate(transform, xAxisRotation);
transform = CGAffineTransformScale(transform, rx, ry);
CGPathAddRelativeArc(path.path, &transform, 0.f, 0.f, 1.f,
startAngle, angleDelta);
} }
@end @end
@@ -23,10 +23,12 @@
path:(IJSVGPath*)path path:(IJSVGPath*)path
{ {
if (type == kIJSVGCommandTypeAbsolute) { if (type == kIJSVGCommandTypeAbsolute) {
[path.path lineToPoint:NSMakePoint(params[0], path.currentPoint.y)]; CGPathAddLineToPoint(path.path, NULL, params[0], path.currentPoint.y);
return; return;
} }
[path.path relativeLineToPoint:NSMakePoint(params[0], 0.f)]; CGPoint currentPoint = path.currentPoint;
CGPathAddLineToPoint(path.path, NULL, currentPoint.x + params[0],
path.currentPoint.y);
} }
@end @end
@@ -23,12 +23,12 @@
path:(IJSVGPath*)path path:(IJSVGPath*)path
{ {
if (type == kIJSVGCommandTypeAbsolute) { if (type == kIJSVGCommandTypeAbsolute) {
[path.path lineToPoint:NSMakePoint(params[0], params[1])]; CGPathAddLineToPoint(path.path, NULL, params[0], params[1]);
return; return;
} }
NSPoint point = NSMakePoint(path.currentPoint.x + params[0], CGPoint currentPoint = path.currentPoint;
path.currentPoint.y + params[1]); CGPathAddLineToPoint(path.path, NULL, currentPoint.x + params[0],
[path.path lineToPoint:point]; currentPoint.y + params[1]);
} }
@end @end
@@ -35,17 +35,20 @@
return; return;
} }
// actual move to command // actual move to command - do a moveToPoint only
if (type == kIJSVGCommandTypeAbsolute) { // if the type is absolute, or its possible the type is
[path.path moveToPoint:NSMakePoint(params[0], params[1])]; // 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) {
CGPathMoveToPoint(path.path, NULL,
params[0], params[1]);
return; return;
} }
@try { CGPoint currentPoint = path.currentPoint;
[path.path relativeMoveToPoint:NSMakePoint(params[0], params[1])]; CGPathMoveToPoint(path.path, NULL,
} currentPoint.x + params[0],
@catch (NSException* exception) { currentPoint.y + params[1]);
[path.path moveToPoint:NSMakePoint(params[0], params[1])];
}
} }
@end @end
@@ -24,12 +24,14 @@
path:(IJSVGPath*)path path:(IJSVGPath*)path
{ {
if (type == kIJSVGCommandTypeAbsolute) { if (type == kIJSVGCommandTypeAbsolute) {
[path.path addQuadCurveToPoint:NSMakePoint(params[2], params[3]) CGPathAddQuadCurveToPoint(path.path, NULL, params[0], params[1],
controlPoint:NSMakePoint(params[0], params[1])]; params[2], params[3]);
return; return;
} }
[path.path addQuadCurveToPoint:NSMakePoint(path.currentPoint.x + params[2], path.currentPoint.y + params[3]) CGPoint currentPoint = path.currentPoint;
controlPoint:NSMakePoint(path.currentPoint.x + params[0], path.currentPoint.y + params[1])]; CGPathAddQuadCurveToPoint(path.path, NULL,
currentPoint.x + params[0], currentPoint.y + params[1],
currentPoint.x + params[2], currentPoint.y + params[3]);
} }
@end @end
@@ -24,41 +24,41 @@
type:(IJSVGCommandType)type type:(IJSVGCommandType)type
path:(IJSVGPath*)path path:(IJSVGPath*)path
{ {
NSPoint firstControl = NSMakePoint(path.currentPoint.x, path.currentPoint.y); CGPoint currentPoint = path.currentPoint;
CGPoint firstControl = CGPointMake(currentPoint.x, currentPoint.y);
if (command != nil) { if (command != nil) {
if (command.class == [IJSVGCommandCurve class] || command.class == self.class) { if (command.class == [IJSVGCommandCurve class] || command.class == self.class) {
if (command.class == [IJSVGCommandCurve class]) { if (command.class == [IJSVGCommandCurve class]) {
if (command.type == kIJSVGCommandTypeAbsolute) { if (command.type == kIJSVGCommandTypeAbsolute) {
firstControl = NSMakePoint(-1 * command.parameters[2] + 2 * path.currentPoint.x, firstControl = CGPointMake(-1 * command.parameters[2] + 2 * currentPoint.x,
-1 * command.parameters[3] + 2 * path.currentPoint.y); -1 * command.parameters[3] + 2 * currentPoint.y);
} else { } else {
NSPoint oldPoint = NSMakePoint(path.currentPoint.x - command.parameters[4], NSPoint oldPoint = CGPointMake(currentPoint.x - command.parameters[4],
path.currentPoint.y - command.parameters[5]); currentPoint.y - command.parameters[5]);
firstControl = NSMakePoint(-1 * (command.parameters[2] + oldPoint.x) + 2 * path.currentPoint.x, firstControl = CGPointMake(-1 * (command.parameters[2] + oldPoint.x) + 2 * currentPoint.x,
-1 * (command.parameters[3] + oldPoint.y) + 2 * path.currentPoint.y); -1 * (command.parameters[3] + oldPoint.y) + 2 * currentPoint.y);
} }
} else { } else {
if (command.type == kIJSVGCommandTypeAbsolute) { if (command.type == kIJSVGCommandTypeAbsolute) {
firstControl = NSMakePoint(-1 * command.parameters[0] + 2 * path.currentPoint.x, firstControl = CGPointMake(-1 * command.parameters[0] + 2 * currentPoint.x,
-1 * command.parameters[1] + 2 * path.currentPoint.y); -1 * command.parameters[1] + 2 * currentPoint.y);
} else { } else {
NSPoint oldPoint = NSMakePoint(path.currentPoint.x - command.parameters[2], NSPoint oldPoint = CGPointMake(currentPoint.x - command.parameters[2],
path.currentPoint.y - command.parameters[3]); currentPoint.y - command.parameters[3]);
firstControl = NSMakePoint(-1 * (command.parameters[0] + oldPoint.x) + 2 * path.currentPoint.x, firstControl = CGPointMake(-1 * (command.parameters[0] + oldPoint.x) + 2 * currentPoint.x,
-1 * (command.parameters[1] + oldPoint.y) + 2 * path.currentPoint.y); -1 * (command.parameters[1] + oldPoint.y) + 2 * currentPoint.y);
} }
} }
} }
} }
if (type == kIJSVGCommandTypeAbsolute) { if (type == kIJSVGCommandTypeAbsolute) {
[path.path curveToPoint:NSMakePoint(params[2], params[3]) CGPathAddCurveToPoint(path.path, NULL, firstControl.x, firstControl.y,
controlPoint1:NSMakePoint(firstControl.x, firstControl.y) params[0], params[1], params[2], params[3]);
controlPoint2:NSMakePoint(params[0], params[1])];
return; return;
} }
[path.path curveToPoint:NSMakePoint(path.currentPoint.x + params[2], path.currentPoint.y + params[3]) CGPathAddCurveToPoint(path.path, NULL, firstControl.x, firstControl.y,
controlPoint1:NSMakePoint(firstControl.x, firstControl.y) currentPoint.x + params[0], currentPoint.y + params[1],
controlPoint2:NSMakePoint(path.currentPoint.x + params[0], path.currentPoint.y + params[1])]; currentPoint.x + params[2], currentPoint.y + params[3]);
} }
@end @end
@@ -24,33 +24,34 @@
type:(IJSVGCommandType)type type:(IJSVGCommandType)type
path:(IJSVGPath*)path path:(IJSVGPath*)path
{ {
NSPoint commandPoint = NSMakePoint(path.currentPoint.x, path.currentPoint.y); CGPoint currentPoint = path.currentPoint;
CGPoint commandPoint = CGPointMake(currentPoint.x, currentPoint.y);
if (command != nil) { if (command != nil) {
if (command.class == IJSVGCommandQuadraticCurve.class) { if (command.class == IJSVGCommandQuadraticCurve.class) {
// quadratic curve // quadratic curve
if (command.type == kIJSVGCommandTypeAbsolute) { if (command.type == kIJSVGCommandTypeAbsolute) {
commandPoint = NSMakePoint(-1 * command.parameters[0] + 2 * path.currentPoint.x, commandPoint = NSMakePoint(-1 * command.parameters[0] + 2 * currentPoint.x,
-1 * command.parameters[1] + 2 * path.currentPoint.y); -1 * command.parameters[1] + 2 * currentPoint.y);
} else { } else {
NSPoint oldPoint = CGPointMake(path.currentPoint.x - command.parameters[2], CGPoint oldPoint = CGPointMake(currentPoint.x - command.parameters[2],
path.currentPoint.y - command.parameters[3]); currentPoint.y - command.parameters[3]);
commandPoint = CGPointMake(-1 * (command.parameters[0] + oldPoint.x) + 2 * (path.currentPoint.x), commandPoint = CGPointMake(-1 * (command.parameters[0] + oldPoint.x) + 2 * (currentPoint.x),
-1 * (command.parameters[1] + oldPoint.y) + 2 * path.currentPoint.y); -1 * (command.parameters[1] + oldPoint.y) + 2 * currentPoint.y);
} }
} else if (command.class == self.class) { } else if (command.class == self.class) {
// smooth quadratic curve // smooth quadratic curve
commandPoint = CGPointMake(-1 * (path.lastControlPoint.x) + 2 * (path.currentPoint.x), commandPoint = CGPointMake(-1 * (path.lastControlPoint.x) + 2 * (currentPoint.x),
-1 * (path.lastControlPoint.y) + 2 * path.currentPoint.y); -1 * (path.lastControlPoint.y) + 2 * currentPoint.y);
} }
} }
path.lastControlPoint = commandPoint; path.lastControlPoint = commandPoint;
if (type == kIJSVGCommandTypeAbsolute) { if (type == kIJSVGCommandTypeAbsolute) {
[path.path addQuadCurveToPoint:NSMakePoint(params[0], params[1]) CGPathAddQuadCurveToPoint(path.path, NULL, commandPoint.x, commandPoint.y,
controlPoint:commandPoint]; params[0], params[1]);
return; return;
} }
[path.path addQuadCurveToPoint:NSMakePoint(path.currentPoint.x + params[0], path.currentPoint.y + params[1]) CGPathAddQuadCurveToPoint(path.path, NULL, commandPoint.x, commandPoint.y,
controlPoint:commandPoint]; currentPoint.x + params[0], currentPoint.y + params[1]);
} }
@end @end
@@ -23,10 +23,11 @@
path:(IJSVGPath*)path path:(IJSVGPath*)path
{ {
if (type == kIJSVGCommandTypeAbsolute) { if (type == kIJSVGCommandTypeAbsolute) {
[path.path lineToPoint:NSMakePoint(path.currentPoint.x, params[0])]; CGPathAddLineToPoint(path.path, NULL, path.currentPoint.x, params[0]);
return; return;
} }
[path.path relativeLineToPoint:NSMakePoint(0.f, params[0])]; CGPoint currentPoint = path.currentPoint;
CGPathAddLineToPoint(path.path, NULL, currentPoint.x, currentPoint.y + params[0]);
} }
@end @end
+41 -14
View File
@@ -6,21 +6,26 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved. // Copyright (c) 2014 Curtis Hard. All rights reserved.
// //
#import "IJSVGBezierPathAdditions.h" #import <IJSVG/IJSVGColorList.h>
#import "IJSVGColorList.h" #import <IJSVG/IJSVGExporter.h>
#import "IJSVGExporter.h" #import <IJSVG/IJSVGGradientLayer.h>
#import "IJSVGGradientLayer.h" #import <IJSVG/IJSVGGroupLayer.h>
#import "IJSVGGroupLayer.h" #import <IJSVG/IJSVGImageLayer.h>
#import "IJSVGImageLayer.h" #import <IJSVG/IJSVGLayerTree.h>
#import "IJSVGLayerTree.h" #import <IJSVG/IJSVGParser.h>
#import "IJSVGParser.h" #import <IJSVG/IJSVGRendering.h>
#import "IJSVGRendering.h" #import <IJSVG/IJSVGRenderingStyle.h>
#import "IJSVGRenderingStyle.h" #import <IJSVG/IJSVGTransaction.h>
#import "IJSVGTransaction.h"
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@class IJSVG; @class IJSVG;
typedef NS_OPTIONS(NSInteger, IJSVGMatchPropertiesMask) {
IJSVGMatchPropertyNone = 0,
IJSVGMatchPropertyContainsMaskedElement = 1 << 0,
IJSVGMatchPropertyContainsStrokedElement = 1 << 1
};
@protocol IJSVGDelegate <NSObject, IJSVGParserDelegate> @protocol IJSVGDelegate <NSObject, IJSVGParserDelegate>
@optional @optional
@@ -44,7 +49,6 @@
id<IJSVGDelegate> _delegate; id<IJSVGDelegate> _delegate;
IJSVGLayer* _layerTree; IJSVGLayer* _layerTree;
CGRect _viewBox; CGRect _viewBox;
CGSize _proposedViewSize;
CGFloat _backingScaleFactor; CGFloat _backingScaleFactor;
CGFloat _lastProposedBackingScale; CGFloat _lastProposedBackingScale;
IJSVGRenderQuality _lastProposedRenderQuality; IJSVGRenderQuality _lastProposedRenderQuality;
@@ -68,7 +72,10 @@
// fillColor, strokeColor, pattern and gradient fill // fillColor, strokeColor, pattern and gradient fill
@property (nonatomic, assign) IJSVGRenderQuality renderQuality; @property (nonatomic, assign) IJSVGRenderQuality renderQuality;
@property (nonatomic, assign) BOOL clipToViewport; @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; - (void)prepForDrawingInView:(NSView*)view;
- (BOOL)isFont; - (BOOL)isFont;
@@ -80,6 +87,8 @@
- (IJSVGLayer*)layerWithTree:(IJSVGLayerTree*)tree; - (IJSVGLayer*)layerWithTree:(IJSVGLayerTree*)tree;
- (NSArray<IJSVG*>*)subSVGs:(BOOL)recursive; - (NSArray<IJSVG*>*)subSVGs:(BOOL)recursive;
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options; - (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options;
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (CGFloat)computeBackingScale:(CGFloat)scale; - (CGFloat)computeBackingScale:(CGFloat)scale;
- (void)discardDOM; - (void)discardDOM;
@@ -101,6 +110,10 @@
- (id)initWithSVGString:(NSString*)string - (id)initWithSVGString:(NSString*)string
error:(NSError**)error; error:(NSError**)error;
- (id)initWithSVGData:(NSData*)data;
- (id)initWithSVGData:(NSData*)data
error:(NSError**)error;
- (id)initWithFile:(NSString*)file; - (id)initWithFile:(NSString*)file;
- (id)initWithFile:(NSString*)file - (id)initWithFile:(NSString*)file
error:(NSError**)error; error:(NSError**)error;
@@ -117,11 +130,21 @@
- (id)initWithFilePathURL:(NSURL*)aURL - (id)initWithFilePathURL:(NSURL*)aURL
error:(NSError**)error error:(NSError**)error
delegate:(id<IJSVGDelegate>)delegate; delegate:(id<IJSVGDelegate>)delegate;
- (id)initWithDataAssetNamed:(NSDataAssetName)name
error:(NSError**)error;
- (id)initWithDataAssetNamed:(NSDataAssetName)name
bundle:(NSBundle*)bundle
error:(NSError**)error;
- (NSImage*)imageWithSize:(NSSize)aSize; - (NSImage*)imageWithSize:(NSSize)aSize;
- (NSImage*)imageWithSize:(NSSize)aSize - (NSImage*)imageWithSize:(NSSize)aSize
error:(NSError**)error; error:(NSError**)error;
- (NSImage*)imageWithSize:(NSSize)aSize - (NSImage*)imageWithSize:(NSSize)aSize
flipped:(BOOL)flipped; flipped:(BOOL)flipped;
- (NSImage*)imageWithSize:(NSSize)aSize
flipped:(BOOL)flipped
error:(NSError**)error;
- (NSImage*)imageByMaintainingAspectRatioWithSize:(NSSize)aSize - (NSImage*)imageByMaintainingAspectRatioWithSize:(NSSize)aSize
flipped:(BOOL)flipped flipped:(BOOL)flipped
error:(NSError**)error; error:(NSError**)error;
@@ -153,5 +176,9 @@
- (void)setNeedsDisplay; - (void)setNeedsDisplay;
// colors // colors
- (IJSVGColorList*)computedColorList:(BOOL*)hasPatternFills; - (IJSVGColorList*)colorList;
- (void)performBlock:(dispatch_block_t)block;
// matching
- (BOOL)matchesPropertiesWithMask:(IJSVGMatchPropertiesMask)mask;
@end @end
+267 -87
View File
@@ -12,23 +12,28 @@
@implementation IJSVG @implementation IJSVG
@synthesize renderingBackingScaleHelper; // these are explicitly implemented
@synthesize clipToViewport; @synthesize title = _title;
@synthesize renderQuality; @synthesize desc = _desc;
@synthesize style = _style;
- (void)dealloc - (void)dealloc
{ {
// this can all be called on the background thread to be released // this can all be called on the background thread to be released
(void)([renderingBackingScaleHelper release]), BOOL hasTransaction = IJSVGBeginTransaction();
renderingBackingScaleHelper = nil; (void)([_renderingBackingScaleHelper release]), _renderingBackingScaleHelper = nil;
(void)([_replacementColors release]), _replacementColors = nil; (void)([_replacementColors release]), _replacementColors = nil;
(void)([_style release]), _style = nil; (void)([_renderingStyle release]), _renderingStyle = nil;
(void)([_group release]), _group = nil; (void)([_group release]), _group = nil;
(void)([_intrinsicSize release]), _intrinsicSize = nil;
(void)([_title release]), _title = nil;
(void)([_desc release]), _desc = nil;
// kill any memory that has been around // kill any memory that has been around
(void)([_layerTree release]), _layerTree = nil; (void)([_layerTree release]), _layerTree = nil;
[super dealloc]; [super dealloc];
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
} }
+ (id)svgNamed:(NSString*)string + (id)svgNamed:(NSString*)string
@@ -64,12 +69,35 @@
ext = @"svg"; ext = @"svg";
} }
if ((str = [bundle pathForResource:[string stringByDeletingPathExtension] if ((str = [bundle pathForResource:[string stringByDeletingPathExtension]
ofType:ext]) ofType:ext]) != nil) {
!= nil) {
return [[[self alloc] initWithFile:str return [[[self alloc] initWithFile:str
error:error error:error
delegate:delegate] autorelease]; delegate:delegate] autorelease];
} }
// check the asset catalogues
return [[[self alloc] initWithDataAssetNamed:string
error:error] autorelease];
}
- (id)initWithDataAssetNamed:(NSDataAssetName)name
error:(NSError**)error
{
return [self initWithDataAssetNamed:name
bundle:NSBundle.mainBundle
error:error];
}
- (id)initWithDataAssetNamed:(NSDataAssetName)name
bundle:(NSBundle*)bundle
error:(NSError**)error
{
NSDataAsset* dataAsset = [[[NSDataAsset alloc] initWithName:name
bundle:bundle] autorelease];
if(dataAsset != nil) {
return [[self initWithSVGData:dataAsset.data
error:error] autorelease];
}
return nil; return nil;
} }
@@ -79,10 +107,14 @@
__block IJSVGImageLayer* imageLayer = nil; __block IJSVGImageLayer* imageLayer = nil;
// create the layers we require // create the layers we require
BOOL hasTransaction = IJSVGBeginTransaction();
layer = [[[IJSVGGroupLayer alloc] init] autorelease]; layer = [[[IJSVGGroupLayer alloc] init] autorelease];
imageLayer = imageLayer =
[[[IJSVGImageLayer alloc] initWithImage:image] autorelease]; [[[IJSVGImageLayer alloc] initWithImage:image] autorelease];
[layer addSublayer:imageLayer]; [layer addSublayer:imageLayer];
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
// return the initialized SVG // return the initialized SVG
return [self initWithSVGLayer:layer return [self initWithSVGLayer:layer
@@ -115,7 +147,7 @@
error:(NSError**)error error:(NSError**)error
delegate:(id<IJSVGDelegate>)delegate delegate:(id<IJSVGDelegate>)delegate
{ {
return [self initWithFilePathURL:[NSURL fileURLWithPath:file] return [self initWithFilePathURL:[NSURL fileURLWithPath:file isDirectory:NO]
error:error error:error
delegate:delegate]; delegate:delegate];
} }
@@ -192,6 +224,21 @@
return self; 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 - (id)initWithSVGString:(NSString*)string
{ {
return [self initWithSVGString:string return [self initWithSVGString:string
@@ -238,6 +285,15 @@
return self; return self;
} }
- (void)performBlock:(dispatch_block_t)block
{
BOOL hasTransaction = IJSVGBeginTransaction();
block();
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
}
- (void)discardDOM - (void)discardDOM
{ {
// if we discard, we can no longer create a tree, so lets create tree // if we discard, we can no longer create a tree, so lets create tree
@@ -250,24 +306,43 @@
- (void)_setupBasicInfoFromGroup - (void)_setupBasicInfoFromGroup
{ {
// store the viewbox
_viewBox = _group.viewBox; _viewBox = _group.viewBox;
_proposedViewSize = _group.proposedViewSize; _intrinsicSize = _group.intrinsicSize.retain;
} }
- (void)_setupBasicsFromAnyInitializer - (void)_setupBasicsFromAnyInitializer
{ {
self.style = [[[IJSVGRenderingStyle alloc] init] autorelease]; self.renderingStyle = [[[IJSVGRenderingStyle alloc] init] autorelease];
self.clipToViewport = YES; self.clipToViewport = YES;
self.renderQuality = kIJSVGRenderQualityFullResolution; self.renderQuality = kIJSVGRenderQualityFullResolution;
// setup low level backing scale // setup low level backing scale
_lastProposedBackingScale = 0.f; _lastProposedBackingScale = 0.f;
self.renderingBackingScaleHelper = ^CGFloat { 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 - (NSString*)identifier
{ {
return _group.identifier; return _group.identifier;
@@ -310,10 +385,19 @@
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options - (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options
{ {
IJSVGExporter* exporter = IJSVGExporter* exporter = [[[IJSVGExporter alloc] initWithSVG:self
[[[IJSVGExporter alloc] initWithSVG:self size:self.viewBox.size
size:self.viewBox.size options:options] autorelease];
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]; return [exporter SVGString];
} }
@@ -340,11 +424,19 @@
error:nil]; 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 - (NSRect)computeOriginalDrawingFrameWithSize:(NSSize)aSize
{ {
[self _beginDraw:(NSRect){ .origin = CGPointZero, .size = aSize }]; NSSize propSize = [self computeSVGSizeWithRenderSize:aSize];
return NSMakeRect(0.f, 0.f, _proposedViewSize.width * _clipScale, [self _beginDraw:(NSRect) { .origin = CGPointZero, .size = aSize }];
_proposedViewSize.height * _clipScale); return NSMakeRect(0.f, 0.f, propSize.width * _clipScale,
propSize.height * _clipScale);
} }
- (CGImageRef)newCGImageRefWithSize:(CGSize)size - (CGImageRef)newCGImageRefWithSize:(CGSize)size
@@ -353,7 +445,7 @@
{ {
// setup the drawing rect, this is used for both the intial drawing // setup the drawing rect, this is used for both the intial drawing
// and the backing scale helper block // and the backing scale helper block
NSRect rect = (CGRect){ NSRect rect = (CGRect) {
.origin = CGPointZero, .origin = CGPointZero,
.size = (CGSize)size .size = (CGSize)size
}; };
@@ -362,7 +454,7 @@
[self _beginDraw:rect]; [self _beginDraw:rect];
// make sure we setup the scale based on the backing scale factor // make sure we setup the scale based on the backing scale factor
CGFloat scale = [self backindScaleFactor:NULL]; CGFloat scale = [self backingScaleFactor:NULL];
// create the context and colorspace // create the context and colorspace
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
@@ -422,7 +514,7 @@
- (NSData*)PDFData:(NSError**)error - (NSData*)PDFData:(NSError**)error
{ {
return [self return [self
PDFDataWithRect:(NSRect){ .origin = NSZeroPoint, .size = _viewBox.size } PDFDataWithRect:(NSRect) { .origin = NSZeroPoint, .size = _viewBox.size }
error:error]; error:error];
} }
@@ -480,20 +572,24 @@
{ {
// turn on converts masks to PDF's // turn on converts masks to PDF's
// as PDF context and layer masks dont work // as PDF context and layer masks dont work
void (^block)(CALayer* layer, BOOL isMask) = ^void(CALayer* layer, BOOL isMask) { void (^block)(CALayer* layer, BOOL isMask, BOOL* stop) =
^void(CALayer* layer, BOOL isMask, BOOL* stop) {
((IJSVGLayer*)layer).convertMasksToPaths = YES; ((IJSVGLayer*)layer).convertMasksToPaths = YES;
}; };
[IJSVGLayer recursivelyWalkLayer:self.layer withBlock:block]; [IJSVGLayer recursivelyWalkLayer:self.layer
withBlock:block];
} }
- (void)_endVectorDraw - (void)_endVectorDraw
{ {
// turn of convert masks to paths as not // turn of convert masks to paths as not
// needed for generic rendering // needed for generic rendering
void (^block)(CALayer* layer, BOOL isMask) = ^void(CALayer* layer, BOOL isMask) { void (^block)(CALayer* layer, BOOL isMask, BOOL* stop) =
^void(CALayer* layer, BOOL isMask, BOOL* stop) {
((IJSVGLayer*)layer).convertMasksToPaths = NO; ((IJSVGLayer*)layer).convertMasksToPaths = NO;
}; };
[IJSVGLayer recursivelyWalkLayer:self.layer withBlock:block]; [IJSVGLayer recursivelyWalkLayer:self.layer
withBlock:block];
} }
- (void)prepForDrawingInView:(NSView*)view - (void)prepForDrawingInView:(NSView*)view
@@ -540,8 +636,14 @@
- (BOOL)drawInRect:(NSRect)rect - (BOOL)drawInRect:(NSRect)rect
error:(NSError**)error error:(NSError**)error
{ {
CGContextRef currentCGContext;
if (@available(macOS 10.10, *)) {
currentCGContext = NSGraphicsContext.currentContext.CGContext;
} else {
currentCGContext = NSGraphicsContext.currentContext.graphicsPort;
}
return [self _drawInRect:rect return [self _drawInRect:rect
context:[[NSGraphicsContext currentContext] CGContext] context:currentCGContext
error:error]; error:error];
} }
@@ -557,14 +659,22 @@
// we also need to calculate the viewport so we can clip // we also need to calculate the viewport so we can clip
// the drawing if needed // the drawing if needed
NSRect viewPort = NSZeroRect; 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( viewPort.origin.y = round(
(rect.size.height / 2 - (_proposedViewSize.height / 2) * _clipScale) + rect.origin.y); (rect.size.height / 2 - (propSize.height / 2) * _clipScale) + rect.origin.y);
viewPort.size.width = _proposedViewSize.width * _clipScale; viewPort.size.width = propSize.width * _clipScale;
viewPort.size.height = _proposedViewSize.height * _clipScale; viewPort.size.height = propSize.height * _clipScale;
// check the viewport // 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) { 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) {
*valid = NO; *valid = NO;
return NSZeroRect; return NSZeroRect;
} }
@@ -615,9 +725,7 @@
// do we need to update the backing scales on the // do we need to update the backing scales on the
// layers? // layers?
if (self.renderingBackingScaleHelper != nil) { [self backingScaleFactor:nil];
[self backindScaleFactor:nil];
}
CGInterpolationQuality quality; CGInterpolationQuality quality;
switch (self.renderQuality) { switch (self.renderQuality) {
@@ -634,7 +742,11 @@
} }
} }
CGContextSetInterpolationQuality(ref, quality); CGContextSetInterpolationQuality(ref, quality);
BOOL hasTransaction = IJSVGBeginTransaction();
[self.layer renderInContext:ref]; [self.layer renderInContext:ref];
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
} }
} @catch (NSException* exception) { } @catch (NSException* exception) {
// just catch and give back a drawing error to the caller // just catch and give back a drawing error to the caller
@@ -648,7 +760,7 @@
return (error == nil); return (error == nil);
} }
- (CGFloat)backindScaleFactor:(CGFloat* _Nullable)proposedBackingScale - (CGFloat)backingScaleFactor:(CGFloat* _Nullable)proposedBackingScale
{ {
__block CGFloat scale = 1.f; __block CGFloat scale = 1.f;
scale = (self.renderingBackingScaleHelper)(); scale = (self.renderingBackingScaleHelper)();
@@ -663,7 +775,7 @@
// dont do anything, nothing has changed, no point of iterating over // dont do anything, nothing has changed, no point of iterating over
// every layer for no reason! // every layer for no reason!
if (scale == _lastProposedBackingScale && renderQuality == _lastProposedRenderQuality) { if (scale == _lastProposedBackingScale && _renderQuality == _lastProposedRenderQuality) {
return _backingScaleFactor; return _backingScaleFactor;
} }
@@ -675,7 +787,8 @@
} }
// walk the tree // walk the tree
void (^block)(CALayer* layer, BOOL isMask) = ^void(CALayer* layer, BOOL isMask) { void (^block)(CALayer* layer, BOOL isMask, BOOL* stop) =
^void(CALayer* layer, BOOL isMask, BOOL* stop) {
IJSVGLayer* propLayer = ((IJSVGLayer*)layer); IJSVGLayer* propLayer = ((IJSVGLayer*)layer);
propLayer.renderQuality = quality; propLayer.renderQuality = quality;
if (propLayer.requiresBackingScaleHelp == YES) { if (propLayer.requiresBackingScaleHelp == YES) {
@@ -684,19 +797,27 @@
}; };
// gogogo // gogogo
BOOL hasTransaction = IJSVGBeginTransaction();
[IJSVGLayer recursivelyWalkLayer:self.layer withBlock:block]; [IJSVGLayer recursivelyWalkLayer:self.layer withBlock:block];
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
return _backingScaleFactor; return _backingScaleFactor;
} }
- (IJSVGLayer*)layerWithTree:(IJSVGLayerTree*)tree - (IJSVGLayer*)layerWithTree:(IJSVGLayerTree*)tree
{ {
// clear memory // clear memory
BOOL hasTransaction = IJSVGBeginTransaction();
if (_layerTree != nil) { if (_layerTree != nil) {
(void)([_layerTree release]), _layerTree = nil; (void)([_layerTree release]), _layerTree = nil;
} }
// force rebuild of the tree // force rebuild of the tree
_layerTree = [[tree layerForNode:_group] retain]; _layerTree = [[tree layerForNode:_group] retain];
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
return _layerTree; return _layerTree;
} }
@@ -710,16 +831,16 @@
// from this SVG object // from this SVG object
IJSVGLayerTree* renderer = [[[IJSVGLayerTree alloc] init] autorelease]; IJSVGLayerTree* renderer = [[[IJSVGLayerTree alloc] init] autorelease];
renderer.viewBox = self.viewBox; renderer.viewBox = self.viewBox;
renderer.style = self.style; renderer.style = self.renderingStyle;
// return the rendered layer // return the rendered layer
return [self layerWithTree:renderer]; return [self layerWithTree:renderer];
} }
- (void)setStyle:(IJSVGRenderingStyle*)style - (void)setRenderingStyle:(IJSVGRenderingStyle*)style
{ {
(void)([_style release]), _style = nil; (void)([_renderingStyle release]), _renderingStyle = nil;
_style = style.retain; _renderingStyle = style.retain;
} }
- (void)observeValueForKeyPath:(NSString*)keyPath - (void)observeValueForKeyPath:(NSString*)keyPath
@@ -728,7 +849,7 @@
context:(void*)context context:(void*)context
{ {
// invalidate the tree if a style is set // invalidate the tree if a style is set
if (object == _style) { if (object == _renderingStyle) {
[self invalidateLayerTree]; [self invalidateLayerTree];
} }
} }
@@ -743,53 +864,72 @@
(void)([_layerTree release]), _layerTree = nil; (void)([_layerTree release]), _layerTree = nil;
} }
- (IJSVGColorList*)computedColorList:(BOOL*)hasPatternFills - (IJSVGColorList*)colorList
{ {
IJSVGColorList* sheet = [[[IJSVGColorList alloc] init] autorelease]; IJSVGColorList* sheet = [[[IJSVGColorList alloc] init] autorelease];
void (^block)(CALayer* layer, BOOL isMask) = ^void(CALayer* layer, void (^block)(CALayer* layer, BOOL isMask, BOOL* stop) =
BOOL isMask) { ^void(CALayer* layer, BOOL isMask, BOOL* stop) {
if ([layer isKindOfClass:[IJSVGShapeLayer class]] && isMask == NO && layer.isHidden == NO) {
IJSVGShapeLayer* sLayer = (IJSVGShapeLayer*)layer; // dont do anything
NSColor* color = nil; if(([layer isKindOfClass:IJSVGShapeLayer.class] && isMask == NO &&
if (sLayer.fillColor != nil) { layer.isHidden == NO) == false) {
color = [NSColor colorWithCGColor:sLayer.fillColor]; return;
if (color.alphaComponent != 0.f) { }
[sheet addColor:color];
}
// compute
IJSVGShapeLayer* sLayer = (IJSVGShapeLayer*)layer;
NSColor* color = nil;
// fill color
if (sLayer.fillColor != nil) {
color = [NSColor colorWithCGColor:sLayer.fillColor];
color = [IJSVGColor computeColorSpace:color];
if (color.alphaComponent != 0.f) {
IJSVGColorType* type = nil;
type = [IJSVGColorType typeWithColor:color
flags:IJSVGColorTypeFlagFill];
[sheet addColor:type];
} }
if (sLayer.strokeColor != nil) { }
color = [NSColor colorWithCGColor:sLayer.strokeColor];
color = [IJSVGColor computeColorSpace:color]; // stroke color
if (color.alphaComponent != 0.f) { if (sLayer.strokeColor != nil) {
[sheet addColor:color]; color = [NSColor colorWithCGColor:sLayer.strokeColor];
} color = [IJSVGColor computeColorSpace:color];
if (color.alphaComponent != 0.f) {
IJSVGColorType* type = nil;
type = [IJSVGColorType typeWithColor:color
flags:IJSVGColorTypeFlagStroke];
[sheet addColor:type];
}
}
// check for any patterns or strokes
if (sLayer.patternFillLayer != nil || sLayer.gradientFillLayer != nil ||
sLayer.gradientStrokeLayer != nil || sLayer.patternStrokeLayer != nil) {
// add any colors from gradients
IJSVGGradientLayer* gradLayer = nil;
IJSVGGradientLayer* gradStrokeLayer = nil;
// gradient fill
if ((gradLayer = sLayer.gradientFillLayer) != nil) {
IJSVGColorList* gradSheet = gradLayer.gradient.colorList;
[sheet addColorsFromList:gradSheet];
} }
// check for any patterns // gradient stroke layers
if (sLayer.patternFillLayer != nil || sLayer.gradientFillLayer != nil || sLayer.gradientStrokeLayer != nil || sLayer.patternStrokeLayer != nil) { if ((gradStrokeLayer = sLayer.gradientStrokeLayer) != nil) {
if (hasPatternFills != nil && *hasPatternFills != YES) { IJSVGColorList* gradSheet = gradStrokeLayer.gradient.colorList;
*hasPatternFills = YES; [sheet addColorsFromList:gradSheet];
}
// add any colors from gradients
IJSVGGradientLayer* gradLayer = nil;
IJSVGGradientLayer* gradStrokeLayer = nil;
if ((gradLayer = sLayer.gradientFillLayer) != nil) {
IJSVGColorList* gradSheet = gradLayer.gradient.computedColorList;
[sheet addColorsFromList:gradSheet];
}
if ((gradStrokeLayer = sLayer.gradientStrokeLayer) != nil) {
IJSVGColorList* gradSheet = gradStrokeLayer.gradient.computedColorList;
[sheet addColorsFromList:gradSheet];
}
} }
} }
}; };
// walk // gogogo!
[IJSVGLayer recursivelyWalkLayer:self.layer withBlock:block]; [IJSVGLayer recursivelyWalkLayer:self.layer
withBlock:block];
// return the colours!
return sheet; return sheet;
} }
@@ -800,12 +940,13 @@
// to transform the paths into our viewbox // to transform the paths into our viewbox
NSSize dest = rect.size; NSSize dest = rect.size;
NSSize source = _viewBox.size; NSSize source = _viewBox.size;
_clipScale = MIN(dest.width / _proposedViewSize.width, NSSize propSize = [self computeSVGSizeWithRenderSize:rect.size];
dest.height / _proposedViewSize.height); _clipScale = MIN(dest.width / propSize.width,
dest.height / propSize.height);
// work out the actual scale based on the clip scale // work out the actual scale based on the clip scale
CGFloat w = _proposedViewSize.width * _clipScale; CGFloat w = propSize.width * _clipScale;
CGFloat h = _proposedViewSize.height * _clipScale; CGFloat h = propSize.height * _clipScale;
_scale = MIN(w / source.width, h / source.height); _scale = MIN(w / source.width, h / source.height);
} }
@@ -858,4 +999,43 @@
} }
} }
#pragma mark matching
- (BOOL)matchesPropertiesWithMask:(IJSVGMatchPropertiesMask)mask
{
__block IJSVGMatchPropertiesMask matchedMask = IJSVGMatchPropertyNone;
IJSVGNodeWalkHandler handler = ^(IJSVGNode* node, BOOL* allowChildNodes,
BOOL* stop) {
// dont compute nodes that are not designed
// to be rendered
if(node.shouldRender == NO) {
*allowChildNodes = NO;
return;
}
// check for stroke
IJSVGPath* path = (IJSVGPath*)node;
if((mask & IJSVGMatchPropertyContainsStrokedElement) != 0 &&
[node isKindOfClass:IJSVGPath.class] == YES &&
path.isStroked == YES) {
matchedMask |= IJSVGMatchPropertyContainsStrokedElement;
}
// check for mask
if((mask & IJSVGMatchPropertyContainsMaskedElement) != 0 &&
node.mask != nil) {
matchedMask |= IJSVGMatchPropertyContainsMaskedElement;
}
// simply check if masks equal, if they are, stop this loop
// and return the evaluation
if(matchedMask == mask) {
*stop = YES;
}
};
[IJSVGNode walkNodeTree:_group
handler:handler];
return matchedMask == mask;
}
@end @end
@@ -6,7 +6,7 @@
// Copyright (c) 2015 Curtis Hard. All rights reserved. // Copyright (c) 2015 Curtis Hard. All rights reserved.
// //
#import "IJSVG.h" #import <IJSVG/IJSVG.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
typedef void (^IJSVGFontConverterEnumerateBlock)(NSString* unicode, IJSVG* svg); typedef void (^IJSVGFontConverterEnumerateBlock)(NSString* unicode, IJSVG* svg);
@@ -6,7 +6,6 @@
// Copyright (c) 2015 Curtis Hard. All rights reserved. // Copyright (c) 2015 Curtis Hard. All rights reserved.
// //
#import "IJSVGBezierPathAdditions.h"
#import "IJSVGFontConverter.h" #import "IJSVGFontConverter.h"
#import "IJSVGShapeLayer.h" #import "IJSVGShapeLayer.h"
@@ -104,16 +103,15 @@
+ (IJSVG*)convertIJSVGPathToSVG:(IJSVGPath*)path + (IJSVG*)convertIJSVGPathToSVG:(IJSVGPath*)path
{ {
CGPathRef cgPath = [path.path newCGPathRef:NO]; CGPathRef flippedPath = [IJSVGUtils newFlippedCGPath:path.path];
CGPathRef flippedPath = [IJSVGUtils newFlippedCGPath:cgPath];
IJSVG* svg = [self convertPathToSVG:flippedPath]; IJSVG* svg = [self convertPathToSVG:flippedPath];
CGPathRelease(flippedPath); CGPathRelease(flippedPath);
CGPathRelease(cgPath);
return svg; return svg;
} }
+ (IJSVG*)convertPathToSVG:(CGPathRef)path + (IJSVG*)convertPathToSVG:(CGPathRef)path
{ {
IJSVGBeginTransaction();
__block IJSVG* svg = nil; __block IJSVG* svg = nil;
IJSVGGroupLayer* layer = [[[IJSVGGroupLayer alloc] init] autorelease]; IJSVGGroupLayer* layer = [[[IJSVGGroupLayer alloc] init] autorelease];
IJSVGShapeLayer* shape = [[[IJSVGShapeLayer alloc] init] autorelease]; IJSVGShapeLayer* shape = [[[IJSVGShapeLayer alloc] init] autorelease];
@@ -122,6 +120,7 @@
CGRect box = CGPathGetPathBoundingBox(path); CGRect box = CGPathGetPathBoundingBox(path);
svg = [[IJSVG alloc] initWithSVGLayer:layer svg = [[IJSVG alloc] initWithSVGLayer:layer
viewBox:box]; viewBox:box];
IJSVGEndTransaction();
return [svg autorelease]; return [svg autorelease];
} }
@@ -17,6 +17,9 @@
IJSVG* _svg; IJSVG* _svg;
} }
- (instancetype)initWithData:(NSData*)data;
@property (nonatomic, readonly) CGRect viewBox; @property (nonatomic, readonly) CGRect viewBox;
@property (nonatomic, readonly) IJSVG* SVG;
@end @end
@@ -11,8 +11,6 @@
@implementation IJSVGImageRep @implementation IJSVGImageRep
@synthesize viewBox = _viewBox;
+ (void)load + (void)load
{ {
[NSBitmapImageRep registerImageRepClass:self]; [NSBitmapImageRep registerImageRepClass:self];
@@ -25,12 +23,20 @@
+ (NSArray<NSString*>*)imageTypes + (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 + (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 + (NSArray<NSImageRep*>*)imageRepsWithData:(NSData*)data
@@ -101,4 +107,9 @@
return _svg.viewBox; return _svg.viewBox;
} }
- (IJSVG*)SVG
{
return _svg;
}
@end @end
@@ -6,7 +6,7 @@
// Copyright © 2017 Curtis Hard. All rights reserved. // Copyright © 2017 Curtis Hard. All rights reserved.
// //
#import "IJSVG.h" #import <IJSVG/IJSVG.h>
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
IB_DESIGNABLE IB_DESIGNABLE
@@ -42,7 +42,7 @@
if (imageName != nil) { if (imageName != nil) {
IJSVG* anSVG = [IJSVG svgNamed:imageName]; IJSVG* anSVG = [IJSVG svgNamed:imageName];
if (tintColor != nil) { if (tintColor != nil) {
anSVG.style.fillColor = tintColor; anSVG.renderingStyle.fillColor = tintColor;
} }
self.SVG = anSVG; self.SVG = anSVG;
} }
@@ -1,19 +0,0 @@
//
// IJSVGWriter.h
// IJSVGExample
//
// Created by Curtis Hard on 21/05/2015.
// Copyright (c) 2015 Curtis Hard. All rights reserved.
//
#import "IJSVGPath.h"
#import <Foundation/Foundation.h>
@interface IJSVGWriter : NSObject {
}
+ (NSString*)SVGDocumentStringForSVGGlyph:(IJSVGPath*)node;
+ (NSString*)SVGDocumentStringForBezierPath:(NSBezierPath*)path;
+ (NSXMLDocument*)SVGDocumentForBezierPath:(NSBezierPath*)path;
@end
@@ -1,115 +0,0 @@
//
// IJSVGWriter.m
// IJSVGExample
//
// Created by Curtis Hard on 21/05/2015.
// Copyright (c) 2015 Curtis Hard. All rights reserved.
//
#import "IJSVGWriter.h"
@implementation IJSVGWriter
+ (NSString*)SVGDocumentStringForSVGGlyph:(IJSVGPath*)node
{
NSBezierPath* path = [node path];
// we need to flip it
NSAffineTransform* trans = NSAffineTransform.transform;
[trans scaleXBy:1.f yBy:-1.f];
[trans translateXBy:0.f yBy:path.controlPointBounds.size.height];
path = [trans transformBezierPath:path];
return [self.class SVGDocumentStringForBezierPath:path];
}
+ (NSString*)SVGDocumentStringForBezierPath:(NSBezierPath*)path
{
return [self.class SVGDocumentForBezierPath:path].XMLString;
}
+ (NSXMLDocument*)SVGDocumentForBezierPath:(NSBezierPath*)path
{
NSXMLElement* root = [self.class rootElementForPath:path];
// create the path data
NSXMLElement* p = [[[NSXMLElement alloc] initWithName:@"path"] autorelease];
// add the drawing command
NSXMLNode* n = [[[NSXMLNode alloc] initWithKind:NSXMLAttributeKind] autorelease];
[n setName:@"d"];
[n setStringValue:[self.class SVGPathStringForBezierPath:path]];
[p addAttribute:n];
// add the drawing path to the root
[root addChild:p];
return [[[NSXMLDocument alloc] initWithRootElement:root] autorelease];
}
+ (NSXMLElement*)rootElementForPath:(NSBezierPath*)path
{
NSXMLElement* element = [[[NSXMLElement alloc] initWithName:@"svg"] autorelease];
NSRect bounds = path.controlPointBounds;
// width
NSXMLNode* att = [[[NSXMLNode alloc] initWithKind:NSXMLAttributeKind] autorelease];
[att setName:@"width"];
[att setStringValue:[NSString stringWithFormat:@"%f", bounds.size.width]];
[element addAttribute:att];
// height
att = [[[NSXMLNode alloc] initWithKind:NSXMLAttributeKind] autorelease];
[att setName:@"height"];
[att setStringValue:[NSString stringWithFormat:@"%f", bounds.size.height]];
[element addAttribute:att];
// viewbox
att = [[[NSXMLNode alloc] initWithKind:NSXMLAttributeKind] autorelease];
[att setName:@"viewBox"];
[att setStringValue:[NSString stringWithFormat:@"%f %f %f %f", bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height]];
[element addAttribute:att];
// namespace
att = [[[NSXMLNode alloc] initWithKind:NSXMLAttributeKind] autorelease];
[att setName:@"xmlns"];
[att setStringValue:@"http://www.w3.org/2000/svg"];
[element addAttribute:att];
return element;
}
+ (NSString*)SVGPathStringForBezierPath:(NSBezierPath*)path
{
NSMutableString* str = [[[NSMutableString alloc] init] autorelease];
for (NSInteger i = 0; i < path.elementCount; i++) {
NSBezierPathElement element = [path elementAtIndex:i];
switch (element) {
// move
case NSMoveToBezierPathElement: {
NSPoint points[1];
[path elementAtIndex:i associatedPoints:points];
[str appendFormat:@"M%f %f", points[0].x, points[0].y];
break;
}
// line
case NSLineToBezierPathElement: {
NSPoint points[1];
[path elementAtIndex:i associatedPoints:points];
[str appendFormat:@"L%f %f", points[0].x, points[0].y];
break;
}
// curve
case NSCurveToBezierPathElement: {
NSPoint points[3];
[path elementAtIndex:i associatedPoints:points];
[str appendFormat:@"C%f %f %f %f %f %f", points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y];
break;
}
// close
case NSClosePathBezierPathElement: {
[str appendFormat:@"Z"];
break;
}
}
}
return str;
}
@end
@@ -7,8 +7,15 @@
// //
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <IJSVG/IJSVGUtils.h>
#import <IJSVG/IJSVGColorType.h>
@class IJSVG; @class IJSVG;
@class IJSVGExporter;
@class IJSVGLayer;
@class IJSVGNode;
NS_ASSUME_NONNULL_BEGIN
typedef void (^IJSVGCGPathHandler)(const CGPathElement* pathElement); typedef void (^IJSVGCGPathHandler)(const CGPathElement* pathElement);
typedef void (^IJSVGPathElementEnumerationBlock)(const CGPathElement* pathElement, CGPoint currentPoint); typedef void (^IJSVGPathElementEnumerationBlock)(const CGPathElement* pathElement, CGPoint currentPoint);
@@ -33,12 +40,43 @@ typedef NS_OPTIONS(NSInteger, IJSVGExporterOptions) {
IJSVGExporterOptionColorAllowRRGGBBAA = 1 << 14, IJSVGExporterOptionColorAllowRRGGBBAA = 1 << 14,
IJSVGExporterOptionRemoveComments = 1 << 15, IJSVGExporterOptionRemoveComments = 1 << 15,
IJSVGExporterOptionCenterWithinViewBox = 1 << 16, IJSVGExporterOptionCenterWithinViewBox = 1 << 16,
IJSVGExporterOptionAll = IJSVGExporterOptionRemoveUselessDef | IJSVGExporterOptionRemoveUselessGroups | IJSVGExporterOptionCreateUseForPaths | IJSVGExporterOptionMoveAttributesToGroup | IJSVGExporterOptionSortAttributes | IJSVGExporterOptionCollapseGroups | IJSVGExporterOptionCleanupPaths | IJSVGExporterOptionRemoveHiddenElements | IJSVGExporterOptionScaleToSizeIfNecessary | IJSVGExporterOptionCompressOutput | IJSVGExporterOptionCollapseGradients | IJSVGExporterOptionRemoveWidthHeightAttributes | IJSVGExporterOptionColorAllowRRGGBBAA | IJSVGExporterOptionRemoveComments | IJSVGExporterOptionCenterWithinViewBox 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); BOOL IJSVGExporterHasOption(IJSVGExporterOptions options, NSInteger option);
void IJSVGEnumerateCGPathElements(CGPathRef path, IJSVGPathElementEnumerationBlock enumBlock); void IJSVGEnumerateCGPathElements(CGPathRef path, IJSVGPathElementEnumerationBlock enumBlock);
const NSArray* IJSVGShortCharacterArray(void); const NSArray<NSString*>* IJSVGShortCharacterArray(void);
const NSDictionary<NSString*, NSString*>* IJSVGDefaultAttributes(void);
@protocol IJSVGExporterDelegate <NSObject>
@optional
- (NSString* _Nullable)svgExporter:(IJSVGExporter*)exporter
identifierForElement:(NSXMLElement* _Nullable)element
type:(IJSVGNodeType)type
defaultID:(NSString* (^)(void))defaultID;
- (NSString* _Nullable)svgExporter:(IJSVGExporter*)exporter
stringForColor:(NSColor*)color
flags:(IJSVGColorTypeFlags)flag
options:(IJSVGColorStringOptions)options;
@end
@interface IJSVGExporter : NSObject { @interface IJSVGExporter : NSObject {
@@ -50,15 +88,31 @@ const NSArray* IJSVGShortCharacterArray(void);
NSXMLElement* _defElement; NSXMLElement* _defElement;
NSInteger _idCount; NSInteger _idCount;
NSInteger _shortIdCount; NSInteger _shortIdCount;
BOOL _appliedXLink;
struct {
unsigned int identifierForElement: 1;
unsigned int stringForColor: 1;
} _respondsTo;
} }
@property (nonatomic, copy) NSString* title; @property (nonatomic, assign) id<IJSVGExporterDelegate> delegate;
@property (nonatomic, copy) NSString* description; @property (nonatomic, assign) IJSVGFloatingPointOptions floatingPointOptions;
@property (nonatomic, copy, nullable) NSString* title;
@property (nonatomic, copy, nullable) NSString* desc;
- (id)initWithSVG:(IJSVG*)svg - (id)initWithSVG:(IJSVG*)svg
size:(CGSize)size size:(CGSize)size
options:(IJSVGExporterOptions)options; options:(IJSVGExporterOptions)options;
- (id)initWithSVG:(IJSVG*)svg
size:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (NSString*)SVGString; - (NSString*)SVGString;
- (NSData*)SVGData; - (NSData*)SVGData;
- (IJSVG*)SVG:(NSError**)error;
@end @end
NS_ASSUME_NONNULL_END
File diff suppressed because it is too large Load Diff
@@ -15,30 +15,53 @@ typedef struct {
NSArray<NSString*>* params; NSArray<NSString*>* params;
} IJSVGExporterPathInstructionCommand; } IJSVGExporterPathInstructionCommand;
typedef struct {
CGPoint center;
CGFloat radius;
} IJSVGExporterPathInstructionCircle;
@interface IJSVGExporterPathInstruction : NSObject { @interface IJSVGExporterPathInstruction : NSObject {
@private @private
NSInteger _dataCount; NSInteger _dataCount;
char _instruction;
CGFloat* _data; CGFloat* _data;
CGFloat* _base;
CGFloat* _coords;
} }
@property (nonatomic, assign) char instruction;
void IJSVGExporterPathInstructionRoundData(CGFloat* data, NSInteger length, IJSVGFloatingPointOptions options);
CGFloat IJSVGExporterPathFloatToFixed(CGFloat number, int precision);
IJSVGExporterPathInstructionCommand* IJSVGExporterPathInstructionCommandCopy(IJSVGExporterPathInstructionCommand command); IJSVGExporterPathInstructionCommand* IJSVGExporterPathInstructionCommandCopy(IJSVGExporterPathInstructionCommand command);
void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand* _Nullable command); void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand* _Nullable command);
+ (NSArray<IJSVGExporterPathInstruction*>*)instructionsFromPath:(CGPathRef)path; + (NSArray<IJSVGExporterPathInstruction*>*)instructionsFromPath:(CGPathRef)path
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (id)initWithInstruction:(char)instruction - (id)initWithInstruction:(char)instruction
dataCount:(NSInteger)floatCount; dataCount:(NSInteger)floatCount;
- (void)setInstruction:(char)newInstruction;
- (char)instruction;
- (CGFloat*)data; - (CGFloat*)data;
- (NSInteger)dataLength; - (NSInteger)dataLength;
+ (void)convertInstructionsToRelativeCoordinates:(NSArray<IJSVGExporterPathInstruction*>*)instructions; + (NSArray<IJSVGExporterPathInstruction*>*)convertInstructionsCurves:(NSArray<IJSVGExporterPathInstruction*>*)instructions
+ (NSString*)pathStringFromInstructions:(NSArray<IJSVGExporterPathInstruction*>*)instructions; floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (NSString*)pathStringWithInstructionSet:(NSArray<NSValue*>*)instructionSets; + (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 @end
NS_ASSUME_NONNULL_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)
@@ -9,13 +9,22 @@
#import "IJSVGExporter.h" #import "IJSVGExporter.h"
#import "IJSVGExporterPathInstruction.h" #import "IJSVGExporterPathInstruction.h"
#import "IJSVGUtils.h" #import "IJSVGUtils.h"
#import <math.h>
@implementation IJSVGExporterPathInstruction @implementation IJSVGExporterPathInstruction
@synthesize instruction = _instruction;
- (void)dealloc - (void)dealloc
{ {
if (_data != NULL) { 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]; [super dealloc];
} }
@@ -28,8 +37,13 @@
// only allocate if not zero // only allocate if not zero
if (floatCount != 0) { if (floatCount != 0) {
_dataCount = floatCount;
_data = (CGFloat*)calloc(sizeof(CGFloat), 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; return self;
} }
@@ -39,21 +53,21 @@
return _dataCount; return _dataCount;
} }
- (void)setInstruction:(char)newInstruction
{
_instruction = newInstruction;
}
- (char)instruction
{
return _instruction;
}
- (CGFloat*)data - (CGFloat*)data
{ {
return _data; return _data;
} }
- (CGFloat*)base
{
return _base;
}
- (CGFloat*)coords
{
return _coords;
}
IJSVGExporterPathInstructionCommand* IJSVGExporterPathInstructionCommandCopy(IJSVGExporterPathInstructionCommand command) IJSVGExporterPathInstructionCommand* IJSVGExporterPathInstructionCommandCopy(IJSVGExporterPathInstructionCommand command)
{ {
IJSVGExporterPathInstructionCommand* copy = NULL; IJSVGExporterPathInstructionCommand* copy = NULL;
@@ -71,10 +85,10 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
} }
+ (NSString*)pathStringWithInstructionSet:(NSArray<NSValue*>*)instructionSets + (NSString*)pathStringWithInstructionSet:(NSArray<NSValue*>*)instructionSets
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{ {
IJSVGExporterPathInstructionCommand* lastCommand = NULL; IJSVGExporterPathInstructionCommand* lastCommand = NULL;
NSMutableString* string = [[[NSMutableString alloc] init] autorelease]; NSMutableString* string = [[[NSMutableString alloc] init] autorelease];
char* lastCommandChars = NULL;
for (NSValue* value in instructionSets) { for (NSValue* value in instructionSets) {
// read back the bytes // read back the bytes
IJSVGExporterPathInstructionCommand command; IJSVGExporterPathInstructionCommand command;
@@ -89,31 +103,9 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
[string appendString:@" "]; [string appendString:@" "];
} }
NSInteger index = 0; // compresses the floats
for (NSString* dataString in command.params) { NSString* compressedFloats = IJSVGCompressFloatParameterArray(command.params);
const char* chars = dataString.UTF8String; [string appendString:compressedFloats];
// 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;
}
// store last command // store last command
IJSVGExporterPathInstructionCommandFree(lastCommand); IJSVGExporterPathInstructionCommandFree(lastCommand);
@@ -125,6 +117,7 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
} }
+ (NSString*)pathStringFromInstructions:(NSArray<IJSVGExporterPathInstruction*>*)instructions + (NSString*)pathStringFromInstructions:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{ {
NSMutableArray* pathInstructions = [[[NSMutableArray alloc] init] autorelease]; NSMutableArray* pathInstructions = [[[NSMutableArray alloc] init] autorelease];
for (IJSVGExporterPathInstruction* instruction in instructions) { for (IJSVGExporterPathInstruction* instruction in instructions) {
@@ -132,11 +125,12 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
const char lowerInstruction = tolower(instruction.instruction); const char lowerInstruction = tolower(instruction.instruction);
NSArray<NSString*>* set = nil; NSArray<NSString*>* set = nil;
switch (lowerInstruction) { switch (lowerInstruction) {
case 't':
case 'm': case 'm':
case 'l': { case 'l': {
set = @[ set = @[
IJSVGShortFloatString(data[0]), IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatString(data[1]) IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions)
]; ];
break; break;
} }
@@ -144,29 +138,43 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
case 'v': case 'v':
case 'h': { case 'h': {
set = @[ set = @[
IJSVGShortFloatString(data[0]) IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions)
]; ];
break; break;
} }
case 'c': { case 'c': {
set = @[ set = @[
IJSVGShortFloatString(data[0]), IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatString(data[1]), IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions),
IJSVGShortFloatString(data[2]), IJSVGShortFloatStringWithOptions(data[2], floatingPointOptions),
IJSVGShortFloatString(data[3]), IJSVGShortFloatStringWithOptions(data[3], floatingPointOptions),
IJSVGShortFloatString(data[4]), IJSVGShortFloatStringWithOptions(data[4], floatingPointOptions),
IJSVGShortFloatString(data[5]) IJSVGShortFloatStringWithOptions(data[5], floatingPointOptions)
]; ];
break; break;
} }
case 's':
case 'q': { case 'q': {
set = @[ set = @[
IJSVGShortFloatString(data[0]), IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatString(data[1]), IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions),
IJSVGShortFloatString(data[2]), IJSVGShortFloatStringWithOptions(data[2], floatingPointOptions),
IJSVGShortFloatString(data[3]) 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; break;
} }
@@ -187,13 +195,201 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
objCType:@encode(IJSVGExporterPathInstructionCommand)]; objCType:@encode(IJSVGExporterPathInstructionCommand)];
[pathInstructions addObject:value]; [pathInstructions addObject:value];
} }
return [self pathStringWithInstructionSet:pathInstructions]; 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;
}
}
}
+ (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[2];
nInstruction.data[1] = instruction.data[3];
[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[2];
nInstruction.data[1] = instruction.data[3];
[nInstructions addObject:nInstruction];
continue;
}
}
[nInstructions addObject:instruction];
}
return nInstructions;
} }
+ (void)convertInstructionsToRelativeCoordinates:(NSArray<IJSVGExporterPathInstruction*>*)instructions + (void)convertInstructionsToRelativeCoordinates:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{ {
CGFloat point[2] = { 0, 0 }; CGFloat point[2] = { 0, 0 };
CGFloat subpathPoint[2] = { 0, 0 }; CGFloat subpathPoint[2] = { 0, 0 };
IJSVGExporterPathInstruction* baseInstruction = nil;
IJSVGExporterPathInstruction* prevInstruction = nil;
NSInteger index = 0; NSInteger index = 0;
for (IJSVGExporterPathInstruction* anInstruction in instructions) { for (IJSVGExporterPathInstruction* anInstruction in instructions) {
@@ -212,6 +408,8 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
if (instruction == 'm') { if (instruction == 'm') {
subpathPoint[0] = point[0]; subpathPoint[0] = point[0];
subpathPoint[1] = point[1]; subpathPoint[1] = point[1];
baseInstruction = anInstruction;
} }
} else if (instruction == 'h') { } else if (instruction == 'h') {
@@ -232,8 +430,8 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
subpathPoint[0] = point[0] += data[0]; subpathPoint[0] = point[0] += data[0];
subpathPoint[1] = point[1] += data[1]; subpathPoint[1] = point[1] += data[1];
baseInstruction = anInstruction;
} else if (instruction == 'L' || instruction == 'T') { } else if (instruction == 'L' || instruction == 'T') {
instruction = tolower(instruction); instruction = tolower(instruction);
data[0] -= point[0]; data[0] -= point[0];
@@ -241,9 +439,7 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
point[0] += data[0]; point[0] += data[0];
point[1] += data[1]; point[1] += data[1];
} else if (instruction == 'C') { } else if (instruction == 'C') {
instruction = 'c'; instruction = 'c';
data[0] -= point[0]; data[0] -= point[0];
@@ -255,9 +451,7 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
point[0] += data[4]; point[0] += data[4];
point[1] += data[5]; point[1] += data[5];
} else if (instruction == 'S' || instruction == 'Q') { } else if (instruction == 'S' || instruction == 'Q') {
instruction = tolower(instruction); instruction = tolower(instruction);
data[0] -= point[0]; data[0] -= point[0];
@@ -267,9 +461,7 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
point[0] += data[2]; point[0] += data[2];
point[1] += data[3]; point[1] += data[3];
} else if (instruction == 'A') { } else if (instruction == 'A') {
instruction = 'a'; instruction = 'a';
data[5] -= point[0]; data[5] -= point[0];
@@ -277,38 +469,51 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
point[0] += data[5]; point[0] += data[5];
point[1] += data[6]; point[1] += data[6];
} else if (instruction == 'H') { } else if (instruction == 'H') {
instruction = 'h'; instruction = 'h';
data[0] -= point[0]; data[0] -= point[0];
point[0] += data[0]; point[0] += data[0];
} else if (instruction == 'V') { } else if (instruction == 'V') {
instruction = 'v'; instruction = 'v';
data[0] -= point[1]; data[0] -= point[1];
point[1] += data[0]; point[1] += data[0];
} }
// reset the instruction // 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') { } 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[0] = subpathPoint[0];
point[1] = subpathPoint[1]; 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 // increment index
prevInstruction = anInstruction;
index++; index++;
} }
} }
+ (NSArray<IJSVGExporterPathInstruction*>*)instructionsFromPath:(CGPathRef)path + (NSArray<IJSVGExporterPathInstruction*>*)instructionsFromPath:(CGPathRef)path
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{ {
// keep track of the current point // keep track of the current point
@@ -378,6 +583,7 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
CGPoint controlPoint1 = pathElement->points[0]; CGPoint controlPoint1 = pathElement->points[0];
CGPoint controlPoint2 = pathElement->points[1]; CGPoint controlPoint2 = pathElement->points[1];
CGPoint point = pathElement->points[2]; CGPoint point = pathElement->points[2];
currentPoint = point; currentPoint = point;
instruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'C' instruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'C'
dataCount:6] autorelease]; dataCount:6] autorelease];
@@ -6,9 +6,9 @@
// Copyright © 2016 Curtis Hard. All rights reserved. // Copyright © 2016 Curtis Hard. All rights reserved.
// //
#import "IJSVGGradient.h" #import <IJSVG/IJSVGGradient.h>
#import "IJSVGLayer.h" #import <IJSVG/IJSVGLayer.h>
#import "IJSVGPath.h" #import <IJSVG/IJSVGPath.h>
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
@interface IJSVGGradientLayer : IJSVGLayer { @interface IJSVGGradientLayer : IJSVGLayer {
@@ -10,14 +10,9 @@
@implementation IJSVGGradientLayer @implementation IJSVGGradientLayer
@synthesize viewBox;
@synthesize gradient;
@synthesize absoluteTransform;
@synthesize objectRect;
- (void)dealloc - (void)dealloc
{ {
(void)([gradient release]), gradient = nil; (void)([_gradient release]), _gradient = nil;
[super dealloc]; [super dealloc];
} }
@@ -32,17 +27,17 @@
- (void)setGradient:(IJSVGGradient*)newGradient - (void)setGradient:(IJSVGGradient*)newGradient
{ {
if (gradient != nil) { if (_gradient != nil) {
(void)([gradient release]), gradient = nil; (void)([_gradient release]), _gradient = nil;
} }
gradient = [newGradient retain]; _gradient = [newGradient retain];
// lets check its alpha properties on the colors // lets check its alpha properties on the colors
BOOL hasAlphaChannel = NO; BOOL hasAlphaChannel = NO;
NSInteger stops = gradient.gradient.numberOfColorStops; NSInteger stops = _gradient.gradient.numberOfColorStops;
for (NSInteger i = 0; i < stops; i++) { for (NSInteger i = 0; i < stops; i++) {
NSColor* color = nil; NSColor* color = nil;
[gradient.gradient getColor:&color [_gradient.gradient getColor:&color
location:NULL location:NULL
atIndex:i]; atIndex:i];
if (color.alphaComponent != 1.f) { if (color.alphaComponent != 1.f) {
@@ -89,12 +84,12 @@
} }
// draw the gradient // draw the gradient
CGAffineTransform trans = CGAffineTransformMakeTranslation(-CGRectGetMinX(objectRect), CGAffineTransform trans = CGAffineTransformMakeTranslation(-CGRectGetMinX(_objectRect),
-CGRectGetMinY(objectRect)); -CGRectGetMinY(_objectRect));
CGAffineTransform transform = CGAffineTransformConcat(absoluteTransform, trans); CGAffineTransform transform = CGAffineTransformConcat(_absoluteTransform, trans);
CGContextSaveGState(ctx); CGContextSaveGState(ctx);
[self.gradient drawInContextRef:ctx [self.gradient drawInContextRef:ctx
objectRect:objectRect objectRect:_objectRect
absoluteTransform:transform absoluteTransform:transform
viewPort:self.viewBox]; viewPort:self.viewBox];
CGContextRestoreGState(ctx); CGContextRestoreGState(ctx);
@@ -6,8 +6,8 @@
// Copyright © 2017 Curtis Hard. All rights reserved. // Copyright © 2017 Curtis Hard. All rights reserved.
// //
#import "IJSVGLayer.h" #import <IJSVG/IJSVGLayer.h>
#import "IJSVGShapeLayer.h" #import <IJSVG/IJSVGShapeLayer.h>
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
@interface IJSVGGroupLayer : IJSVGLayer @interface IJSVGGroupLayer : IJSVGLayer
@@ -6,7 +6,7 @@
// Copyright © 2017 Curtis Hard. All rights reserved. // Copyright © 2017 Curtis Hard. All rights reserved.
// //
#import "IJSVGLayer.h" #import <IJSVG/IJSVGLayer.h>
#import <AppKit/AppKit.h> #import <AppKit/AppKit.h>
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
@@ -6,8 +6,8 @@
// Copyright © 2017 Curtis Hard. All rights reserved. // Copyright © 2017 Curtis Hard. All rights reserved.
// //
#import "IJSVGRendering.h" #import <IJSVG/IJSVGRendering.h>
#import "IJSVGTransaction.h" #import <IJSVG/IJSVGTransaction.h>
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
@class IJSVGShapeLayer; @class IJSVGShapeLayer;
@@ -36,7 +36,7 @@
+ (NSArray*)deepestSublayersOfLayer:(CALayer*)layer; + (NSArray*)deepestSublayersOfLayer:(CALayer*)layer;
+ (void)recursivelyWalkLayer:(CALayer*)layer + (void)recursivelyWalkLayer:(CALayer*)layer
withBlock:(void (^)(CALayer* layer, BOOL isMask))block; withBlock:(void (^)(CALayer* layer, BOOL isMask, BOOL* stop))block;
- (void)applySublayerMaskToContext:(CGContextRef)context - (void)applySublayerMaskToContext:(CGContextRef)context
forSublayer:(IJSVGLayer*)sublayer forSublayer:(IJSVGLayer*)sublayer
@@ -13,19 +13,8 @@
@implementation IJSVGLayer @implementation IJSVGLayer
@synthesize gradientFillLayer;
@synthesize patternFillLayer;
@synthesize gradientStrokeLayer;
@synthesize patternStrokeLayer;
@synthesize strokeLayer;
@synthesize requiresBackingScaleHelp;
@synthesize backingScaleFactor;
@synthesize blendingMode;
@synthesize convertMasksToPaths;
- (void)dealloc - (void)dealloc
{ {
self.contents = nil;
(void)([_maskingLayer release]), _maskingLayer = nil; (void)([_maskingLayer release]), _maskingLayer = nil;
[super dealloc]; [super dealloc];
} }
@@ -45,14 +34,21 @@
} }
+ (void)recursivelyWalkLayer:(CALayer*)layer + (void)recursivelyWalkLayer:(CALayer*)layer
withBlock:(void (^)(CALayer* layer, BOOL isMask))block withBlock:(void (^)(CALayer* layer, BOOL isMask, BOOL* stop))block
{ {
// call for layer and mask if there is one // call for layer and mask if there is one
block(layer, NO); BOOL stop = NO;
block(layer, NO, &stop);
if(stop == YES) {
return;
}
// do the mask too! // do the mask too!
if (layer.mask != nil) { if (layer.mask != nil) {
block(layer.mask, YES); block(layer.mask, YES, &stop);
if(stop == YES) {
return;
}
} }
// sublayers!! // sublayers!!
@@ -64,10 +60,10 @@
- (void)setBackingScaleFactor:(CGFloat)newFactor - (void)setBackingScaleFactor:(CGFloat)newFactor
{ {
if (self.backingScaleFactor == newFactor) { if (_backingScaleFactor == newFactor) {
return; return;
} }
backingScaleFactor = newFactor; _backingScaleFactor = newFactor;
self.contentsScale = newFactor; self.contentsScale = newFactor;
self.rasterizationScale = newFactor; self.rasterizationScale = newFactor;
[self setNeedsDisplay]; [self setNeedsDisplay];
@@ -75,7 +71,7 @@
- (void)_customRenderInContext:(CGContextRef)ctx - (void)_customRenderInContext:(CGContextRef)ctx
{ {
if (self.convertMasksToPaths == YES && _maskingLayer != nil) { if (_convertMasksToPaths == YES && _maskingLayer != nil) {
CGContextSaveGState(ctx); CGContextSaveGState(ctx);
[self applySublayerMaskToContext:ctx [self applySublayerMaskToContext:ctx
forSublayer:(IJSVGLayer*)self forSublayer:(IJSVGLayer*)self
@@ -89,10 +85,10 @@
- (void)setConvertMasksToPaths:(BOOL)flag - (void)setConvertMasksToPaths:(BOOL)flag
{ {
if (convertMasksToPaths == flag) { if (_convertMasksToPaths == flag) {
return; return;
} }
convertMasksToPaths = flag; _convertMasksToPaths = flag;
if (flag == YES) { if (flag == YES) {
if (_maskingLayer != nil) { if (_maskingLayer != nil) {
(void)([_maskingLayer release]), _maskingLayer = nil; (void)([_maskingLayer release]), _maskingLayer = nil;
@@ -152,9 +148,9 @@
- (void)renderInContext:(CGContextRef)ctx - (void)renderInContext:(CGContextRef)ctx
{ {
if (self.blendingMode != kCGBlendModeNormal) { if (_blendingMode != kCGBlendModeNormal) {
CGContextSaveGState(ctx); CGContextSaveGState(ctx);
CGContextSetBlendMode(ctx, self.blendingMode); CGContextSetBlendMode(ctx, _blendingMode);
[self _customRenderInContext:ctx]; [self _customRenderInContext:ctx];
CGContextRestoreGState(ctx); CGContextRestoreGState(ctx);
return; return;
@@ -10,13 +10,10 @@
@implementation IJSVGPatternLayer @implementation IJSVGPatternLayer
@synthesize pattern;
@synthesize patternNode;
- (void)dealloc - (void)dealloc
{ {
(void)([pattern release]), pattern = nil; (void)([_pattern release]), _pattern = nil;
(void)([patternNode release]), patternNode = nil; (void)([_patternNode release]), _patternNode = nil;
[super dealloc]; [super dealloc];
} }
@@ -50,8 +47,8 @@ void IJSVGPatternDrawingCallBack(void* info, CGContextRef ctx)
CGRect rect = self.bounds; CGRect rect = self.bounds;
CGPatternRef ref = CGPatternCreate((void*)self, self.bounds, CGPatternRef ref = CGPatternCreate((void*)self, self.bounds,
CGAffineTransformIdentity, CGAffineTransformIdentity,
roundf(rect.size.width * self.patternNode.width.value), roundf(rect.size.width * _patternNode.width.value),
roundf(rect.size.height * self.patternNode.height.value), roundf(rect.size.height * _patternNode.height.value),
kCGPatternTilingConstantSpacing, kCGPatternTilingConstantSpacing,
true, &callbacks); true, &callbacks);
@@ -6,8 +6,8 @@
// Copyright © 2017 Curtis Hard. All rights reserved. // Copyright © 2017 Curtis Hard. All rights reserved.
// //
#import "IJSVGLayer.h" #import <IJSVG/IJSVGLayer.h>
#import "IJSVGUtils.h" #import <IJSVG/IJSVGUtils.h>
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
@interface IJSVGShapeLayer : CAShapeLayer { @interface IJSVGShapeLayer : CAShapeLayer {
@@ -11,22 +11,8 @@
@implementation IJSVGShapeLayer @implementation IJSVGShapeLayer
@synthesize gradientFillLayer;
@synthesize patternFillLayer;
@synthesize gradientStrokeLayer;
@synthesize patternStrokeLayer;
@synthesize strokeLayer;
@synthesize requiresBackingScaleHelp;
@synthesize backingScaleFactor;
@synthesize blendingMode;
@synthesize convertMasksToPaths;
@synthesize originalPathOrigin;
@synthesize renderQuality;
@synthesize primitiveType;
- (void)dealloc - (void)dealloc
{ {
self.contents = nil;
(void)([_maskingLayer release]), _maskingLayer = nil; (void)([_maskingLayer release]), _maskingLayer = nil;
[super dealloc]; [super dealloc];
} }
@@ -36,7 +22,7 @@
if (self.backingScaleFactor == newFactor) { if (self.backingScaleFactor == newFactor) {
return; return;
} }
backingScaleFactor = newFactor; _backingScaleFactor = newFactor;
self.contentsScale = newFactor; self.contentsScale = newFactor;
self.rasterizationScale = newFactor; self.rasterizationScale = newFactor;
[self setNeedsDisplay]; [self setNeedsDisplay];
@@ -58,10 +44,10 @@
- (void)setConvertMasksToPaths:(BOOL)flag - (void)setConvertMasksToPaths:(BOOL)flag
{ {
if (convertMasksToPaths == flag) { if (_convertMasksToPaths == flag) {
return; return;
} }
convertMasksToPaths = flag; _convertMasksToPaths = flag;
if (flag == YES) { if (flag == YES) {
if (_maskingLayer != nil) { if (_maskingLayer != nil) {
(void)([_maskingLayer release]), _maskingLayer = nil; (void)([_maskingLayer release]), _maskingLayer = nil;
@@ -6,7 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved. // Copyright (c) 2014 Curtis Hard. All rights reserved.
// //
#import "IJSVGNode.h" #import <IJSVG/IJSVGNode.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface IJSVGDef : NSObject { @interface IJSVGDef : NSObject {
@@ -7,14 +7,11 @@
// //
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "IJSVGNode.h" #import <IJSVG/IJSVGNode.h>
@interface IJSVGForeignObject : IJSVGNode { @interface IJSVGForeignObject : IJSVGNode {
NSString * requiredExtension;
} }
@property ( nonatomic, copy ) NSString * requiredExtension; @property (nonatomic, copy) NSString* requiredExtension;
@end @end
@@ -10,11 +10,9 @@
@implementation IJSVGForeignObject @implementation IJSVGForeignObject
@synthesize requiredExtension;
- (void)dealloc - (void)dealloc
{ {
(void)([requiredExtension release]), requiredExtension = nil; (void)([_requiredExtension release]), _requiredExtension = nil;
[super dealloc]; [super dealloc];
} }
@@ -6,12 +6,13 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved. // Copyright (c) 2014 Curtis Hard. All rights reserved.
// //
#import "IJSVGColorList.h" #import <IJSVG/IJSVGColorList.h>
#import "IJSVGDef.h" #import <IJSVG/IJSVGDef.h>
#import "IJSVGTransform.h" #import <IJSVG/IJSVGTransform.h>
#import <IJSVG/IJSVGGroup.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface IJSVGGradient : IJSVGNode @interface IJSVGGradient : IJSVGGroup
@property (nonatomic, retain) NSGradient* gradient; @property (nonatomic, retain) NSGradient* gradient;
@property (nonatomic, assign) CGGradientRef CGGradient; @property (nonatomic, assign) CGGradientRef CGGradient;
@@ -21,8 +22,9 @@
@property (nonatomic, retain) IJSVGUnitLength* y2; @property (nonatomic, retain) IJSVGUnitLength* y2;
@property (nonatomic, retain) IJSVGColorList* colorList; @property (nonatomic, retain) IJSVGColorList* colorList;
+ (CGFloat*)computeColorStopsFromString:(NSXMLElement*)element + (CGFloat*)computeColorStops:(IJSVGGradient*)gradient
colors:(NSArray**)someColors; colors:(NSArray**)someColors;
- (CGGradientRef)CGGradient; - (CGGradientRef)CGGradient;
- (void)drawInContextRef:(CGContextRef)ctx - (void)drawInContextRef:(CGContextRef)ctx
objectRect:(NSRect)objectRect objectRect:(NSRect)objectRect
@@ -33,6 +35,7 @@
end:(CGPoint)endPoint end:(CGPoint)endPoint
context:(CGContextRef)ctx; context:(CGContextRef)ctx;
- (IJSVGColorList*)colorList;
- (IJSVGColorList*)computedColorList; - (IJSVGColorList*)computedColorList;
@end @end
@@ -7,23 +7,22 @@
// //
#import "IJSVGGradient.h" #import "IJSVGGradient.h"
#import "IJSVGParser.h"
@implementation IJSVGGradient @implementation IJSVGGradient
@synthesize gradient, CGGradient; @synthesize colorList = _privateColorList;
@synthesize x1, x2, y1, y2;
@synthesize colorList = _colorList;
- (void)dealloc - (void)dealloc
{ {
(void)([x1 release]), x1 = nil; (void)([_x1 release]), _x1 = nil;
(void)([x2 release]), x2 = nil; (void)([_x2 release]), _x2 = nil;
(void)([y1 release]), y1 = nil; (void)([_y1 release]), _y1 = nil;
(void)([y2 release]), y2 = nil; (void)([_y2 release]), _y2 = nil;
(void)([gradient release]), gradient = nil; (void)([_gradient release]), _gradient = nil;
(void)([_colorList release]), _colorList = nil; (void)([_privateColorList release]), _privateColorList = nil;
if (CGGradient != nil) { if (_CGGradient != nil) {
CGGradientRelease(CGGradient); CGGradientRelease(_CGGradient);
} }
[super dealloc]; [super dealloc];
} }
@@ -37,84 +36,41 @@
- (void)setColorList:(IJSVGColorList*)list - (void)setColorList:(IJSVGColorList*)list
{ {
(void)([_colorList release]), _colorList = nil; (void)([_privateColorList release]), _privateColorList = nil;
_colorList = list.retain; _privateColorList = list.retain;
if (CGGradient != nil) { if (_CGGradient != nil) {
CGGradientRelease(CGGradient); CGGradientRelease(_CGGradient);
_CGGradient = nil;
} }
} }
+ (CGFloat*)computeColorStopsFromString:(NSXMLElement*)element + (CGFloat *)computeColorStops:(IJSVGGradient*)gradient
colors:(NSArray**)someColors colors:(NSArray**)someColors
{ {
// find each stop element NSArray<IJSVGNode*>* stops = gradient.childNodes;
NSArray* stops = [element children];
NSMutableArray* colors = [[[NSMutableArray alloc] initWithCapacity:stops.count] autorelease]; NSMutableArray* colors = [[[NSMutableArray alloc] initWithCapacity:stops.count] autorelease];
CGFloat* stopsParams = (CGFloat*)malloc(stops.count * sizeof(CGFloat)); CGFloat* stopsParams = (CGFloat*)malloc(stops.count * sizeof(CGFloat));
NSInteger i = 0; NSInteger i = 0;
for (NSXMLElement* stop in stops) { for(IJSVGNode* stopNode in stops) {
// find the offset NSColor* color = stopNode.fillColor;
CGFloat offset = [stop attributeForName:@"offset"].stringValue.floatValue; CGFloat opacity = stopNode.fillOpacity.value;
if (offset > 1.f) { CGFloat offset = stopNode.offset.value;
offset /= 100.f;
}
stopsParams[i++] = offset; stopsParams[i++] = offset;
if(color == nil) {
// find the stop opacity color = [IJSVGColor colorFromHEXInteger:0x000000];
CGFloat stopOpacity = 1.f; if(opacity != 1.f) {
NSXMLNode* stopOpacityAttribute = [stop attributeForName:@"stop-opacity"]; color = [IJSVGColor changeAlphaOnColor:color
if (stopOpacityAttribute != nil) { to:opacity];
stopOpacity = stopOpacityAttribute.stringValue.floatValue;
}
// find the stop color
NSString* scs = [stop attributeForName:@"stop-color"].stringValue;
NSColor* stopColor = [IJSVGColor colorFromString:scs];
if (stopColor != nil && stopOpacity != 1.f) {
stopColor = [IJSVGColor changeAlphaOnColor:stopColor
to:stopOpacity];
}
// compute any style that there was...
NSXMLNode* styleAttribute = [stop attributeForName:@"style"];
if (styleAttribute != nil) {
IJSVGStyle* style = [IJSVGStyle parseStyleString:styleAttribute.stringValue];
NSColor* color = [IJSVGColor colorFromString:[style property:@"stop-color"]];
// we have a color!
if (color != nil) {
// is there a stop opacity?
NSString* numberString = nil;
if ((numberString = [style property:@"stop-opacity"]) != nil) {
color = [IJSVGColor changeAlphaOnColor:color
to:numberString.floatValue];
} else {
color = [IJSVGColor changeAlphaOnColor:color
to:stopOpacity];
}
stopColor = color;
} }
} }
[colors addObject:color];
// default is black
if (stopColor == nil) {
stopColor = [IJSVGColor colorFromString:@"black"];
if (stopOpacity != 1.f) {
stopColor = [IJSVGColor changeAlphaOnColor:stopColor
to:stopOpacity];
}
}
// add the stop color
[(NSMutableArray*)colors addObject:stopColor];
} }
*someColors = colors; *someColors = (NSArray*)colors;
return stopsParams; return stopsParams;
} }
- (IJSVGColorList*)computedColorList - (IJSVGColorList*)colorList
{ {
IJSVGColorList* sheet = [[[IJSVGColorList alloc] init] autorelease]; IJSVGColorList* sheet = [[[IJSVGColorList alloc] init] autorelease];
NSInteger num = self.gradient.numberOfColorStops; NSInteger num = self.gradient.numberOfColorStops;
@@ -123,16 +79,23 @@
[self.gradient getColor:&color [self.gradient getColor:&color
location:nil location:nil
atIndex:i]; atIndex:i];
[sheet addColor:color]; IJSVGColorType* type = [IJSVGColorType typeWithColor:color
flags:IJSVGColorTypeFlagStop];
[sheet addColor:type];
} }
return sheet; return sheet;
} }
- (IJSVGColorList*)computedColorList
{
return _privateColorList;
}
- (CGGradientRef)CGGradient - (CGGradientRef)CGGradient
{ {
// store it in the cache // store it in the cache
if (CGGradient != nil) { if (_CGGradient != nil) {
return CGGradient; return _CGGradient;
} }
// actually create the gradient // actually create the gradient
@@ -145,8 +108,8 @@
[self.gradient getColor:&color [self.gradient getColor:&color
location:&locations[i] location:&locations[i]
atIndex:i]; atIndex:i];
if (_colorList != nil) { if (_privateColorList != nil) {
color = [_colorList proposedColorForColor:color]; color = [_privateColorList proposedColorForColor:color];
} }
CFArrayAppendValue(colors, color.CGColor); CFArrayAppendValue(colors, color.CGColor);
} }
@@ -154,7 +117,7 @@
colors, locations); colors, locations);
CFRelease(colors); CFRelease(colors);
free(locations); free(locations);
return CGGradient = result; return _CGGradient = result;
} }
- (void)drawInContextRef:(CGContextRef)ctx - (void)drawInContextRef:(CGContextRef)ctx
@@ -6,17 +6,18 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved. // Copyright (c) 2014 Curtis Hard. All rights reserved.
// //
#import "IJSVGNode.h" #import <IJSVG/IJSVGNode.h>
#import "IJSVGPath.h" #import <IJSVG/IJSVGPath.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface IJSVGGroup : IJSVGNode { @interface IJSVGGroup : IJSVGNode {
NSMutableArray* children; @private
NSMutableArray<IJSVGNode*>* _childNodes;
} }
- (void)addChild:(id)child; - (void)addChild:(IJSVGNode*)child;
- (NSArray*)children; - (NSArray<IJSVGNode*>*)childNodes;
- (void)purgeChildren; - (void)purgeChildren;
@end @end
+16 -11
View File
@@ -12,21 +12,24 @@
- (void)dealloc - (void)dealloc
{ {
(void)([children release]), children = nil; (void)([_childNodes release]), _childNodes = nil;
[super dealloc]; [super dealloc];
} }
- (id)init - (id)init
{ {
if ((self = [super init]) != nil) { if ((self = [super init]) != nil) {
children = [[NSMutableArray alloc] init]; _childNodes = [[NSMutableArray alloc] init];
} }
return self; return self;
} }
- (void)prepareFromCopy - (void)prepareFromCopy
{ {
children = [[NSMutableArray alloc] init]; if(_childNodes != nil) {
(void)[_childNodes release], _childNodes = nil;
}
_childNodes = [[NSMutableArray alloc] init];
} }
- (id)copyWithZone:(NSZone*)zone - (id)copyWithZone:(NSZone*)zone
@@ -34,7 +37,7 @@
IJSVGGroup* node = [super copyWithZone:zone]; IJSVGGroup* node = [super copyWithZone:zone];
[node prepareFromCopy]; [node prepareFromCopy];
for (IJSVGNode* childNode in self.children) { for (IJSVGNode* childNode in _childNodes) {
childNode = [[childNode copy] autorelease]; childNode = [[childNode copy] autorelease];
childNode.parentNode = node; childNode.parentNode = node;
[node addChild:childNode]; [node addChild:childNode];
@@ -44,23 +47,25 @@
- (void)purgeChildren - (void)purgeChildren
{ {
[children removeAllObjects]; [_childNodes removeAllObjects];
} }
- (void)addChild:(id)child - (void)addChild:(IJSVGNode*)child
{ {
if (child != nil) if (child != nil) {
[children addObject:child]; [_childNodes addObject:child];
}
} }
- (NSArray*)children - (NSArray<IJSVGNode*>*)childNodes
{ {
return children; return _childNodes;
} }
- (NSString*)description - (NSString*)description
{ {
return [NSString stringWithFormat:@"%@ - %@", [super description], self.children]; return [NSString stringWithFormat:@"%@ - %@",
[super description], self.childNodes];
} }
@end @end
@@ -6,7 +6,7 @@
// Copyright © 2016 Curtis Hard. All rights reserved. // Copyright © 2016 Curtis Hard. All rights reserved.
// //
#import "IJSVGNode.h" #import <IJSVG/IJSVGNode.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@class IJSVGPath; @class IJSVGPath;
@@ -21,6 +21,7 @@
- (CGImageRef)CGImage; - (CGImageRef)CGImage;
- (void)drawInContextRef:(CGContextRef)context - (void)drawInContextRef:(CGContextRef)context
path:(IJSVGPath*)path; path:(IJSVGPath*)path;
- (void)loadFromBase64EncodedString:(NSString*)encodedString; - (void)loadFromString:(NSString*)encodedString;
- (void)loadFromURL:(NSURL*)aURL;
@end @end
@@ -20,7 +20,7 @@
[super dealloc]; [super dealloc];
} }
- (void)loadFromBase64EncodedString:(NSString*)encodedString - (void)loadFromString:(NSString*)encodedString
{ {
if ([encodedString hasPrefix:@"data:"]) { if ([encodedString hasPrefix:@"data:"]) {
encodedString = [encodedString stringByReplacingOccurrencesOfString:@"\\s+" encodedString = [encodedString stringByReplacingOccurrencesOfString:@"\\s+"
@@ -28,8 +28,15 @@
options:NSRegularExpressionSearch options:NSRegularExpressionSearch
range:NSMakeRange(0, encodedString.length)]; range:NSMakeRange(0, encodedString.length)];
} }
NSURL* URL = [NSURL URLWithString:encodedString]; NSURL* url = [NSURL URLWithString:encodedString];
NSData* data = [NSData dataWithContentsOfURL:URL]; if(url != nil) {
[self loadFromURL:url];
}
}
- (void)loadFromURL:(NSURL*)aURL
{
NSData* data = [NSData dataWithContentsOfURL:aURL];
// no data, just ignore...invalid probably // no data, just ignore...invalid probably
if (data == nil) { if (data == nil) {
@@ -46,7 +53,8 @@
if (imagePath == nil) { if (imagePath == nil) {
// lazy load the path as it might not be needed // lazy load the path as it might not be needed
imagePath = [[IJSVGPath alloc] init]; imagePath = [[IJSVGPath alloc] init];
[imagePath.path appendBezierPathWithRect:NSMakeRect(0.f, 0.f, self.width.value, self.height.value)]; CGRect rect = CGRectMake(0.f, 0.f, self.width.value, self.height.value);
CGPathAddRect(imagePath.path, NULL, rect);
[imagePath close]; [imagePath close];
} }
return imagePath; return imagePath;
@@ -92,7 +100,7 @@
path = [self path]; path = [self path];
} }
CGRect rect = path.path.bounds; CGRect rect = path.pathBoundingBox;
CGRect bounds = CGRectMake(0.f, 0.f, rect.size.width, rect.size.height); CGRect bounds = CGRectMake(0.f, 0.f, rect.size.width, rect.size.height);
// save the state of the context // save the state of the context
@@ -6,7 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved. // Copyright (c) 2014 Curtis Hard. All rights reserved.
// //
#import "IJSVGGradient.h" #import <IJSVG/IJSVGGradient.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface IJSVGLinearGradient : IJSVGGradient @interface IJSVGLinearGradient : IJSVGGradient
@@ -8,6 +8,7 @@
#import "IJSVGLinearGradient.h" #import "IJSVGLinearGradient.h"
#import "IJSVGUtils.h" #import "IJSVGUtils.h"
#import "IJSVGParser.h"
@implementation IJSVGLinearGradient @implementation IJSVGLinearGradient
@@ -15,10 +16,10 @@
gradient:(IJSVGLinearGradient*)aGradient gradient:(IJSVGLinearGradient*)aGradient
{ {
// just ask unit for the value // just ask unit for the value
NSString* x1 = ([element attributeForName:@"x1"].stringValue ?: @"0"); NSString* x1 = ([element attributeForName:IJSVGAttributeX1].stringValue ?: @"0");
NSString* x2 = ([element attributeForName:@"x2"].stringValue ?: @"100%"); NSString* x2 = ([element attributeForName:IJSVGAttributeX2].stringValue ?: @"100%");
NSString* y1 = ([element attributeForName:@"y1"].stringValue ?: @"0"); NSString* y1 = ([element attributeForName:IJSVGAttributeY1].stringValue ?: @"0");
NSString* y2 = ([element attributeForName:@"y2"].stringValue ?: @"0"); NSString* y2 = ([element attributeForName:IJSVGAttributeY2].stringValue ?: @"0");
aGradient.x1 = [IJSVGGradientUnitLength unitWithString:x1 fromUnitType:aGradient.units]; aGradient.x1 = [IJSVGGradientUnitLength unitWithString:x1 fromUnitType:aGradient.units];
aGradient.x2 = [IJSVGGradientUnitLength unitWithString:x2 fromUnitType:aGradient.units]; aGradient.x2 = [IJSVGGradientUnitLength unitWithString:x2 fromUnitType:aGradient.units];
aGradient.y1 = [IJSVGGradientUnitLength unitWithString:y1 fromUnitType:aGradient.units]; aGradient.y1 = [IJSVGGradientUnitLength unitWithString:y1 fromUnitType:aGradient.units];
@@ -26,8 +27,8 @@
// compute the color stops and colours // compute the color stops and colours
NSArray* colors = nil; NSArray* colors = nil;
CGFloat* stopsParams = [self.class computeColorStopsFromString:element CGFloat* stopsParams = [self.class computeColorStops:aGradient
colors:&colors]; colors:&colors];
// create the gradient with the colours // create the gradient with the colours
NSGradient* grad = [[NSGradient alloc] initWithColors:colors NSGradient* grad = [[NSGradient alloc] initWithColors:colors
+20 -2
View File
@@ -6,11 +6,12 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved. // Copyright (c) 2014 Curtis Hard. All rights reserved.
// //
#import "IJSVGStyle.h" #import <IJSVG/IJSVGStyle.h>
#import "IJSVGUnitLength.h" #import <IJSVG/IJSVGUnitLength.h>
#import <AppKit/AppKit.h> #import <AppKit/AppKit.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@class IJSVGNode;
@class IJSVG; @class IJSVG;
@class IJSVGGroup; @class IJSVGGroup;
@class IJSVGDef; @class IJSVGDef;
@@ -19,6 +20,8 @@
@class IJSVGPattern; @class IJSVGPattern;
@class IJSVGTransform; @class IJSVGTransform;
typedef void (^IJSVGNodeWalkHandler)(IJSVGNode* node, BOOL* allowChildNodes, BOOL* stop);
typedef NS_ENUM(NSInteger, IJSVGNodeType) { typedef NS_ENUM(NSInteger, IJSVGNodeType) {
IJSVGNodeTypeGroup, IJSVGNodeTypeGroup,
IJSVGNodeTypePath, IJSVGNodeTypePath,
@@ -43,6 +46,9 @@ typedef NS_ENUM(NSInteger, IJSVGNodeType) {
IJSVGNodeTypeTextSpan, IJSVGNodeTypeTextSpan,
IJSVGNodeTypeStyle, IJSVGNodeTypeStyle,
IJSVGNodeTypeSwitch, IJSVGNodeTypeSwitch,
IJSVGNodeTypeTitle,
IJSVGNodeTypeDesc,
IJSVGNodeTypeStop,
IJSVGNodeTypeNotFound, IJSVGNodeTypeNotFound,
}; };
@@ -93,10 +99,17 @@ typedef NS_ENUM(NSInteger, IJSVGBlendMode) {
IJSVGBlendModeLuminosity = kCGBlendModeLuminosity IJSVGBlendModeLuminosity = kCGBlendModeLuminosity
}; };
typedef NS_ENUM(NSInteger, IJSVGOverflowVisibility) {
IJSVGOverflowVisibilityHidden,
IJSVGOverflowVisibilityVisible
};
static CGFloat IJSVGInheritedFloatValue = -99.9999991; static CGFloat IJSVGInheritedFloatValue = -99.9999991;
@interface IJSVGNode : NSObject <NSCopying> @interface IJSVGNode : NSObject <NSCopying>
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* desc;
@property (nonatomic, assign) IJSVGNodeType type; @property (nonatomic, assign) IJSVGNodeType type;
@property (nonatomic, copy) NSString* name; @property (nonatomic, copy) NSString* name;
@property (nonatomic, copy) NSString* className; @property (nonatomic, copy) NSString* className;
@@ -112,6 +125,7 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991;
@property (nonatomic, retain) IJSVGUnitLength* fillOpacity; @property (nonatomic, retain) IJSVGUnitLength* fillOpacity;
@property (nonatomic, retain) IJSVGUnitLength* strokeOpacity; @property (nonatomic, retain) IJSVGUnitLength* strokeOpacity;
@property (nonatomic, retain) IJSVGUnitLength* strokeWidth; @property (nonatomic, retain) IJSVGUnitLength* strokeWidth;
@property (nonatomic, retain) IJSVGUnitLength* offset;
@property (nonatomic, retain) NSColor* fillColor; @property (nonatomic, retain) NSColor* fillColor;
@property (nonatomic, retain) NSColor* strokeColor; @property (nonatomic, retain) NSColor* strokeColor;
@property (nonatomic, copy) NSString* identifier; @property (nonatomic, copy) NSString* identifier;
@@ -135,6 +149,10 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991;
@property (nonatomic, assign) IJSVGUnitType contentUnits; @property (nonatomic, assign) IJSVGUnitType contentUnits;
@property (nonatomic, assign) IJSVGUnitType units; @property (nonatomic, assign) IJSVGUnitType units;
@property (nonatomic, assign) IJSVGBlendMode blendMode; @property (nonatomic, assign) IJSVGBlendMode blendMode;
@property (nonatomic, assign) IJSVGOverflowVisibility overflowVisibility;
+ (void)walkNodeTree:(IJSVGNode*)node
handler:(IJSVGNodeWalkHandler)handler;
+ (IJSVGNodeType)typeForString:(NSString*)string + (IJSVGNodeType)typeForString:(NSString*)string
kind:(NSXMLNodeKind)kind; kind:(NSXMLNodeKind)kind;
+189 -142
View File
@@ -9,136 +9,134 @@
#import "IJSVGDef.h" #import "IJSVGDef.h"
#import "IJSVGNode.h" #import "IJSVGNode.h"
#import "IJSVGUtils.h" #import "IJSVGUtils.h"
#import "IJSVGGroup.h"
@implementation IJSVGNode @implementation IJSVGNode
@synthesize shouldRender;
@synthesize type;
@synthesize name;
@synthesize classNameList;
@synthesize className;
@synthesize unicode;
@synthesize x;
@synthesize y;
@synthesize width;
@synthesize height;
@synthesize fillColor;
@synthesize fillOpacity;
@synthesize strokeColor;
@synthesize strokeOpacity;
@synthesize strokeWidth;
@synthesize opacity;
@synthesize identifier;
@synthesize parentNode;
@synthesize intermediateParentNode;
@synthesize transforms;
@synthesize windingRule;
@synthesize def;
@synthesize fillGradient;
@synthesize fillPattern;
@synthesize strokeGradient;
@synthesize strokePattern;
@synthesize clipPath;
@synthesize lineCapStyle;
@synthesize lineJoinStyle;
@synthesize strokeDashArrayCount;
@synthesize strokeDashArray;
@synthesize strokeDashOffset;
@synthesize usesDefaultFillColor;
@synthesize svg;
@synthesize mask;
@synthesize units;
@synthesize contentUnits;
@synthesize blendMode;
- (void)dealloc - (void)dealloc
{ {
free(strokeDashArray); (void)free(_strokeDashArray), _strokeDashArray = NULL;
(void)([x release]), x = nil; (void)([_x release]), _x = nil;
(void)([y release]), y = nil; (void)([_y release]), _y = nil;
(void)([width release]), width = nil; (void)([_width release]), _width = nil;
(void)([height release]), height = nil; (void)([_height release]), _height = nil;
(void)([opacity release]), opacity = nil; (void)([_opacity release]), _opacity = nil;
(void)([fillOpacity release]), fillOpacity = nil; (void)([_offset release]), _offset = nil;
(void)([strokeOpacity release]), strokeOpacity = nil; (void)([_fillOpacity release]), _fillOpacity = nil;
(void)([strokeWidth release]), strokeWidth = nil; (void)([_strokeOpacity release]), _strokeOpacity = nil;
(void)([strokeDashOffset release]), strokeDashOffset = nil; (void)([_strokeWidth release]), _strokeWidth = nil;
(void)([unicode release]), unicode = nil; (void)([_strokeDashOffset release]), _strokeDashOffset = nil;
(void)([fillGradient release]), fillGradient = nil; (void)([_unicode release]), _unicode = nil;
(void)([strokeGradient release]), strokeGradient = nil; (void)([_fillGradient release]), _fillGradient = nil;
(void)([strokePattern release]), strokePattern = nil; (void)([_strokeGradient release]), _strokeGradient = nil;
(void)([transforms release]), transforms = nil; (void)([_strokePattern release]), _strokePattern = nil;
(void)([fillColor release]), fillColor = nil; (void)([_transforms release]), _transforms = nil;
(void)([strokeColor release]), strokeColor = nil; (void)([_fillColor release]), _fillColor = nil;
(void)([identifier release]), identifier = nil; (void)([_strokeColor release]), _strokeColor = nil;
(void)([def release]), def = nil; (void)([_identifier release]), _identifier = nil;
(void)([name release]), name = nil; (void)([_def release]), _def = nil;
(void)([className release]), className = nil; (void)([_name release]), _name = nil;
(void)([classNameList release]), classNameList = nil; (void)([_title release]), _title = nil;
(void)([fillPattern release]), fillPattern = nil; (void)([_desc release]), _desc = nil;
(void)([clipPath release]), clipPath = nil; (void)([_className release]), _className = nil;
(void)([svg release]), svg = nil; (void)([_classNameList release]), _classNameList = nil;
(void)([mask release]), mask = nil; (void)([_fillPattern release]), _fillPattern = nil;
(void)([_clipPath release]), _clipPath = nil;
(void)([_svg release]), _svg = nil;
(void)([_mask release]), _mask = nil;
[super dealloc]; [super dealloc];
} }
+ (IJSVGNodeType)typeForString:(NSString*)string + (IJSVGNodeType)typeForString:(NSString*)string
kind:(NSXMLNodeKind)kind kind:(NSXMLNodeKind)kind
{ {
string = [string lowercaseString]; // possible fix for older os's that complain
if ([string isEqualToString:@"style"]) if(string == nil || kind == NSXMLCommentKind) {
return IJSVGNodeTypeNotFound;
}
const char* name = string.lowercaseString.UTF8String;
if(name == NULL) {
return IJSVGNodeTypeNotFound;
}
if (strcmp(name, "style") == 0) {
return IJSVGNodeTypeStyle; return IJSVGNodeTypeStyle;
if ([string isEqualToString:@"switch"]) }
if (strcmp(name, "switch") == 0) {
return IJSVGNodeTypeSwitch; return IJSVGNodeTypeSwitch;
if ([string isEqualToString:@"defs"]) }
if (strcmp(name, "defs") == 0) {
return IJSVGNodeTypeDef; return IJSVGNodeTypeDef;
if ([string isEqualToString:@"g"]) }
if (strcmp(name, "g") == 0) {
return IJSVGNodeTypeGroup; return IJSVGNodeTypeGroup;
if ([string isEqualToString:@"path"]) }
if (strcmp(name, "path") == 0) {
return IJSVGNodeTypePath; return IJSVGNodeTypePath;
if ([string isEqualToString:@"polygon"]) }
if (strcmp(name, "polygon") == 0) {
return IJSVGNodeTypePolygon; return IJSVGNodeTypePolygon;
if ([string isEqualToString:@"polyline"]) }
if (strcmp(name, "polyline") == 0) {
return IJSVGNodeTypePolyline; return IJSVGNodeTypePolyline;
if ([string isEqualToString:@"rect"]) }
if (strcmp(name, "rect") == 0) {
return IJSVGNodeTypeRect; return IJSVGNodeTypeRect;
if ([string isEqualToString:@"line"]) }
if (strcmp(name, "line") == 0) {
return IJSVGNodeTypeLine; return IJSVGNodeTypeLine;
if ([string isEqualToString:@"circle"]) }
if (strcmp(name, "circle") == 0) {
return IJSVGNodeTypeCircle; return IJSVGNodeTypeCircle;
if ([string isEqualToString:@"ellipse"]) }
if (strcmp(name, "ellipse") == 0) {
return IJSVGNodeTypeEllipse; return IJSVGNodeTypeEllipse;
if ([string isEqualToString:@"use"]) }
if (strcmp(name, "use") == 0) {
return IJSVGNodeTypeUse; return IJSVGNodeTypeUse;
if ([string isEqualToString:@"lineargradient"]) }
if (strcmp(name, "lineargradient") == 0) {
return IJSVGNodeTypeLinearGradient; return IJSVGNodeTypeLinearGradient;
if ([string isEqualToString:@"radialgradient"]) }
if (strcmp(name, "radialgradient") == 0) {
return IJSVGNodeTypeRadialGradient; return IJSVGNodeTypeRadialGradient;
if ([string isEqualToString:@"glyph"]) }
if(strcmp(name, "stop") == 0) {
return IJSVGNodeTypeStop;
}
if (strcmp(name, "glyph") == 0) {
return IJSVGNodeTypeGlyph; return IJSVGNodeTypeGlyph;
if ([string isEqualToString:@"font"]) }
if (strcmp(name, "font") == 0) {
return IJSVGNodeTypeFont; return IJSVGNodeTypeFont;
if ([string isEqualToString:@"clippath"]) }
if (strcmp(name, "clippath") == 0) {
return IJSVGNodeTypeClipPath; return IJSVGNodeTypeClipPath;
if ([string isEqualToString:@"mask"]) }
if (strcmp(name, "mask") == 0) {
return IJSVGNodeTypeMask; return IJSVGNodeTypeMask;
if ([string isEqualToString:@"image"]) }
if (strcmp(name, "image") == 0) {
return IJSVGNodeTypeImage; return IJSVGNodeTypeImage;
if ([string isEqualToString:@"pattern"]) }
if (strcmp(name, "pattern") == 0) {
return IJSVGNodeTypePattern; return IJSVGNodeTypePattern;
if ([string isEqualToString:@"svg"]) }
if (strcmp(name, "svg") == 0) {
return IJSVGNodeTypeSVG; return IJSVGNodeTypeSVG;
if ([string isEqualToString:@"text"]) }
if (strcmp(name, "text") == 0) {
return IJSVGNodeTypeText; return IJSVGNodeTypeText;
if ([string isEqualToString:@"tspan"] || kind == NSXMLTextKind) { }
if (strcmp(name, "tspan") == 0 || kind == NSXMLTextKind) {
return IJSVGNodeTypeTextSpan; return IJSVGNodeTypeTextSpan;
} }
if(strcmp(name, "title") == 0) {
// are we commong HTML? - if so just treat as a group return IJSVGNodeTypeTitle;
if (IJSVGIsCommonHTMLElementName(string) == YES) { }
return IJSVGNodeTypeGroup; if(strcmp(name, "desc") == 0) {
return IJSVGNodeTypeDesc;
} }
return IJSVGNodeTypeNotFound; return IJSVGNodeTypeNotFound;
} }
@@ -150,8 +148,55 @@
return self; return self;
} }
+ (void)walkNodeTree:(IJSVGNode*)node
handler:(IJSVGNodeWalkHandler)handler
{
BOOL allowChildNodes = YES;
BOOL stop = NO;
[self _walkNodeTree:node
handler:handler
allowChildNodes:&allowChildNodes
stop:&stop];
}
+ (void)_walkNodeTree:(IJSVGNode*)node
handler:(IJSVGNodeWalkHandler)handler
allowChildNodes:(BOOL*)allowChildNodes
stop:(BOOL*)stop
{
// run the handler and instantly stop
// if stop is set
handler(node, allowChildNodes, stop);
if(*stop == YES) {
return;
}
// child nodes only work for nodes
// that are type group
if(*allowChildNodes == NO ||
[node isKindOfClass:IJSVGGroup.class] == NO) {
*allowChildNodes = YES;
return;
}
// iterate over the childnodes
IJSVGGroup* group = (IJSVGGroup*)node;
for(IJSVGNode* childNode in group.childNodes) {
[self _walkNodeTree:childNode
handler:handler
allowChildNodes:allowChildNodes
stop:stop];
if(*stop == YES) {
return;
}
}
}
- (void)applyPropertiesFromNode:(IJSVGNode*)node - (void)applyPropertiesFromNode:(IJSVGNode*)node
{ {
self.title = node.title;
self.desc = node.desc;
self.name = node.name; self.name = node.name;
self.type = node.type; self.type = node.type;
self.unicode = node.unicode; self.unicode = node.unicode;
@@ -192,6 +237,7 @@
self.shouldRender = node.shouldRender; self.shouldRender = node.shouldRender;
self.blendMode = node.blendMode; self.blendMode = node.blendMode;
self.overflowVisibility = node.overflowVisibility;
// dash array needs physical memory copied // dash array needs physical memory copied
CGFloat* nStrokeDashArray = (CGFloat*)malloc(node.strokeDashArrayCount * sizeof(CGFloat)); CGFloat* nStrokeDashArray = (CGFloat*)malloc(node.strokeDashArrayCount * sizeof(CGFloat));
@@ -230,9 +276,10 @@
self.units = IJSVGUnitInherit; self.units = IJSVGUnitInherit;
self.blendMode = IJSVGBlendModeNormal; self.blendMode = IJSVGBlendModeNormal;
self.overflowVisibility = IJSVGOverflowVisibilityVisible;
if (flag) { if (flag == YES) {
def = [[IJSVGDef alloc] init]; _def = [[IJSVGDef alloc] init];
} }
} }
return self; return self;
@@ -241,146 +288,146 @@
- (IJSVGDef*)defForID:(NSString*)anID - (IJSVGDef*)defForID:(NSString*)anID
{ {
IJSVGDef* aDef = nil; IJSVGDef* aDef = nil;
if ((aDef = [def defForID:anID]) != nil) { if ((aDef = [_def defForID:anID]) != nil) {
return aDef; return aDef;
} }
if (parentNode != nil) { if (_parentNode != nil) {
return [parentNode defForID:anID]; return [_parentNode defForID:anID];
} }
return nil; return nil;
} }
- (void)addDef:(IJSVGNode*)aDef - (void)addDef:(IJSVGNode*)aDef
{ {
[def addDef:aDef]; [_def addDef:aDef];
} }
// winding rule can inherit.. // winding rule can inherit..
- (IJSVGWindingRule)windingRule - (IJSVGWindingRule)windingRule
{ {
if (windingRule == IJSVGWindingRuleInherit && parentNode != nil) { if (_windingRule == IJSVGWindingRuleInherit && _parentNode != nil) {
return parentNode.windingRule; return _parentNode.windingRule;
} }
return windingRule; return _windingRule;
} }
- (IJSVGLineCapStyle)lineCapStyle - (IJSVGLineCapStyle)lineCapStyle
{ {
if (lineCapStyle == IJSVGLineCapStyleInherit) { if (_lineCapStyle == IJSVGLineCapStyleInherit) {
if (parentNode != nil) { if (_parentNode != nil) {
return parentNode.lineCapStyle; return _parentNode.lineCapStyle;
} }
} }
return lineCapStyle; return _lineCapStyle;
} }
- (IJSVGLineJoinStyle)lineJoinStyle - (IJSVGLineJoinStyle)lineJoinStyle
{ {
if (lineJoinStyle == IJSVGLineJoinStyleInherit) { if (_lineJoinStyle == IJSVGLineJoinStyleInherit) {
if (parentNode != nil) { if (_parentNode != nil) {
return parentNode.lineJoinStyle; return _parentNode.lineJoinStyle;
} }
} }
return lineJoinStyle; return _lineJoinStyle;
} }
// these are all recursive, so go up the chain // these are all recursive, so go up the chain
// if they dont exist on this specific node // if they dont exist on this specific node
- (IJSVGUnitLength*)opacity - (IJSVGUnitLength*)opacity
{ {
if (opacity.inherit && parentNode != nil) { if (_opacity.inherit && _parentNode != nil) {
return parentNode.opacity; return _parentNode.opacity;
} }
return opacity; return _opacity;
} }
// these are all recursive, so go up the chain // these are all recursive, so go up the chain
// if they dont exist on this specific node // if they dont exist on this specific node
- (IJSVGUnitLength*)fillOpacity - (IJSVGUnitLength*)fillOpacity
{ {
if (fillOpacity.inherit && parentNode != nil) { if (_fillOpacity.inherit && _parentNode != nil) {
return parentNode.fillOpacity; return _parentNode.fillOpacity;
} }
return fillOpacity; return _fillOpacity;
} }
// these are all recursive, so go up the chain // these are all recursive, so go up the chain
// if they dont exist on this specific node // if they dont exist on this specific node
- (IJSVGUnitLength*)strokeWidth - (IJSVGUnitLength*)strokeWidth
{ {
if (strokeWidth.inherit && parentNode != nil) { if (_strokeWidth.inherit && _parentNode != nil) {
return parentNode.strokeWidth; return _parentNode.strokeWidth;
} }
return strokeWidth; return _strokeWidth;
} }
// these are all recursive, so go up the chain // these are all recursive, so go up the chain
// if they dont exist on this specific node // if they dont exist on this specific node
- (NSColor*)strokeColor - (NSColor*)strokeColor
{ {
if (strokeColor != nil) if (_strokeColor != nil)
return strokeColor; return _strokeColor;
if (strokeColor == nil && parentNode != nil) if (_strokeColor == nil && _parentNode != nil)
return parentNode.strokeColor; return _parentNode.strokeColor;
return nil; return nil;
} }
- (IJSVGUnitLength*)strokeOpacity - (IJSVGUnitLength*)strokeOpacity
{ {
if (strokeOpacity.inherit && parentNode != nil) { if (_strokeOpacity.inherit && _parentNode != nil) {
return parentNode.strokeOpacity; return _parentNode.strokeOpacity;
} }
return strokeOpacity; return _strokeOpacity;
} }
// even though the spec explicity states fill color // even though the spec explicity states fill color
// must be on the path, it can also be on the // must be on the path, it can also be on the
- (NSColor*)fillColor - (NSColor*)fillColor
{ {
if (fillColor == nil && parentNode != nil) { if (_fillColor == nil && _parentNode != nil) {
return parentNode.fillColor; return _parentNode.fillColor;
} }
return fillColor; return _fillColor;
} }
// these are all recursive, so go up the chain // these are all recursive, so go up the chain
// if they dont exist on this specific node // if they dont exist on this specific node
- (IJSVGGradient*)fillGradient - (IJSVGGradient*)fillGradient
{ {
if (fillGradient == nil && parentNode != nil) { if (_fillGradient == nil && _parentNode != nil) {
return parentNode.fillGradient; return _parentNode.fillGradient;
} }
return fillGradient; return _fillGradient;
} }
// these are all recursive, so go up the chain // these are all recursive, so go up the chain
// if they dont exist on this specific node // if they dont exist on this specific node
- (IJSVGPattern*)fillPattern - (IJSVGPattern*)fillPattern
{ {
if (fillPattern == nil && parentNode != nil) { if (_fillPattern == nil && _parentNode != nil) {
return parentNode.fillPattern; return _parentNode.fillPattern;
} }
return fillPattern; return _fillPattern;
} }
// these are all recursive, so go up the chain // these are all recursive, so go up the chain
// if they dont exist on this specific node // if they dont exist on this specific node
- (IJSVGGradient*)strokeGradient - (IJSVGGradient*)strokeGradient
{ {
if (strokeGradient == nil && parentNode != nil) { if (_strokeGradient == nil && _parentNode != nil) {
return parentNode.strokeGradient; return _parentNode.strokeGradient;
} }
return strokeGradient; return _strokeGradient;
} }
// these are all recursive, so go up the chain // these are all recursive, so go up the chain
// if they dont exist on this specific node // if they dont exist on this specific node
- (IJSVGPattern*)strokePattern - (IJSVGPattern*)strokePattern
{ {
if (strokePattern == nil && parentNode != nil) { if (_strokePattern == nil && _parentNode != nil) {
return parentNode.strokePattern; return _parentNode.strokePattern;
} }
return strokePattern; return _strokePattern;
} }
@end @end
@@ -6,8 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved. // Copyright (c) 2014 Curtis Hard. All rights reserved.
// //
#import "IJSVGBezierPathAdditions.h" #import <IJSVG/IJSVGNode.h>
#import "IJSVGNode.h"
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@class IJSVGGroup; @class IJSVGGroup;
@@ -23,15 +22,14 @@ typedef NS_ENUM(NSInteger, IJSVGPrimitivePathType) {
}; };
@interface IJSVGPath : IJSVGNode { @interface IJSVGPath : IJSVGNode {
NSBezierPath* path;
CGPoint lastControlPoint;
} }
@property (nonatomic, assign) IJSVGPrimitivePathType primitiveType; @property (nonatomic, assign) IJSVGPrimitivePathType primitiveType;
@property (nonatomic, retain) NSBezierPath* path; @property (nonatomic, assign) CGMutablePathRef path;
@property (nonatomic, assign) CGPoint lastControlPoint; @property (nonatomic, assign) CGPoint lastControlPoint;
@property (nonatomic, readonly) CGPathRef CGPath; @property (nonatomic, readonly) CGRect controlPointBoundingBox;
@property (nonatomic, readonly) CGRect pathBoundingBox;
@property (nonatomic, assign, readonly) BOOL isStroked;
- (void)close; - (void)close;
- (NSPoint)currentPoint; - (NSPoint)currentPoint;
+30 -26
View File
@@ -11,18 +11,11 @@
@implementation IJSVGPath @implementation IJSVGPath
@synthesize path = _path;
@synthesize lastControlPoint;
@synthesize CGPath = _CGPath;
@synthesize primitiveType = _primitiveType;
- (void)dealloc - (void)dealloc
{ {
if (_CGPath != nil) { if(_path != NULL) {
CGPathRelease(_CGPath); (void)CGPathRelease(_path), _path = NULL;
_CGPath = nil;
} }
((void)[_path release]), _path = nil;
[super dealloc]; [super dealloc];
} }
@@ -30,7 +23,7 @@
{ {
if ((self = [super init]) != nil) { if ((self = [super init]) != nil) {
_primitiveType = kIJSVGPrimitivePathTypePath; _primitiveType = kIJSVGPrimitivePathTypePath;
_path = NSBezierPath.bezierPath.retain; _path = CGPathCreateMutable();
} }
return self; return self;
} }
@@ -38,34 +31,45 @@
- (id)copyWithZone:(NSZone*)zone - (id)copyWithZone:(NSZone*)zone
{ {
IJSVGPath* node = [super copyWithZone:zone]; IJSVGPath* node = [super copyWithZone:zone];
node.path = [self.path.copy autorelease]; node.path = _path;
return node; return node;
} }
- (void)setPath:(CGMutablePathRef)path
{
// this will automatically copy any path into a mutable path
// regardless of if it was a mutable path to begin with
if(_path != NULL) {
(void)CGPathRelease(_path), _path = NULL;
}
_path = CGPathCreateMutableCopy(path);
}
- (CGRect)pathBoundingBox
{
return CGPathGetPathBoundingBox(_path);
}
- (CGRect)controlPointBoundingBox
{
return CGPathGetBoundingBox(_path);
}
- (NSPoint)currentPoint - (NSPoint)currentPoint
{ {
return _path.currentPoint; return CGPathGetCurrentPoint(_path);
} }
- (void)close - (void)close
{ {
[_path closePath]; CGPathCloseSubpath(_path);
} }
- (void)invlidateCGPath - (BOOL)isStroked
{ {
if (_CGPath != nil) { return (self.strokeColor != nil && self.strokeColor.alphaComponent != 0.f) ||
CGPathRelease(_CGPath); self.strokePattern != nil ||
} self.strokeGradient != nil;
_CGPath = nil;
}
- (CGPathRef)CGPath
{
if (_CGPath == nil) {
_CGPath = [_path newCGPathRef:NO];
}
return _CGPath;
} }
@end @end
@@ -6,8 +6,8 @@
// Copyright © 2016 Curtis Hard. All rights reserved. // Copyright © 2016 Curtis Hard. All rights reserved.
// //
#import "IJSVGGroup.h" #import <IJSVG/IJSVGGroup.h>
#import "IJSVGImage.h" #import <IJSVG/IJSVGImage.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface IJSVGPattern : IJSVGGroup { @interface IJSVGPattern : IJSVGGroup {
@@ -6,7 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved. // Copyright (c) 2014 Curtis Hard. All rights reserved.
// //
#import "IJSVGGradient.h" #import <IJSVG/IJSVGGradient.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface IJSVGRadialGradient : IJSVGGradient @interface IJSVGRadialGradient : IJSVGGradient
@@ -7,33 +7,28 @@
// //
#import "IJSVGRadialGradient.h" #import "IJSVGRadialGradient.h"
#import "IJSVGParser.h"
@implementation IJSVGRadialGradient @implementation IJSVGRadialGradient
@synthesize cx;
@synthesize cy;
@synthesize fx;
@synthesize fy;
@synthesize radius;
- (void)dealloc - (void)dealloc
{ {
(void)([cx release]), cx = nil; (void)([_cx release]), _cx = nil;
(void)([cy release]), cy = nil; (void)([_cy release]), _cy = nil;
(void)([fx release]), fx = nil; (void)([_fx release]), _fx = nil;
(void)([fy release]), fy = nil; (void)([_fy release]), _fy = nil;
(void)([radius release]), radius = nil; (void)([_radius release]), _radius = nil;
[super dealloc]; [super dealloc];
} }
- (id)copyWithZone:(NSZone*)zone - (id)copyWithZone:(NSZone*)zone
{ {
IJSVGRadialGradient* grad = [super copyWithZone:zone]; IJSVGRadialGradient* grad = [super copyWithZone:zone];
grad.fx = self.fx; grad.fx = _fx;
grad.fy = self.fy; grad.fy = _fy;
grad.cx = self.cx; grad.cx = _cx;
grad.cy = self.cy; grad.cy = _cy;
grad.radius = self.radius; grad.radius = _radius;
return grad; return grad;
} }
@@ -41,9 +36,10 @@
gradient:(IJSVGRadialGradient*)gradient gradient:(IJSVGRadialGradient*)gradient
{ {
// cx defaults to 50% if not specified // cx defaults to 50% if not specified
NSDictionary* kv = @{ @"cx" : @"cx", NSDictionary* kv = @{
@"cy" : @"cy", IJSVGAttributeCX : @"cx",
@"r" : @"radius" }; IJSVGAttributeCY : @"cy",
IJSVGAttributeR : @"radius" };
for (NSString* key in kv.allKeys) { for (NSString* key in kv.allKeys) {
NSString* str = [element attributeForName:key].stringValue; NSString* str = [element attributeForName:key].stringValue;
@@ -64,13 +60,13 @@
gradient.fy = gradient.cy; gradient.fy = gradient.cy;
// needs fixing // needs fixing
NSString* fx = [element attributeForName:@"fx"].stringValue; NSString* fx = [element attributeForName:IJSVGAttributeFX].stringValue;
if (fx != nil) { if (fx != nil) {
gradient.fx = [IJSVGUnitLength unitWithString:fx gradient.fx = [IJSVGUnitLength unitWithString:fx
fromUnitType:gradient.units]; fromUnitType:gradient.units];
} }
NSString* fy = [element attributeForName:@"fy"].stringValue; NSString* fy = [element attributeForName:IJSVGAttributeFY].stringValue;
if (fx != nil) { if (fx != nil) {
gradient.fy = [IJSVGUnitLength unitWithString:fy gradient.fy = [IJSVGUnitLength unitWithString:fy
fromUnitType:gradient.units]; fromUnitType:gradient.units];
@@ -81,7 +77,9 @@
} }
NSArray* colors = nil; NSArray* colors = nil;
CGFloat* colorStops = [self.class computeColorStopsFromString:element colors:&colors]; CGFloat* colorStops = [self.class computeColorStops:gradient
colors:&colors];
NSGradient* ret = [[[NSGradient alloc] initWithColors:colors NSGradient* ret = [[[NSGradient alloc] initWithColors:colors
atLocations:colorStops atLocations:colorStops
colorSpace:IJSVGColor.defaultColorSpace] autorelease]; colorSpace:IJSVGColor.defaultColorSpace] autorelease];
@@ -112,15 +110,15 @@
} }
// compute size based on percentages // compute size based on percentages
CGFloat x = [self.cx computeValue:CGRectGetWidth(boundingBox)]; CGFloat x = [_cx computeValue:CGRectGetWidth(boundingBox)];
CGFloat y = [self.cy computeValue:CGRectGetHeight(boundingBox)]; CGFloat y = [_cy computeValue:CGRectGetHeight(boundingBox)];
startPoint = CGPointMake(x, y); startPoint = CGPointMake(x, y);
CGFloat val = MIN(CGRectGetWidth(boundingBox), CGFloat val = MIN(CGRectGetWidth(boundingBox),
CGRectGetHeight(boundingBox)); CGRectGetHeight(boundingBox));
radius = [self.radius computeValue:val]; radius = [_radius computeValue:val];
CGFloat ex = [self.fx computeValue:CGRectGetWidth(boundingBox)]; CGFloat ex = [_fx computeValue:CGRectGetWidth(boundingBox)];
CGFloat ey = [self.fy computeValue:CGRectGetHeight(boundingBox)]; CGFloat ey = [_fy computeValue:CGRectGetHeight(boundingBox)];
gradientEndPoint = CGPointMake(ex, ey); gradientEndPoint = CGPointMake(ex, ey);
gradientStartPoint = startPoint; gradientStartPoint = startPoint;
@@ -6,11 +6,9 @@
// Copyright © 2017 Curtis Hard. All rights reserved. // Copyright © 2017 Curtis Hard. All rights reserved.
// //
#import "IJSVGGroup.h" #import <IJSVG/IJSVGGroup.h>
@interface IJSVGText : IJSVGGroup { @interface IJSVGText : IJSVGGroup {
NSString* text;
} }
@property (nonatomic, copy) NSString* text; @property (nonatomic, copy) NSString* text;
@@ -10,18 +10,16 @@
@implementation IJSVGText @implementation IJSVGText
@synthesize text;
- (void)dealloc - (void)dealloc
{ {
(void)([text release]), text = nil; (void)([_text release]), _text = nil;
[super dealloc]; [super dealloc];
} }
- (IJSVGText*)copyWithZone:(NSZone*)zone - (IJSVGText*)copyWithZone:(NSZone*)zone
{ {
IJSVGText* node = [super copyWithZone:zone]; IJSVGText* node = [super copyWithZone:zone];
node.text = self.text; node.text = _text;
return node; return node;
} }
@@ -6,65 +6,74 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved. // Copyright (c) 2014 Curtis Hard. All rights reserved.
// //
#import "IJSVGColor.h" #import <IJSVG/IJSVGColor.h>
#import "IJSVGCommand.h" #import <IJSVG/IJSVGCommand.h>
#import "IJSVGDef.h" #import <IJSVG/IJSVGDef.h>
#import "IJSVGError.h" #import <IJSVG/IJSVGError.h>
#import "IJSVGForeignObject.h" #import <IJSVG/IJSVGForeignObject.h>
#import "IJSVGGroup.h" #import <IJSVG/IJSVGGroup.h>
#import "IJSVGImage.h" #import <IJSVG/IJSVGImage.h>
#import "IJSVGLinearGradient.h" #import <IJSVG/IJSVGLinearGradient.h>
#import "IJSVGPath.h" #import <IJSVG/IJSVGPath.h>
#import "IJSVGPattern.h" #import <IJSVG/IJSVGPattern.h>
#import "IJSVGRadialGradient.h" #import <IJSVG/IJSVGRadialGradient.h>
#import "IJSVGStyleSheet.h" #import <IJSVG/IJSVGStyleSheet.h>
#import "IJSVGText.h" #import <IJSVG/IJSVGText.h>
#import "IJSVGTransform.h" #import <IJSVG/IJSVGTransform.h>
#import "IJSVGUtils.h" #import <IJSVG/IJSVGUnitRect.h>
#import <IJSVG/IJSVGUtils.h>
#import <AppKit/AppKit.h> #import <AppKit/AppKit.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
static NSString const* IJSVGAttributeViewBox = @"viewBox"; extern NSString* const IJSVGAttributeViewBox;
static NSString const* IJSVGAttributeID = @"id"; extern NSString* const IJSVGAttributeID;
static NSString const* IJSVGAttributeClass = @"class"; extern NSString* const IJSVGAttributeClass;
static NSString const* IJSVGAttributeX = @"x"; extern NSString* const IJSVGAttributeX;
static NSString const* IJSVGAttributeY = @"y"; extern NSString* const IJSVGAttributeY;
static NSString const* IJSVGAttributeWidth = @"width"; extern NSString* const IJSVGAttributeWidth;
static NSString const* IJSVGAttributeHeight = @"height"; extern NSString* const IJSVGAttributeHeight;
static NSString const* IJSVGAttributeOpacity = @"opacity"; extern NSString* const IJSVGAttributeOpacity;
static NSString const* IJSVGAttributeStrokeOpacity = @"stroke-opacity"; extern NSString* const IJSVGAttributeStrokeOpacity;
static NSString const* IJSVGAttributeStrokeWidth = @"stroke-width"; extern NSString* const IJSVGAttributeStrokeWidth;
static NSString const* IJSVGAttributeStrokeDashOffset = @"stroke-dashoffset"; extern NSString* const IJSVGAttributeStrokeDashOffset;
static NSString const* IJSVGAttributeFillOpacity = @"fill-opacity"; extern NSString* const IJSVGAttributeFillOpacity;
static NSString const* IJSVGAttributeClipPath = @"clip-path"; extern NSString* const IJSVGAttributeClipPath;
static NSString const* IJSVGAttributeMask = @"mask"; extern NSString* const IJSVGAttributeMask;
static NSString const* IJSVGAttributeGradientUnits = @"gradientUnits"; extern NSString* const IJSVGAttributeGradientUnits;
static NSString const* IJSVGAttributeMaskUnits = @"maskUnits"; extern NSString* const IJSVGAttributeMaskUnits;
static NSString const* IJSVGAttributeMaskContentUnits = @"maskContentUnits"; extern NSString* const IJSVGAttributeMaskContentUnits;
static NSString const* IJSVGAttributeTransform = @"transform"; extern NSString* const IJSVGAttributeTransform;
static NSString const* IJSVGAttributeGradientTransform = @"gradientTransform"; extern NSString* const IJSVGAttributeGradientTransform;
static NSString const* IJSVGAttributeUnicode = @"unicode"; extern NSString* const IJSVGAttributeUnicode;
static NSString const* IJSVGAttributeStrokeLineCap = @"stroke-linecap"; extern NSString* const IJSVGAttributeStrokeLineCap;
static NSString const* IJSVGAttributeLineJoin = @"stroke-linejoin"; extern NSString* const IJSVGAttributeLineJoin;
static NSString const* IJSVGAttributeStroke = @"stroke"; extern NSString* const IJSVGAttributeStroke;
static NSString const* IJSVGAttributeStrokeDashArray = @"stroke-dasharray"; extern NSString* const IJSVGAttributeStrokeDashArray;
static NSString const* IJSVGAttributeFill = @"fill"; extern NSString* const IJSVGAttributeFill;
static NSString const* IJSVGAttributeFillRule = @"fill-rule"; extern NSString* const IJSVGAttributeFillRule;
static NSString const* IJSVGAttributeBlendMode = @"mix-blend-mode"; extern NSString* const IJSVGAttributeBlendMode;
static NSString const* IJSVGAttributeDisplay = @"display"; extern NSString* const IJSVGAttributeDisplay;
static NSString const* IJSVGAttributeStyle = @"style"; extern NSString* const IJSVGAttributeStyle;
static NSString const* IJSVGAttributeD = @"d"; extern NSString* const IJSVGAttributeD;
static NSString const* IJSVGAttributeXLink = @"xlink:href"; extern NSString* const IJSVGAttributeXLink;
static NSString const* IJSVGAttributeX1 = @"x1"; extern NSString* const IJSVGAttributeX1;
static NSString const* IJSVGAttributeX2 = @"x2"; extern NSString* const IJSVGAttributeX2;
static NSString const* IJSVGAttributeY1 = @"y1"; extern NSString* const IJSVGAttributeY1;
static NSString const* IJSVGAttributeY2 = @"y2"; extern NSString* const IJSVGAttributeY2;
static NSString const* IJSVGAttributeRX = @"rx"; extern NSString* const IJSVGAttributeRX;
static NSString const* IJSVGAttributeRY = @"ry"; extern NSString* const IJSVGAttributeRY;
static NSString const* IJSVGAttributeCX = @"cx"; extern NSString* const IJSVGAttributeCX;
static NSString const* IJSVGAttributeCY = @"cy"; extern NSString* const IJSVGAttributeCY;
static NSString const* IJSVGAttributeR = @"r"; extern NSString* const IJSVGAttributeR;
static NSString const* IJSVGAttributePoints = @"points"; extern NSString* const IJSVGAttributeFX;
extern NSString* const IJSVGAttributeFY;
extern NSString* const IJSVGAttributePoints;
extern NSString* const IJSVGAttributeOffset;
extern NSString* const IJSVGAttributeStopColor;
extern NSString* const IJSVGAttributeStopOpacity;
extern NSString* const IJSVGAttributeHref;
extern NSString* const IJSVGAttributeOverflow;
@class IJSVGParser; @class IJSVGParser;
@@ -84,17 +93,13 @@ static NSString const* IJSVGAttributePoints = @"points";
@interface IJSVGParser : IJSVGGroup { @interface IJSVGParser : IJSVGGroup {
NSRect viewBox;
NSSize proposedViewSize;
@private @private
id<IJSVGParserDelegate> _delegate; id<IJSVGParserDelegate> _delegate;
NSXMLDocument* _document; NSXMLDocument* _document;
NSMutableArray* _glyphs; NSMutableArray<IJSVGPath*>* _glyphs;
IJSVGStyleSheet* _styleSheet; IJSVGStyleSheet* _styleSheet;
NSMutableArray* _parsedNodes; NSMutableDictionary<NSString*, NSXMLElement*>* _defNodes;
NSMutableDictionary* _defNodes; NSMutableDictionary<NSString*, NSXMLElement*>* _baseDefNodes;
NSMutableDictionary* _baseDefNodes;
NSMutableArray<IJSVG*>* _svgs; NSMutableArray<IJSVG*>* _svgs;
struct { struct {
@@ -107,7 +112,7 @@ static NSString const* IJSVGAttributePoints = @"points";
} }
@property (nonatomic, readonly) NSRect viewBox; @property (nonatomic, readonly) NSRect viewBox;
@property (nonatomic, readonly) NSSize proposedViewSize; @property (nonatomic, readonly) IJSVGUnitSize* intrinsicSize;
+ (BOOL)isDataSVG:(NSData*)data; + (BOOL)isDataSVG:(NSData*)data;
+348 -143
View File
@@ -9,10 +9,85 @@
#import "IJSVG.h" #import "IJSVG.h"
#import "IJSVGParser.h" #import "IJSVGParser.h"
NSString* const IJSVGAttributeViewBox = @"viewBox";
NSString* const IJSVGAttributeID = @"id";
NSString* const IJSVGAttributeClass = @"class";
NSString* const IJSVGAttributeX = @"x";
NSString* const IJSVGAttributeY = @"y";
NSString* const IJSVGAttributeWidth = @"width";
NSString* const IJSVGAttributeHeight = @"height";
NSString* const IJSVGAttributeOpacity = @"opacity";
NSString* const IJSVGAttributeStrokeOpacity = @"stroke-opacity";
NSString* const IJSVGAttributeStrokeWidth = @"stroke-width";
NSString* const IJSVGAttributeStrokeDashOffset = @"stroke-dashoffset";
NSString* const IJSVGAttributeFillOpacity = @"fill-opacity";
NSString* const IJSVGAttributeClipPath = @"clip-path";
NSString* const IJSVGAttributeMask = @"mask";
NSString* const IJSVGAttributeGradientUnits = @"gradientUnits";
NSString* const IJSVGAttributeMaskUnits = @"maskUnits";
NSString* const IJSVGAttributeMaskContentUnits = @"maskContentUnits";
NSString* const IJSVGAttributeTransform = @"transform";
NSString* const IJSVGAttributeGradientTransform = @"gradientTransform";
NSString* const IJSVGAttributeUnicode = @"unicode";
NSString* const IJSVGAttributeStrokeLineCap = @"stroke-linecap";
NSString* const IJSVGAttributeLineJoin = @"stroke-linejoin";
NSString* const IJSVGAttributeStroke = @"stroke";
NSString* const IJSVGAttributeStrokeDashArray = @"stroke-dasharray";
NSString* const IJSVGAttributeFill = @"fill";
NSString* const IJSVGAttributeFillRule = @"fill-rule";
NSString* const IJSVGAttributeBlendMode = @"mix-blend-mode";
NSString* const IJSVGAttributeDisplay = @"display";
NSString* const IJSVGAttributeStyle = @"style";
NSString* const IJSVGAttributeD = @"d";
NSString* const IJSVGAttributeXLink = @"xlink:href";
NSString* const IJSVGAttributeX1 = @"x1";
NSString* const IJSVGAttributeX2 = @"x2";
NSString* const IJSVGAttributeY1 = @"y1";
NSString* const IJSVGAttributeY2 = @"y2";
NSString* const IJSVGAttributeRX = @"rx";
NSString* const IJSVGAttributeRY = @"ry";
NSString* const IJSVGAttributeCX = @"cx";
NSString* const IJSVGAttributeCY = @"cy";
NSString* const IJSVGAttributeR = @"r";
NSString* const IJSVGAttributeFX = @"fx";
NSString* const IJSVGAttributeFY = @"fy";
NSString* const IJSVGAttributePoints = @"points";
NSString* const IJSVGAttributeOffset = @"offset";
NSString* const IJSVGAttributeStopColor = @"stop-color";
NSString* const IJSVGAttributeStopOpacity = @"stop-opacity";
NSString* const IJSVGAttributeHref = @"href";
NSString* const IJSVGAttributeOverflow = @"overflow";
@implementation IJSVGParser @implementation IJSVGParser
@synthesize viewBox; static NSDictionary* _IJSVGAttributeDictionaryFloats = nil;
@synthesize proposedViewSize; static NSDictionary* _IJSVGAttributeDictionaryNodes = nil;
static NSDictionary* _IJSVGAttributeDictionaryUnits = nil;
static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
+ (void)load
{
_IJSVGAttributeDictionaryFloats = [@{
IJSVGAttributeX : @"x",
IJSVGAttributeY : @"y",
IJSVGAttributeWidth : @"width",
IJSVGAttributeHeight : @"height",
IJSVGAttributeOpacity : @"opacity",
IJSVGAttributeStrokeOpacity : @"strokeOpacity",
IJSVGAttributeStrokeWidth : @"strokeWidth",
IJSVGAttributeStrokeDashOffset : @"strokeDashOffset",
IJSVGAttributeFillOpacity : @"fillOpacity" } retain];
_IJSVGAttributeDictionaryNodes = [@{
IJSVGAttributeClipPath : @"clipPath",
IJSVGAttributeMask : @"mask" } retain];
_IJSVGAttributeDictionaryUnits = [@{
IJSVGAttributeGradientUnits : @"units",
IJSVGAttributeMaskUnits : @"units",
IJSVGAttributeMaskContentUnits : @"contentUnits"} retain];
_IJSVGAttributeDictionaryTransforms = [@{
IJSVGAttributeTransform : @"transforms",
IJSVGAttributeGradientTransform : @"transforms" } retain];
}
+ (IJSVGParser*)groupForFileURL:(NSURL*)aURL + (IJSVGParser*)groupForFileURL:(NSURL*)aURL
{ {
@@ -42,10 +117,10 @@
{ {
(void)([_glyphs release]), _glyphs = nil; (void)([_glyphs release]), _glyphs = nil;
(void)([_styleSheet release]), _styleSheet = nil; (void)([_styleSheet release]), _styleSheet = nil;
(void)([_parsedNodes release]), _parsedNodes = nil;
(void)([_defNodes release]), _defNodes = nil; (void)([_defNodes release]), _defNodes = nil;
(void)([_baseDefNodes release]), _baseDefNodes = nil; (void)([_baseDefNodes release]), _baseDefNodes = nil;
(void)([_svgs release]), _svgs = nil; (void)([_svgs release]), _svgs = nil;
(void)([_intrinsicSize release]), _intrinsicSize = nil;
if (_commandDataStream != NULL) { if (_commandDataStream != NULL) {
(void)IJSVGPathDataStreamRelease(_commandDataStream), _commandDataStream = nil; (void)IJSVGPathDataStreamRelease(_commandDataStream), _commandDataStream = nil;
} }
@@ -64,11 +139,7 @@
_respondsTo.handleSubSVG = [_delegate respondsToSelector:@selector(svgParser:foundSubSVG:withSVGString:)]; _respondsTo.handleSubSVG = [_delegate respondsToSelector:@selector(svgParser:foundSubSVG:withSVGString:)];
_commandDataStream = IJSVGPathDataStreamCreateDefault(); _commandDataStream = IJSVGPathDataStreamCreateDefault();
_glyphs = [[NSMutableArray alloc] init];
_parsedNodes = [[NSMutableArray alloc] init];
_defNodes = [[NSMutableDictionary alloc] init]; _defNodes = [[NSMutableDictionary alloc] init];
_baseDefNodes = [[NSMutableDictionary alloc] init];
_svgs = [[NSMutableArray alloc] init];
// load the document / file, assume its UTF8 // load the document / file, assume its UTF8
@@ -181,7 +252,7 @@
- (NSSize)size - (NSSize)size
{ {
return viewBox.size; return _viewBox.size;
} }
- (void)_parse - (void)_parse
@@ -195,35 +266,47 @@
// find the sizebox! // find the sizebox!
NSXMLNode* attribute = nil; NSXMLNode* attribute = nil;
if ((attribute = [svgElement attributeForName:(NSString*)IJSVGAttributeViewBox]) != nil) { if ((attribute = [svgElement attributeForName:IJSVGAttributeViewBox]) != nil) {
// we have a viewbox... // we have a viewbox...
CGFloat* box = [IJSVGUtils parseViewBox:[attribute stringValue]]; CGFloat* box = [IJSVGUtils parseViewBox:attribute.stringValue];
viewBox = NSMakeRect(box[0], box[1], box[2], box[3]); _viewBox = NSMakeRect(box[0], box[1], box[2], box[3]);
free(box); (void)free(box);
} else { } else {
// there is no view box so find the width and height // there is no view box so find the width and height
CGFloat w = [svgElement attributeForName:(NSString*)IJSVGAttributeWidth].stringValue.floatValue; NSString* wAtt = [svgElement attributeForName:IJSVGAttributeWidth].stringValue;
CGFloat h = [svgElement attributeForName:(NSString*)IJSVGAttributeHeight].stringValue.floatValue; NSString* hAtt = [svgElement attributeForName:IJSVGAttributeHeight].stringValue;
IJSVGUnitLength* wLength = [IJSVGUnitLength unitWithString:wAtt];
IJSVGUnitLength* hLength = [IJSVGUnitLength unitWithString:hAtt];
// its possible wlength or hlength are nil
CGFloat w = wLength ? wLength.value : 0.f;
CGFloat h = hLength ? hLength.value : 0.f;
if (h == 0.f && w != 0.f) { if (h == 0.f && w != 0.f) {
h = w; h = w;
} else if (w == 0.f && h != 0.f) { } else if (w == 0.f && h != 0.f) {
w = h; w = h;
} }
viewBox = NSMakeRect(0.f, 0.f, w, h); _viewBox = NSMakeRect(0.f, 0.f, w, h);
} }
// parse the width and height.... // parse the width and height....
CGFloat w = [svgElement attributeForName:(NSString*)IJSVGAttributeWidth].stringValue.floatValue; NSString* w = [svgElement attributeForName:IJSVGAttributeWidth].stringValue;
CGFloat h = [svgElement attributeForName:(NSString*)IJSVGAttributeHeight].stringValue.floatValue; NSString* h = [svgElement attributeForName:IJSVGAttributeHeight].stringValue;
if (w == 0.f && h == 0.f) {
w = viewBox.size.width; // by default just the the width and height from the viewbox unless
h = viewBox.size.height; // specified otherwise
} else if (w == 0 && h != 0.f) { IJSVGUnitLength* wl = [IJSVGUnitLength unitWithFloat:_viewBox.size.width];
w = viewBox.size.width; IJSVGUnitLength* hl = [IJSVGUnitLength unitWithFloat:_viewBox.size.height];
} else if (h == 0 && w != 0.f) { if (w != nil) {
h = viewBox.size.height; 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 // the root element is SVG, so iterate over its children
// recursively // recursively
@@ -232,16 +315,8 @@
intoGroup:self intoGroup:self
def:NO]; 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 // dont need the style sheet or the parsed nodes as this point
(void)([_styleSheet release]), _styleSheet = nil; (void)([_styleSheet release]), _styleSheet = nil;
(void)([_parsedNodes release]), _parsedNodes = nil;
(void)([_defNodes release]), _defNodes = nil; (void)([_defNodes release]), _defNodes = nil;
(void)IJSVGPathDataStreamRelease(_commandDataStream), _commandDataStream = NULL; (void)IJSVGPathDataStreamRelease(_commandDataStream), _commandDataStream = NULL;
} }
@@ -317,22 +392,13 @@
} }
// floats // floats
atts(@{ (NSString*)IJSVGAttributeX : @"x", atts(_IJSVGAttributeDictionaryFloats,
(NSString*)IJSVGAttributeY : @"y",
(NSString*)IJSVGAttributeWidth : @"width",
(NSString*)IJSVGAttributeHeight : @"height",
(NSString*)IJSVGAttributeOpacity : @"opacity",
(NSString*)IJSVGAttributeStrokeOpacity : @"strokeOpacity",
(NSString*)IJSVGAttributeStrokeWidth : @"strokeWidth",
(NSString*)IJSVGAttributeStrokeDashOffset : @"strokeDashOffset",
(NSString*)IJSVGAttributeFillOpacity : @"fillOpacity" },
^id(NSString* value) { ^id(NSString* value) {
return [IJSVGUnitLength unitWithString:value]; return [IJSVGUnitLength unitWithString:value];
}); });
// nodes // nodes
atts(@{ (NSString*)IJSVGAttributeClipPath : @"clipPath", atts(_IJSVGAttributeDictionaryNodes,
(NSString*)IJSVGAttributeMask : @"mask" },
^id(NSString* value) { ^id(NSString* value) {
NSString* url = [IJSVGUtils defURL:value]; NSString* url = [IJSVGUtils defURL:value];
if (url != nil) { if (url != nil) {
@@ -342,16 +408,13 @@
}); });
// units // units
atts(@{ (NSString*)IJSVGAttributeGradientUnits : @"units", atts(_IJSVGAttributeDictionaryUnits,
(NSString*)IJSVGAttributeMaskUnits : @"units",
(NSString*)IJSVGAttributeMaskContentUnits : @"contentUnits" },
^id(NSString* value) { ^id(NSString* value) {
return @([IJSVGUtils unitTypeForString:value]); return @([IJSVGUtils unitTypeForString:value]);
}); });
// transforms // transforms
atts(@{ (NSString*)IJSVGAttributeTransform : @"transforms", atts(_IJSVGAttributeDictionaryTransforms,
(NSString*)IJSVGAttributeGradientTransform : @"transforms" },
^(NSString* value) { ^(NSString* value) {
NSMutableArray* tempTransforms = [[[NSMutableArray alloc] init] autorelease]; NSMutableArray* tempTransforms = [[[NSMutableArray alloc] init] autorelease];
[tempTransforms addObjectsFromArray:[IJSVGTransform transformsForString:value]]; [tempTransforms addObjectsFromArray:[IJSVGTransform transformsForString:value]];
@@ -418,7 +481,6 @@
if (fillDefID != nil) { if (fillDefID != nil) {
// find the object // find the object
id obj = [self definedObjectForID:fillDefID]; id obj = [self definedObjectForID:fillDefID];
// what type is it? // what type is it?
if ([obj isKindOfClass:[IJSVGGradient class]]) { if ([obj isKindOfClass:[IJSVGGradient class]]) {
node.fillGradient = (IJSVGGradient*)obj; node.fillGradient = (IJSVGGradient*)obj;
@@ -454,6 +516,52 @@
node.shouldRender = NO; node.shouldRender = NO;
} }
}); });
// offset
attr(IJSVGAttributeOffset, ^(NSString* value) {
node.offset = [IJSVGUnitLength unitWithString:value];
});
// stop-opacity
attr(IJSVGAttributeStopOpacity, ^(NSString* value) {
node.fillOpacity = [IJSVGUnitLength unitWithString:value];
});
// stop-color
attr(IJSVGAttributeStopColor, ^(NSString* value) {
node.fillColor = [IJSVGColor colorFromString:value];
if(node.fillOpacity.value != 1.f) {
node.fillColor = [IJSVGColor changeAlphaOnColor:node.fillColor
to:node.fillOpacity.value];
}
});
// overflow
attr(IJSVGAttributeOverflow, ^(NSString* value) {
if([value.lowercaseString isEqualToString:@"hidden"]) {
node.overflowVisibility = IJSVGOverflowVisibilityHidden;
} else {
node.overflowVisibility = IJSVGOverflowVisibilityVisible;
}
});
// 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 - (id)definedObjectForID:(NSString*)anID
@@ -483,25 +591,28 @@
- (BOOL)isFont - (BOOL)isFont
{ {
return [_glyphs count] != 0; return _glyphs != nil && [_glyphs count] != 0;
} }
- (NSArray*)glyphs - (NSArray*)glyphs
{ {
return _glyphs; return _glyphs ?: @[];
} }
- (void)addSubSVG:(IJSVG*)anSVG - (void)addSubSVG:(IJSVG*)anSVG
{ {
if (_svgs == nil) {
_svgs = [[NSMutableArray alloc] init];
}
[_svgs addObject:anSVG]; [_svgs addObject:anSVG];
} }
- (NSArray<IJSVG*>*)subSVGs:(BOOL)recursive - (NSArray<IJSVG*>*)subSVGs:(BOOL)recursive
{ {
if (recursive == NO) { 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) { for (IJSVG* anSVG in svgs) {
[svgs addObject:anSVG]; [svgs addObject:anSVG];
[svgs addObjectsFromArray:[anSVG subSVGs:recursive]]; [svgs addObjectsFromArray:[anSVG subSVGs:recursive]];
@@ -509,8 +620,11 @@
return svgs; return svgs;
} }
- (void)addGlyph:(IJSVGNode*)glyph - (void)addGlyph:(IJSVGPath*)glyph
{ {
if (_glyphs == nil) {
_glyphs = [[NSMutableArray alloc] init];
}
[_glyphs addObject:glyph]; [_glyphs addObject:glyph];
} }
@@ -528,10 +642,18 @@
switch (node.type) { switch (node.type) {
// mask // mask
case IJSVGNodeTypeMask: { case IJSVGNodeTypeMask: {
node.overflowVisibility = IJSVGOverflowVisibilityHidden;
node.units = IJSVGUnitObjectBoundingBox; node.units = IJSVGUnitObjectBoundingBox;
break; break;
} }
// clippath
case IJSVGNodeTypeClipPath: {
node.units = IJSVGUnitObjectBoundingBox;
node.overflowVisibility = IJSVGOverflowVisibilityHidden;
break;
}
// gradient // gradient
case IJSVGNodeTypeRadialGradient: case IJSVGNodeTypeRadialGradient:
case IJSVGNodeTypeLinearGradient: { case IJSVGNodeTypeLinearGradient: {
@@ -581,7 +703,10 @@
default: { default: {
// just a default def, continue on, as we are a def element, // just a default def, continue on, as we are a def element,
// store these seperately to the default ID string ones // store these seperately to the default ID string ones
NSString* defID = [childDef attributeForName:@"id"].stringValue; if (_baseDefNodes == nil) {
_baseDefNodes = [[NSMutableDictionary alloc] init];
}
NSString* defID = [childDef attributeForName:IJSVGAttributeID].stringValue;
if (defID != nil) { if (defID != nil) {
_baseDefNodes[defID] = childDef; _baseDefNodes[defID] = childDef;
} }
@@ -635,8 +760,8 @@
// if its a sub svg, we can remove the attributes for x and y // if its a sub svg, we can remove the attributes for x and y
// this is required or it could go out of bounds before the exporter // this is required or it could go out of bounds before the exporter
// hits the layers from the groups :) // hits the layers from the groups :)
[element removeAttributeForName:@"x"]; [element removeAttributeForName:IJSVGAttributeX];
[element removeAttributeForName:@"y"]; [element removeAttributeForName:IJSVGAttributeY];
// work out the SVG // work out the SVG
NSError* error = nil; NSError* error = nil;
@@ -668,8 +793,8 @@
case IJSVGNodeTypeGlyph: { case IJSVGNodeTypeGlyph: {
// no path data // no path data
if ([element attributeForName:(NSString*)IJSVGAttributeD] == nil || if ([element attributeForName:IJSVGAttributeD] == nil ||
[[element attributeForName:(NSString*)IJSVGAttributeD] stringValue].length == 0) { [[element attributeForName:IJSVGAttributeD] stringValue].length == 0) {
break; break;
} }
@@ -685,11 +810,11 @@
ignoreAttributes:nil]; ignoreAttributes:nil];
// pass the commands for it // pass the commands for it
[self _parsePathCommandData:[[element attributeForName:(NSString*)IJSVGAttributeD] stringValue] [self _parsePathCommandData:[[element attributeForName:IJSVGAttributeD] stringValue]
intoPath:path]; intoPath:path];
// check the size... // check the size...
if (NSIsEmptyRect([path path].controlPointBounds)) { if (CGRectIsEmpty(path.controlPointBoundingBox) == YES) {
break; break;
} }
@@ -750,7 +875,7 @@
[self _parseElementForCommonAttributes:element [self _parseElementForCommonAttributes:element
node:path node:path
ignoreAttributes:nil]; ignoreAttributes:nil];
[self _parsePathCommandData:[[element attributeForName:(NSString*)IJSVGAttributeD] stringValue] [self _parsePathCommandData:[[element attributeForName:IJSVGAttributeD] stringValue]
intoPath:path]; intoPath:path];
[parentGroup addDef:path]; [parentGroup addDef:path];
@@ -819,7 +944,7 @@
[self _setupDefaultsForNode:path]; [self _setupDefaultsForNode:path];
[self _parseElementForCommonAttributes:element [self _parseElementForCommonAttributes:element
node:path node:path
ignoreAttributes:@[ @"x", @"y" ]]; ignoreAttributes:@[ IJSVGAttributeX, IJSVGAttributeY ]];
[parentGroup addDef:path]; [parentGroup addDef:path];
break; break;
} }
@@ -891,7 +1016,7 @@
// use // use
case IJSVGNodeTypeUse: { case IJSVGNodeTypeUse: {
NSString* xlink = [[element attributeForName:(NSString*)IJSVGAttributeXLink] stringValue]; NSString* xlink = [[self resolveXLinkAttributeForElement:element] stringValue];
NSString* xlinkID = [xlink substringFromIndex:1]; NSString* xlinkID = [xlink substringFromIndex:1];
IJSVGNode* node = [self definedObjectForID:xlinkID]; IJSVGNode* node = [self definedObjectForID:xlinkID];
@@ -902,7 +1027,7 @@
} }
// due to this being a carbon clone, we need to clear the ID // due to this being a carbon clone, we need to clear the ID
if ([element attributeForName:(NSString*)IJSVGAttributeID] == nil) { if ([element attributeForName:IJSVGAttributeID] == nil) {
node.identifier = nil; node.identifier = nil;
} }
@@ -914,8 +1039,8 @@
node.intermediateParentNode = subGroup; node.intermediateParentNode = subGroup;
// is there a width and height? // is there a width and height?
CGFloat x = [element attributeForName:(NSString*)IJSVGAttributeX].stringValue.floatValue; CGFloat x = [element attributeForName:IJSVGAttributeX].stringValue.floatValue;
CGFloat y = [element attributeForName:(NSString*)IJSVGAttributeY].stringValue.floatValue; CGFloat y = [element attributeForName:IJSVGAttributeY].stringValue.floatValue;
// we need to add a transform to the subgroup // we need to add a transform to the subgroup
subGroup.transforms = @[ [IJSVGTransform transformByTranslatingX:x y:y] ]; subGroup.transforms = @[ [IJSVGTransform transformByTranslatingX:x y:y] ];
@@ -928,17 +1053,30 @@
// says ignore x, y, width, height and xlink:href... // says ignore x, y, width, height and xlink:href...
[self _parseElementForCommonAttributes:element [self _parseElementForCommonAttributes:element
node:node node:node
ignoreAttributes:@[ @"x", @"y", @"width", ignoreAttributes:@[IJSVGAttributeX, IJSVGAttributeY,
@"height", @"xlink:href" ]]; IJSVGAttributeWidth, IJSVGAttributeHeight,
IJSVGAttributeXLink]];
[parentGroup addDef:node]; [parentGroup addDef:node];
break; break;
} }
// stop color
case IJSVGNodeTypeStop: {
IJSVGNode* node = [[[IJSVGNode alloc] init] autorelease];
node.type = IJSVGNodeTypeStop;
[self _setupDefaultsForNode:node];
[self _parseElementForCommonAttributes:element
node:node
ignoreAttributes:nil];
[parentGroup addChild:node];
break;
}
// linear gradient // linear gradient
case IJSVGNodeTypeLinearGradient: { case IJSVGNodeTypeLinearGradient: {
NSString* xlink = [[element attributeForName:(NSString*)IJSVGAttributeXLink] stringValue]; NSString* xlink = [[self resolveXLinkAttributeForElement:element] stringValue];
NSString* xlinkID = [xlink substringFromIndex:1]; NSString* xlinkID = [xlink substringFromIndex:1];
NSXMLElement* referenceElement; NSXMLElement* referenceElement;
IJSVGNode* node = [self definedObjectForID:xlinkID IJSVGNode* node = [self definedObjectForID:xlinkID
@@ -954,6 +1092,9 @@
[self _parseElementForCommonAttributes:elementCopy [self _parseElementForCommonAttributes:elementCopy
node:grad node:grad
ignoreAttributes:nil]; ignoreAttributes:nil];
[self _parseBlock:elementCopy
intoGroup:grad
def:NO];
grad.gradient = [IJSVGLinearGradient parseGradient:elementCopy grad.gradient = [IJSVGLinearGradient parseGradient:elementCopy
gradient:grad]; gradient:grad];
[parentGroup addDef:grad]; [parentGroup addDef:grad];
@@ -966,6 +1107,9 @@
[self _parseElementForCommonAttributes:element [self _parseElementForCommonAttributes:element
node:gradient node:gradient
ignoreAttributes:nil]; ignoreAttributes:nil];
[self _parseBlock:element
intoGroup:gradient
def:NO];
gradient.gradient = [IJSVGLinearGradient parseGradient:element gradient.gradient = [IJSVGLinearGradient parseGradient:element
gradient:gradient]; gradient:gradient];
[parentGroup addDef:gradient]; [parentGroup addDef:gradient];
@@ -975,7 +1119,7 @@
// radial gradient // radial gradient
case IJSVGNodeTypeRadialGradient: { case IJSVGNodeTypeRadialGradient: {
NSString* xlink = [[element attributeForName:(NSString*)IJSVGAttributeXLink] stringValue]; NSString* xlink = [[self resolveXLinkAttributeForElement:element] stringValue];
NSString* xlinkID = [xlink substringFromIndex:1]; NSString* xlinkID = [xlink substringFromIndex:1];
NSXMLElement* referenceElement; NSXMLElement* referenceElement;
IJSVGNode* node = [self definedObjectForID:xlinkID IJSVGNode* node = [self definedObjectForID:xlinkID
@@ -991,6 +1135,9 @@
[self _parseElementForCommonAttributes:elementCopy [self _parseElementForCommonAttributes:elementCopy
node:grad node:grad
ignoreAttributes:nil]; ignoreAttributes:nil];
[self _parseBlock:elementCopy
intoGroup:grad
def:NO];
grad.gradient = [IJSVGRadialGradient parseGradient:elementCopy grad.gradient = [IJSVGRadialGradient parseGradient:elementCopy
gradient:grad]; gradient:grad];
[parentGroup addDef:grad]; [parentGroup addDef:grad];
@@ -1003,6 +1150,9 @@
[self _parseElementForCommonAttributes:element [self _parseElementForCommonAttributes:element
node:gradient node:gradient
ignoreAttributes:nil]; ignoreAttributes:nil];
[self _parseBlock:element
intoGroup:gradient
def:NO];
gradient.gradient = [IJSVGRadialGradient parseGradient:element gradient.gradient = [IJSVGRadialGradient parseGradient:element
gradient:gradient]; gradient:gradient];
[parentGroup addDef:gradient]; [parentGroup addDef:gradient];
@@ -1064,8 +1214,9 @@
ignoreAttributes:nil]; ignoreAttributes:nil];
// from base64 // from base64
NSString* string = [element attributeForName:(NSString*)IJSVGAttributeXLink].stringValue; NSXMLNode* attributeNode = [self resolveXLinkAttributeForElement:element] ?:
[image loadFromBase64EncodedString:string]; [element attributeForName:IJSVGAttributeHref];
[image loadFromString:attributeNode.stringValue];
// add to parent // add to parent
[parentGroup addChild:image]; [parentGroup addChild:image];
@@ -1075,6 +1226,17 @@
} }
} }
- (NSXMLNode*)resolveXLinkAttributeForElement:(NSXMLElement*)element
{
NSString* const namespaceURI = @"http://www.w3.org/1999/xlink";
NSXMLNode* attributeNode = [element attributeForLocalName:IJSVGAttributeHref
URI:namespaceURI];
if (attributeNode == nil) {
attributeNode = [element attributeForName:IJSVGAttributeXLink];
}
return attributeNode;
}
- (NSXMLElement*)mergedElement:(NSXMLElement*)element - (NSXMLElement*)mergedElement:(NSXMLElement*)element
withReferenceElement:(NSXMLElement*)reference withReferenceElement:(NSXMLElement*)reference
{ {
@@ -1104,22 +1266,14 @@
#pragma mark Parser stuff! #pragma mark Parser stuff!
- (void)_parsePathCommandData:(NSString*)command - (void)_parsePathCommandDataBuffer:(const char*)buffer
intoPath:(IJSVGPath*)path intoPath:(IJSVGPath*)path
{ {
// invalid command NSUInteger len = strlen(buffer);
if (command == nil || command.length == 0) {
return;
}
// allocate memory for the string buffer for reading
NSUInteger len = command.length;
NSUInteger lastIndex = len - 1; NSUInteger lastIndex = len - 1;
const char* buffer = command.UTF8String;
// make sure we plus 1 for the null byte // make sure we plus 1 for the null byte
char* charBuffer = (char*)malloc(sizeof(char) * (len + 1)); char* charBuffer = (char*)malloc(sizeof(char)*(len + 1));
NSInteger start = 0; NSInteger start = 0;
IJSVGCommand* _currentCommand = nil; IJSVGCommand* _currentCommand = nil;
for (NSInteger i = 0; i < len; i++) { for (NSInteger i = 0; i < len; i++) {
@@ -1130,20 +1284,26 @@
// copy memory from current buffer // copy memory from current buffer
NSInteger index = ((i + 1) - start); NSInteger index = ((i + 1) - start);
memcpy(&charBuffer[0], &buffer[start], sizeof(char) * index); memcpy(&charBuffer[0], &buffer[start], sizeof(char)*index);
charBuffer[index] = '\0'; charBuffer[index] = '\0';
// create the command from the substring // create the command from the substring
NSString* commandString = [NSString stringWithUTF8String:charBuffer]; unsigned long length = index + 1;
size_t mlength = sizeof(char)*length;
char* commandString = (char*)malloc(mlength);
memcpy(commandString, &charBuffer[0], mlength);
// reset start position // reset start position
start = (i + 1); start = (i + 1);
// previous command is actual subcommand // previous command is actual subcommand
IJSVGCommand* previousCommand = _currentCommand.subCommands.lastObject; IJSVGCommand* previousCommand = _currentCommand.subCommands.lastObject;
IJSVGCommand* cCommand = [self _parseCommandString:commandString IJSVGCommand* cCommand = [self _parseCommandStringBuffer:commandString
previousCommand:previousCommand previousCommand:previousCommand
intoPath:path]; intoPath:path];
// free the memory as at this point, we are done with it
(void)free(commandString), commandString = NULL;
// retain the current one // retain the current one
if (cCommand != nil) { if (cCommand != nil) {
@@ -1151,12 +1311,26 @@
} }
} }
} }
free(charBuffer); (void)free(charBuffer), charBuffer = NULL;
} }
- (IJSVGCommand*)_parseCommandString:(NSString*)string - (void)_parsePathCommandData:(NSString*)command
previousCommand:(IJSVGCommand*)previousCommand intoPath:(IJSVGPath*)path
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 // 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 // could be a series of the same commands, so work it out by the number of parameters
@@ -1168,9 +1342,10 @@
// main commands // main commands
// Class commandClass = [IJSVGCommand classFor] // Class commandClass = [IJSVGCommand classFor]
Class commandClass = [IJSVGCommand commandClassForCommandChar:[string characterAtIndex:0]]; Class commandClass = [IJSVGCommand commandClassForCommandChar:buffer[0]];
IJSVGCommand* command = (IJSVGCommand*)[[[commandClass alloc] initWithCommandString:string IJSVGCommand* command = nil;
dataStream:_commandDataStream] autorelease]; command = (IJSVGCommand*)[[[commandClass alloc] initWithCommandStringBuffer:buffer
dataStream:_commandDataStream] autorelease];
for (IJSVGCommand* subCommand in command.subCommands) { for (IJSVGCommand* subCommand in command.subCommands) {
[command.class runWithParams:subCommand.parameters [command.class runWithParams:subCommand.parameters
paramCount:subCommand.parameterCount paramCount:subCommand.parameterCount
@@ -1189,41 +1364,44 @@
// convert a line into a command, // convert a line into a command,
// basically MX1 Y1LX2 Y2 // basically MX1 Y1LX2 Y2
path.primitiveType = kIJSVGPrimitivePathTypeLine; path.primitiveType = kIJSVGPrimitivePathTypeLine;
CGFloat x1 = [element attributeForName:(NSString*)IJSVGAttributeX1].stringValue.floatValue; CGFloat x1 = [element attributeForName:IJSVGAttributeX1].stringValue.floatValue;
CGFloat y1 = [element attributeForName:(NSString*)IJSVGAttributeY1].stringValue.floatValue; CGFloat y1 = [element attributeForName:IJSVGAttributeY1].stringValue.floatValue;
CGFloat x2 = [element attributeForName:(NSString*)IJSVGAttributeX2].stringValue.floatValue; CGFloat x2 = [element attributeForName:IJSVGAttributeX2].stringValue.floatValue;
CGFloat y2 = [element attributeForName:(NSString*)IJSVGAttributeY2].stringValue.floatValue; CGFloat y2 = [element attributeForName:IJSVGAttributeY2].stringValue.floatValue;
// use sprintf as its quicker then stringWithFormat... // use sprintf as its quicker then stringWithFormat...
char buffer[50]; char* buffer;
sprintf(buffer, "M%.2f %.2fL%.2f %.2f", x1, y1, x2, y2); asprintf(&buffer, "M%.2f %.2fL%.2f %.2f", x1, y1, x2, y2);
NSString* command = [NSString stringWithCString:buffer [self _parsePathCommandDataBuffer:buffer
encoding:NSUTF8StringEncoding]; intoPath:path];
[self _parsePathCommandData:command (void)free(buffer);
intoPath:path];
} }
- (void)_parseCircle:(NSXMLElement*)element - (void)_parseCircle:(NSXMLElement*)element
intoPath:(IJSVGPath*)path intoPath:(IJSVGPath*)path
{ {
path.primitiveType = kIJSVGPrimitivePathTypeCircle; path.primitiveType = kIJSVGPrimitivePathTypeCircle;
CGFloat cX = [element attributeForName:(NSString*)IJSVGAttributeCX].stringValue.floatValue; CGFloat cX = [element attributeForName:IJSVGAttributeCX].stringValue.floatValue;
CGFloat cY = [element attributeForName:(NSString*)IJSVGAttributeCY].stringValue.floatValue; CGFloat cY = [element attributeForName:IJSVGAttributeCY].stringValue.floatValue;
CGFloat r = [element attributeForName:(NSString*)IJSVGAttributeR].stringValue.floatValue; CGFloat r = [element attributeForName:IJSVGAttributeR].stringValue.floatValue;
NSRect rect = NSMakeRect(cX - r, cY - r, r * 2, r * 2); CGRect rect = CGRectMake(cX - r, cY - r, r * 2, r * 2);
path.path = [NSBezierPath bezierPathWithOvalInRect:rect]; CGPathRef nPath = CGPathCreateWithEllipseInRect(rect, NULL);
path.path = (CGMutablePathRef)nPath;
CGPathRelease(nPath);
} }
- (void)_parseEllipse:(NSXMLElement*)element - (void)_parseEllipse:(NSXMLElement*)element
intoPath:(IJSVGPath*)path intoPath:(IJSVGPath*)path
{ {
path.primitiveType = kIJSVGPrimitivePathTypeEllipse; path.primitiveType = kIJSVGPrimitivePathTypeEllipse;
CGFloat cX = [element attributeForName:(NSString*)IJSVGAttributeCX].stringValue.floatValue; CGFloat cX = [element attributeForName:IJSVGAttributeCX].stringValue.floatValue;
CGFloat cY = [element attributeForName:(NSString*)IJSVGAttributeCY].stringValue.floatValue; CGFloat cY = [element attributeForName:IJSVGAttributeCY].stringValue.floatValue;
CGFloat rX = [element attributeForName:(NSString*)IJSVGAttributeRX].stringValue.floatValue; CGFloat rX = [element attributeForName:IJSVGAttributeRX].stringValue.floatValue;
CGFloat rY = [element attributeForName:(NSString*)IJSVGAttributeRY].stringValue.floatValue; CGFloat rY = [element attributeForName:IJSVGAttributeRY].stringValue.floatValue;
NSRect rect = NSMakeRect(cX - rX, cY - rY, rX * 2, rY * 2); NSRect rect = NSMakeRect(cX - rX, cY - rY, rX * 2, rY * 2);
path.path = [NSBezierPath bezierPathWithOvalInRect:rect]; CGPathRef nPath = CGPathCreateWithEllipseInRect(rect, NULL);
path.path = (CGMutablePathRef)nPath;
CGPathRelease(nPath);
} }
- (void)_parsePolyline:(NSXMLElement*)element - (void)_parsePolyline:(NSXMLElement*)element
@@ -1248,7 +1426,7 @@
intoPath:(IJSVGPath*)path intoPath:(IJSVGPath*)path
closePath:(BOOL)closePath closePath:(BOOL)closePath
{ {
NSString* points = [element attributeForName:(NSString*)IJSVGAttributePoints].stringValue; NSString* points = [element attributeForName:IJSVGAttributePoints].stringValue;
NSInteger count = 0; NSInteger count = 0;
CGFloat* params = [IJSVGUtils commandParameters:points CGFloat* params = [IJSVGUtils commandParameters:points
count:&count]; count:&count];
@@ -1259,22 +1437,48 @@
return; return;
} }
// construct a command const int defBufferSize = 10;
NSInteger capacity = count / 2; char* buffer;
if (closePath == YES) { asprintf(&buffer, "M%f %f L", params[0], params[1]);
capacity += 1;
// compute a default buffer - bSize is strlen + 1 for null byte
size_t bSize = strlen(buffer) + 1;
size_t strLength = bSize - 1;
// 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 - note, we always
// plus 2 incase the close path needs to be appended on the end
if((strLength + sSize + 2) > bSize) {
size_t nLength = MAX(sSize, defBufferSize) + 2;
buffer = realloc(buffer, sizeof(char)*(bSize+nLength));
bSize += nLength;
}
// append the 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]]; // append the close path if required
for (NSInteger i = 2; i < count; i += 2) { if(closePath == YES) {
[str appendFormat:@"%f,%f ", params[i], params[i + 1]]; strcat(buffer, "z");
} }
if (closePath) {
[str appendString:@"z"]; // actually perform the parse
} [self _parsePathCommandDataBuffer:buffer
[self _parsePathCommandData:str intoPath:path];
intoPath:path];
free(params); // free the params
(void)free(buffer), buffer = NULL;
(void)free(params), params = NULL;
} }
- (void)_parseRect:(NSXMLElement*)element - (void)_parseRect:(NSXMLElement*)element
@@ -1282,27 +1486,28 @@
{ {
path.primitiveType = kIJSVGPrimitivePathTypeRect; path.primitiveType = kIJSVGPrimitivePathTypeRect;
// width and height // width and height
CGFloat width = [IJSVGUtils floatValue:[element attributeForName:(NSString*)IJSVGAttributeWidth].stringValue CGFloat width = [IJSVGUtils floatValue:[element attributeForName:IJSVGAttributeWidth].stringValue
fallBackForPercent:self.viewBox.size.width]; fallBackForPercent:self.viewBox.size.width];
CGFloat height = [IJSVGUtils floatValue:[element attributeForName:(NSString*)IJSVGAttributeHeight].stringValue CGFloat height = [IJSVGUtils floatValue:[element attributeForName:IJSVGAttributeHeight].stringValue
fallBackForPercent:self.viewBox.size.height]; fallBackForPercent:self.viewBox.size.height];
// rect uses x and y as start of path, not move path object -_- // rect uses x and y as start of path, not move path object -_-
CGFloat x = [IJSVGUtils floatValue:[element attributeForName:(NSString*)IJSVGAttributeX].stringValue CGFloat x = [IJSVGUtils floatValue:[element attributeForName:IJSVGAttributeX].stringValue
fallBackForPercent:self.viewBox.size.width]; fallBackForPercent:self.viewBox.size.width];
CGFloat y = [IJSVGUtils floatValue:[element attributeForName:(NSString*)IJSVGAttributeY].stringValue CGFloat y = [IJSVGUtils floatValue:[element attributeForName:IJSVGAttributeY].stringValue
fallBackForPercent:self.viewBox.size.height]; fallBackForPercent:self.viewBox.size.height];
// radius // radius
CGFloat rX = [element attributeForName:(NSString*)IJSVGAttributeRX].stringValue.floatValue; CGFloat rX = [element attributeForName:IJSVGAttributeRX].stringValue.floatValue;
CGFloat rY = [element attributeForName:(NSString*)IJSVGAttributeRY].stringValue.floatValue; CGFloat rY = [element attributeForName:IJSVGAttributeRY].stringValue.floatValue;
if ([element attributeForName:(NSString*)IJSVGAttributeRY] == nil) { if ([element attributeForName:IJSVGAttributeRY] == nil) {
rY = rX; rY = rX;
} }
path.path = [NSBezierPath bezierPathWithRoundedRect:NSMakeRect(x, y, width, height) CGRect rect = CGRectMake(x, y, width, height);
xRadius:rX CGPathRef nPath = CGPathCreateWithRoundedRect(rect, rX, rY, NULL);
yRadius:rY]; path.path = (CGMutablePathRef)nPath;
CGPathRelease(nPath);
} }
@end @end
@@ -6,8 +6,8 @@
// Copyright © 2016 Curtis Hard. All rights reserved. // Copyright © 2016 Curtis Hard. All rights reserved.
// //
#import "IJSVGNode.h" #import <IJSVG/IJSVGNode.h>
#import "IJSVGRenderingStyle.h" #import <IJSVG/IJSVGRenderingStyle.h>
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
@class IJSVGLayer; @class IJSVGLayer;
@@ -26,7 +26,6 @@
@implementation IJSVGLayerTree @implementation IJSVGLayerTree
@synthesize viewBox;
@synthesize style = _style; @synthesize style = _style;
- (void)dealloc - (void)dealloc
@@ -74,59 +73,40 @@
{ {
// any x and y? // any x and y?
CGFloat x = [node.x computeValue:layer.frame.size.width]; CGRect frame = layer.bounds;
CGFloat y = [node.y computeValue:layer.frame.size.height]; CGFloat x = [node.x computeValue:frame.size.width];
CGFloat y = [node.y computeValue:frame.size.height];
// do some magic transform // no need to do anything if no transform, or x or y == 0
if (transforms.count == 0 && x == 0.f && y == 0.f) { if (transforms.count == 0 && x == 0.f && y == 0.f) {
return layer; return layer;
} }
// simply cascade all the transforms onto the identity
CGAffineTransform identity = CGAffineTransformIdentity;
if (x != 0.f || y != 0.f) { if (x != 0.f || y != 0.f) {
// we must add translate to the stack identity = CGAffineTransformTranslate(identity, x, y);
NSMutableArray* trans = nil;
if (transforms != nil) {
trans = [[transforms mutableCopy] autorelease];
} else {
trans = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
}
[trans addObject:[IJSVGTransform transformByTranslatingX:x y:y]];
transforms = trans;
} }
// add any transforms // this used to be done with each transform being added to its own
IJSVGLayer* topLayer = nil; // group layer, but we can simply use one and then apply
IJSVGLayer* parentLayer = nil; // the transforms in reverse order, has same outcome with less memory
IJSVGGroupLayer* parentLayer = [[[IJSVGGroupLayer alloc] init] autorelease];
for (IJSVGTransform* transform in transforms) { for(IJSVGTransform* transform in transforms.reverseObjectEnumerator) {
// make sure we apply the transform to the parent identity = CGAffineTransformConcat(identity, transform.CGAffineTransform);
// so they stack
IJSVGGroupLayer* childLayer = [[[IJSVGGroupLayer alloc] init] autorelease];
childLayer.affineTransform = transform.CGAffineTransform;
// add it to the parent layer
if (parentLayer != nil) {
[parentLayer addSublayer:childLayer];
} else {
// make sure we keep track of the top most layer
topLayer = childLayer;
}
// reset parent layer to the new child
parentLayer = childLayer;
} }
parentLayer.affineTransform = identity;
// swap the layer around
[parentLayer addSublayer:layer]; [parentLayer addSublayer:layer];
layer = topLayer; return parentLayer;
return layer;
} }
- (void)applyDefaultsToLayer:(IJSVGLayer*)layer - (void)applyDefaultsToLayer:(IJSVGLayer*)layer
fromNode:(IJSVGNode*)node fromNode:(IJSVGNode*)node
{ {
CGFloat opacity = node.opacity.value; CGFloat opacity = node.opacity.value;
layer.opacity = opacity; if(opacity != 1.f) {
layer.opacity = opacity;
}
// setup the blending mode // setup the blending mode
if (node.blendMode != IJSVGBlendModeNormal) { if (node.blendMode != IJSVGBlendModeNormal) {
@@ -164,14 +144,15 @@
} }
IJSVGGroupLayer* groupLayer = [[[IJSVGGroupLayer alloc] init] autorelease]; IJSVGGroupLayer* groupLayer = [[[IJSVGGroupLayer alloc] init] autorelease];
for (IJSVGNode* node in group.children) { for (IJSVGNode* node in group.childNodes) {
[groupLayer addSublayer:[self layerForNode:node]]; [groupLayer addSublayer:[self layerForNode:node]];
} }
groupLayer.frame = (CGRect){ groupLayer.frame = (CGRect) {
.origin = CGPointZero, .origin = CGPointZero,
.size = (CGSize){ .size = (CGSize) {
.width = group.width.value, .width = group.width.value,
.height = group.height.value } .height = group.height.value
}
}; };
// mask it - forgot groups can have masks too, doh! simple // mask it - forgot groups can have masks too, doh! simple
@@ -204,7 +185,7 @@
// setup path and layer // setup path and layer
IJSVGShapeLayer* layer = [[[IJSVGShapeLayer alloc] init] autorelease]; IJSVGShapeLayer* layer = [[[IJSVGShapeLayer alloc] init] autorelease];
layer.primitiveType = path.primitiveType; layer.primitiveType = path.primitiveType;
CGPathRef introPath = path.CGPath; CGPathRef introPath = path.path;
*originalBoundingBox = CGRectIntegral(CGPathGetBoundingBox(introPath)); *originalBoundingBox = CGRectIntegral(CGPathGetBoundingBox(introPath));
layer.originalPathOrigin = (*originalBoundingBox).origin; layer.originalPathOrigin = (*originalBoundingBox).origin;
@@ -299,9 +280,14 @@
NSColor* fColor = path.fillColor; NSColor* fColor = path.fillColor;
BOOL hasColor = (fColor.alphaComponent == 0.f || fColor == nil) == NO; BOOL hasColor = (fColor.alphaComponent == 0.f || fColor == nil) == NO;
BOOL hasFill = path.fillPattern != nil || path.fillGradient != nil; BOOL hasFill = path.fillPattern != nil || path.fillGradient != nil;
// is there an overriding style in the sheet?
if (_style.fillColor && (hasFill || hasColor || fColor == nil)) { if (_style.fillColor && (hasFill || hasColor || fColor == nil)) {
fColor = _style.fillColor; fColor = _style.fillColor;
} else if (fColor != nil && path.fillOpacity.value != 1.f) { }
// if there is a color, change the opacity if required
if (fColor != nil && path.fillOpacity.value != 1.f) {
fColor = [IJSVGColor changeAlphaOnColor:fColor fColor = [IJSVGColor changeAlphaOnColor:fColor
to:path.fillOpacity.value]; to:path.fillOpacity.value];
} }
@@ -442,7 +428,7 @@
// the gradient drawing layer // the gradient drawing layer
gradient.colorList = _style.colorList; gradient.colorList = _style.colorList;
IJSVGGradientLayer* gradLayer = [[[IJSVGGradientLayer alloc] init] autorelease]; IJSVGGradientLayer* gradLayer = [[[IJSVGGradientLayer alloc] init] autorelease];
gradLayer.viewBox = self.viewBox; gradLayer.viewBox = _viewBox;
gradLayer.frame = layer.bounds; gradLayer.frame = layer.bounds;
gradLayer.gradient = gradient; gradLayer.gradient = gradient;
gradLayer.absoluteTransform = [self absoluteTransform:path]; gradLayer.absoluteTransform = [self absoluteTransform:path];
@@ -603,7 +589,9 @@
// dashing // dashing
strokeLayer.lineDashPhase = path.strokeDashOffset.value; strokeLayer.lineDashPhase = path.strokeDashOffset.value;
strokeLayer.lineDashPattern = [self lineDashPattern:path]; if(path.strokeDashArrayCount != 0.f) {
strokeLayer.lineDashPattern = [self lineDashPattern:path];
}
return strokeLayer; return strokeLayer;
} }
@@ -616,15 +604,13 @@
IJSVGGroupLayer* maskLayer = [[[IJSVGGroupLayer alloc] init] autorelease]; IJSVGGroupLayer* maskLayer = [[[IJSVGGroupLayer alloc] init] autorelease];
// add clip mask // add clip mask
if (node.clipPath != nil) { if (node.clipPath != nil && node.clipPath.overflowVisibility == IJSVGOverflowVisibilityHidden) {
IJSVGLayer* clip = [self layerForNode:node.clipPath]; IJSVGLayer* clip = [self layerForNode:node.clipPath];
// adjust the frame // adjust the frame
if (node.clipPath.units == IJSVGUnitObjectBoundingBox) { if (node.clipPath.units == IJSVGUnitObjectBoundingBox) {
[self adjustLayer:clip [self adjustLayer:clip
toParentLayerFrame:layer]; toParentLayerFrame:layer];
} else {
clip.affineTransform = [self absoluteTransform:node];
} }
// add the layer // add the layer
@@ -632,27 +618,27 @@
} }
// add the actual mask // add the actual mask
if (node.mask != nil) { if (node.mask != nil && node.mask.overflowVisibility == IJSVGOverflowVisibilityHidden) {
IJSVGLayer* mask = [self layerForNode:node.mask]; IJSVGLayer* mask = [self layerForNode:node.mask];
// only move if bounding box // only move if bounding box
if (node.mask.units == IJSVGUnitObjectBoundingBox) { if (node.mask.units == IJSVGUnitObjectBoundingBox) {
[self adjustLayer:mask [self adjustLayer:mask
toParentLayerFrame:layer]; toParentLayerFrame:layer];
} else {
mask.affineTransform = [self absoluteTransform:node];
} }
// add the layer // add the layer
[maskLayer addSublayer:mask]; [maskLayer addSublayer:mask];
} }
// recursive colourize for each item
[self _recursiveColorLayersFromLayer:maskLayer
withColor:[IJSVGColor computeColorSpace:NSColor.whiteColor].CGColor];
// add the mask // add the mask
layer.mask = maskLayer; if(maskLayer.sublayers.count != 0) {
// recursive colourize for each item
NSColor* color = [IJSVGColor computeColorSpace:NSColor.whiteColor];
[self _recursiveColorLayersFromLayer:maskLayer
withColor:color.CGColor];
layer.mask = maskLayer;
}
} }
} }
@@ -692,54 +678,54 @@
- (NSArray<NSNumber*>*)lineDashPattern:(IJSVGNode*)node - (NSArray<NSNumber*>*)lineDashPattern:(IJSVGNode*)node
{ {
NSMutableArray* arr = [[[NSMutableArray alloc] init] autorelease]; NSMutableArray* arr = [[[NSMutableArray alloc] initWithCapacity:node.strokeDashArrayCount] autorelease];
for (NSInteger i = 0; i < node.strokeDashArrayCount; i++) { for (NSInteger i = 0; i < node.strokeDashArrayCount; i++) {
[arr addObject:@((CGFloat)node.strokeDashArray[i])]; [arr addObject:@((CGFloat)node.strokeDashArray[i])];
} }
return [[arr copy] autorelease]; return arr;
} }
- (NSString*)lineJoin:(IJSVGLineJoinStyle)joinStyle - (NSString*)lineJoin:(IJSVGLineJoinStyle)joinStyle
{ {
switch (joinStyle) { switch (joinStyle) {
default: default:
case IJSVGLineJoinStyleMiter: { case IJSVGLineJoinStyleMiter: {
return kCALineJoinMiter; return kCALineJoinMiter;
} }
case IJSVGLineJoinStyleBevel: { case IJSVGLineJoinStyleBevel: {
return kCALineJoinBevel; return kCALineJoinBevel;
} }
case IJSVGLineJoinStyleRound: { case IJSVGLineJoinStyleRound: {
return kCALineJoinRound; return kCALineJoinRound;
} }
} }
} }
- (NSString*)lineCap:(IJSVGLineCapStyle)capStyle - (NSString*)lineCap:(IJSVGLineCapStyle)capStyle
{ {
switch (capStyle) { switch (capStyle) {
default: default:
case IJSVGLineCapStyleButt: { case IJSVGLineCapStyleButt: {
return kCALineCapButt; return kCALineCapButt;
} }
case IJSVGLineCapStyleRound: { case IJSVGLineCapStyleRound: {
return kCALineCapRound; return kCALineCapRound;
} }
case IJSVGLineCapStyleSquare: { case IJSVGLineCapStyleSquare: {
return kCALineCapSquare; return kCALineCapSquare;
} }
} }
} }
- (NSString*)fillRule:(IJSVGWindingRule)rule - (NSString*)fillRule:(IJSVGWindingRule)rule
{ {
switch (rule) { switch (rule) {
case IJSVGWindingRuleEvenOdd: { case IJSVGWindingRuleEvenOdd: {
return kCAFillRuleEvenOdd; return kCAFillRuleEvenOdd;
} }
default: { default: {
return kCAFillRuleNonZero; return kCAFillRuleNonZero;
} }
} }
} }
@@ -6,8 +6,8 @@
// Copyright © 2019 Curtis Hard. All rights reserved. // Copyright © 2019 Curtis Hard. All rights reserved.
// //
#import "IJSVGColorList.h" #import <IJSVG/IJSVGColorList.h>
#import "IJSVGNode.h" #import <IJSVG/IJSVGNode.h>
#import <AppKit/AppKit.h> #import <AppKit/AppKit.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <objc/runtime.h> #import <objc/runtime.h>
@@ -10,13 +10,6 @@
@implementation IJSVGRenderingStyle @implementation IJSVGRenderingStyle
@synthesize colorList = _colorList;
@synthesize lineCapStyle = _lineCapStyle;
@synthesize lineJoinStyle = _lineJoinStyle;
@synthesize lineWidth = _lineWidth;
@synthesize fillColor = _fillColor;
@synthesize strokeColor = _strokeColor;
- (void)dealloc - (void)dealloc
{ {
(void)([_fillColor release]), _fillColor = nil; (void)([_fillColor release]), _fillColor = nil;
@@ -6,7 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved. // Copyright (c) 2014 Curtis Hard. All rights reserved.
// //
#import "IJSVGColor.h" #import <IJSVG/IJSVGColor.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@interface IJSVGStyle : NSObject { @interface IJSVGStyle : NSObject {
@@ -91,7 +91,7 @@
+ (NSArray*)allowedColourKeys + (NSArray*)allowedColourKeys
{ {
return @[ @"fill", @"stroke-colour", @"stop-color", @"stroke" ]; return @[ @"fill", @"stroke-color", @"stop-color", @"stroke" ];
} }
- (void)setProperties:(NSDictionary*)properties - (void)setProperties:(NSDictionary*)properties
@@ -6,8 +6,8 @@
// Copyright © 2016 Curtis Hard. All rights reserved. // Copyright © 2016 Curtis Hard. All rights reserved.
// //
#import "IJSVGStyleSheetRule.h" #import <IJSVG/IJSVGStyleSheetRule.h>
#import "IJSVGStyleSheetSelector.h" #import <IJSVG/IJSVGStyleSheetSelector.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@class IJSVGNode; @class IJSVGNode;
@@ -11,9 +11,6 @@
#import "IJSVGStyleSheet.h" #import "IJSVGStyleSheet.h"
@interface IJSVGStyleSheetSelectorListItem : NSObject { @interface IJSVGStyleSheetSelectorListItem : NSObject {
IJSVGStyleSheetSelector* selector;
IJSVGStyleSheetRule* rule;
} }
@property (nonatomic, retain) IJSVGStyleSheetRule* rule; @property (nonatomic, retain) IJSVGStyleSheetRule* rule;
@@ -23,12 +20,11 @@
@implementation IJSVGStyleSheetSelectorListItem @implementation IJSVGStyleSheetSelectorListItem
@synthesize rule, selector;
- (void)dealloc - (void)dealloc
{ {
(void)([rule release]), rule = nil; (void)([_rule release]), _rule = nil;
(void)([selector release]), selector = nil; (void)([_selector release]), _selector = nil;
[super dealloc]; [super dealloc];
} }
@@ -6,16 +6,13 @@
// Copyright © 2016 Curtis Hard. All rights reserved. // Copyright © 2016 Curtis Hard. All rights reserved.
// //
#import "IJSVGStyle.h" #import <IJSVG/IJSVGStyle.h>
#import "IJSVGStyleSheetSelector.h" #import <IJSVG/IJSVGStyleSheetSelector.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@class IJSVGNode; @class IJSVGNode;
@interface IJSVGStyleSheetRule : NSObject { @interface IJSVGStyleSheetRule : NSObject {
NSArray* selectors;
IJSVGStyle* style;
} }
@property (nonatomic, retain) NSArray* selectors; @property (nonatomic, retain) NSArray* selectors;
@@ -10,12 +10,10 @@
@implementation IJSVGStyleSheetRule @implementation IJSVGStyleSheetRule
@synthesize selectors, style;
- (void)dealloc - (void)dealloc
{ {
(void)([selectors release]), selectors = nil; (void)([_selectors release]), _selectors = nil;
(void)([style release]), style = nil; (void)([_style release]), _style = nil;
[super dealloc]; [super dealloc];
} }
@@ -24,7 +22,7 @@
{ {
// interate over each select and work out if // interate over each select and work out if
// it allows us to be applied // it allows us to be applied
for (IJSVGStyleSheetSelector* selector in selectors) { for (IJSVGStyleSheetSelector* selector in _selectors) {
if ([selector matchesNode:node]) { if ([selector matchesNode:node]) {
*matchedSelector = selector; *matchedSelector = selector;
return YES; return YES;
@@ -6,7 +6,7 @@
// Copyright © 2016 Curtis Hard. All rights reserved. // Copyright © 2016 Curtis Hard. All rights reserved.
// //
#import "IJSVGStyleSheetSelectorRaw.h" #import <IJSVG/IJSVGStyleSheetSelectorRaw.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@class IJSVGNode; @class IJSVGNode;
@@ -14,7 +14,7 @@
@interface IJSVGStyleSheetSelector : NSObject { @interface IJSVGStyleSheetSelector : NSObject {
NSString* selector; NSString* selector;
NSUInteger specificity;
@private @private
NSMutableArray* _rawSelectors; NSMutableArray* _rawSelectors;
} }
@@ -16,8 +16,6 @@
#define SPECIFICITY_CLASS 10 #define SPECIFICITY_CLASS 10
#define SPECIFICITY_IDENTIFIER 100 #define SPECIFICITY_IDENTIFIER 100
@synthesize specificity;
BOOL IJSVGStyleSheetIsSiblingCombinator(IJSVGStyleSheetSelectorCombinator combinator) BOOL IJSVGStyleSheetIsSiblingCombinator(IJSVGStyleSheetSelectorCombinator combinator)
{ {
return combinator == IJSVGStyleSheetSelectorCombinatorNextSibling || return combinator == IJSVGStyleSheetSelectorCombinatorNextSibling ||
@@ -43,11 +41,11 @@ IJSVGNode * IJSVGStyleSheetPreviousNode(IJSVGNode * node)
IJSVGGroup * group = (IJSVGGroup *)node.parentNode; IJSVGGroup * group = (IJSVGGroup *)node.parentNode;
if([group isKindOfClass:[IJSVGGroup class]] == NO) if([group isKindOfClass:[IJSVGGroup class]] == NO)
return nil; return nil;
NSInteger currentIndex = [group.children indexOfObject:node]; NSInteger currentIndex = [group.childNodes indexOfObject:node];
if(currentIndex == 0) { if(currentIndex == 0) {
return nil; return nil;
} }
return group.children[currentIndex-1]; return group.childNodes[currentIndex-1];
}; };
IJSVGNode * IJSVGStyleSheetNextNode(IJSVGNode * node) IJSVGNode * IJSVGStyleSheetNextNode(IJSVGNode * node)
@@ -56,11 +54,11 @@ IJSVGNode * IJSVGStyleSheetNextNode(IJSVGNode * node)
if([group isKindOfClass:[IJSVGGroup class]] == NO) { if([group isKindOfClass:[IJSVGGroup class]] == NO) {
return nil; return nil;
} }
NSInteger currentIndex = [group.children indexOfObject:node]; NSInteger currentIndex = [group.childNodes indexOfObject:node];
if(currentIndex == group.children.count-1) { if(currentIndex == group.childNodes.count-1) {
return nil; return nil;
} }
return group.children[currentIndex+1]; return group.childNodes[currentIndex+1];
}; };
IJSVGStyleSheetSelectorRaw * IJSVGStyleSheetPreviousSelector(IJSVGStyleSheetSelectorRaw * aSelector, NSArray * _rawSelectors) IJSVGStyleSheetSelectorRaw * IJSVGStyleSheetPreviousSelector(IJSVGStyleSheetSelectorRaw * aSelector, NSArray * _rawSelectors)
@@ -149,7 +147,7 @@ BOOL IJSVGStyleSheetMatchSelector(IJSVGNode * node, IJSVGStyleSheetSelectorRaw *
} }
// grab the children // grab the children
NSArray * nodes = parentNode.children; NSArray * nodes = parentNode.childNodes;
NSInteger index = [nodes indexOfObject:aNode]; NSInteger index = [nodes indexOfObject:aNode];
// doesnt contain the child // doesnt contain the child
@@ -228,7 +226,7 @@ BOOL IJSVGStyleSheetMatchSelector(IJSVGNode * node, IJSVGStyleSheetSelectorRaw *
// matches the next selector and... contains the node in question // matches the next selector and... contains the node in question
IJSVGStyleSheetSelectorRaw * s = IJSVGStyleSheetNextSelector(aSelector,_rawSelectors); IJSVGStyleSheetSelectorRaw * s = IJSVGStyleSheetNextSelector(aSelector,_rawSelectors);
if(IJSVGStyleSheetMatchSelector(parentNode, s) && if(IJSVGStyleSheetMatchSelector(parentNode, s) &&
[parentNode.children containsObject:aNode]) { [parentNode.childNodes containsObject:aNode]) {
// set the new starting selector and node // set the new starting selector and node
aSelector = s; aSelector = s;
aNode = parentNode; aNode = parentNode;
@@ -276,15 +274,16 @@ BOOL IJSVGStyleSheetMatchSelector(IJSVGNode * node, IJSVGStyleSheetSelectorRaw *
// 1 for a tag // 1 for a tag
if(rawSelector.tag != nil) { if(rawSelector.tag != nil) {
self.specificity += SPECIFICITY_TAG; _specificity += SPECIFICITY_TAG;
} }
// 100 for a id // 100 for a id
if(rawSelector.identifier != nil) if(rawSelector.identifier != nil) {
self.specificity += SPECIFICITY_IDENTIFIER; _specificity += SPECIFICITY_IDENTIFIER;
}
// 10 for a class // 10 for a class
self.specificity += (rawSelector.classes.count*SPECIFICITY_CLASS); _specificity += (rawSelector.classes.count*SPECIFICITY_CLASS);
} }
} }
@@ -19,13 +19,8 @@ typedef NS_ENUM(NSUInteger, IJSVGStyleSheetSelectorCombinator) {
@interface IJSVGStyleSheetSelectorRaw : NSObject { @interface IJSVGStyleSheetSelectorRaw : NSObject {
NSString* tag; @private
NSString* identifier;
NSMutableArray* classes; NSMutableArray* classes;
IJSVGStyleSheetSelectorCombinator combinator;
NSString* combinatorString;
} }
@property (nonatomic, copy) NSString* tag; @property (nonatomic, copy) NSString* tag;
@@ -10,14 +10,14 @@
@implementation IJSVGStyleSheetSelectorRaw @implementation IJSVGStyleSheetSelectorRaw
@synthesize classes, identifier, tag, combinator, combinatorString; @synthesize classes;
- (void)dealloc - (void)dealloc
{ {
(void)([classes release]), classes = nil; (void)([classes release]), classes = nil;
(void)([identifier release]), identifier = nil; (void)([_identifier release]), _identifier = nil;
(void)([tag release]), tag = nil; (void)([_tag release]), _tag = nil;
(void)([combinatorString release]), combinatorString = nil; (void)([_combinatorString release]), _combinatorString = nil;
[super dealloc]; [super dealloc];
} }
@@ -25,8 +25,8 @@
{ {
if ((self = [super init]) != nil) { if ((self = [super init]) != nil) {
classes = [[NSMutableArray alloc] init]; classes = [[NSMutableArray alloc] init];
combinator = IJSVGStyleSheetSelectorCombinatorDescendant; _combinator = IJSVGStyleSheetSelectorCombinatorDescendant;
combinatorString = @" "; _combinatorString = @" ";
} }
return self; return self;
} }
@@ -38,7 +38,8 @@
- (NSString*)description - (NSString*)description
{ {
return [NSString stringWithFormat:@"Combinator: %@, Tag: %@, Classes: %@, Identifier: %@", combinatorString, tag, classes, identifier]; return [NSString stringWithFormat:@"Combinator: %@, Tag: %@, Classes: %@, Identifier: %@",
_combinatorString, _tag, classes, _identifier];
} }
@end @end
@@ -6,7 +6,7 @@
// Copyright © 2017 Curtis Hard. All rights reserved. // Copyright © 2017 Curtis Hard. All rights reserved.
// //
#import "IJSVGUnitLength.h" #import <IJSVG/IJSVGUnitLength.h>
@interface IJSVGGradientUnitLength : IJSVGUnitLength @interface IJSVGGradientUnitLength : IJSVGUnitLength
@@ -7,6 +7,7 @@
// //
#import "IJSVGMath.h" #import "IJSVGMath.h"
#import "IJSVGCommandParser.h"
@implementation IJSVGMath @implementation IJSVGMath
@@ -0,0 +1,25 @@
//
// IJSVGParsing.h
// IJSVG
//
// Created by Curtis Hard on 04/02/2021.
// Copyright © 2021 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef struct {
char* name;
char* parameters;
} IJSVGParsingStringMethod;
IJSVGParsingStringMethod* IJSVGParsingStringMethodCreate(void);
void IJSVGParsingStringMethodRelease(IJSVGParsingStringMethod* stringMethod);
IJSVGParsingStringMethod** IJSVGParsingMethodParseString(const char* string,
NSUInteger* count);
void IJSVGParsingStringMethodsRelease(IJSVGParsingStringMethod** methods,
NSUInteger count);
@interface IJSVGParsing : NSObject
@end
@@ -0,0 +1,127 @@
//
// IJSVGParsing.m
// IJSVG
//
// Created by Curtis Hard on 04/02/2021.
// Copyright © 2021 Curtis Hard. All rights reserved.
//
#import "IJSVGParsing.h"
#import "IJSVGUtils.h"
IJSVGParsingStringMethod* IJSVGParsingStringMethodCreate(void)
{
IJSVGParsingStringMethod* method = (IJSVGParsingStringMethod*)malloc(sizeof(IJSVGParsingStringMethod));
method->name = NULL;
method->parameters = NULL;
return method;
}
void IJSVGParsingStringMethodRelease(IJSVGParsingStringMethod* stringMethod)
{
if(stringMethod->name != NULL) {
(void)free(stringMethod->name), stringMethod->name = NULL;
}
if(stringMethod->parameters != NULL) {
(void)free(stringMethod->parameters), stringMethod->parameters = NULL;
}
if(stringMethod != NULL) {
(void)free(stringMethod), stringMethod = NULL;
}
}
void IJSVGParsingStringMethodsRelease(IJSVGParsingStringMethod** methods,
NSUInteger count)
{
for(int i = 0; i < count; i++) {
IJSVGParsingStringMethodRelease(methods[i]);
}
(void)free(methods), methods = NULL;
}
IJSVGParsingStringMethod** IJSVGParsingMethodParseString(const char* string,
NSUInteger* count)
{
const char* charString = string;
unsigned long length = strlen(string);
char* buffer = (char*)calloc(sizeof(char), length);
char* originBuffer = buffer;
int bufferIndex = 0;
const size_t defBufferSize = 5;
size_t currentBufferSize = defBufferSize;
NSUInteger methodCount = 0;
IJSVGParsingStringMethod* method = NULL;
IJSVGParsingStringMethod** methods = NULL;
methods = (IJSVGParsingStringMethod**)malloc(sizeof(IJSVGParsingStringMethod*)*currentBufferSize);
// each command requires a name and parameters, store for later use
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;
//write here
if(method == NULL) {
method = IJSVGParsingStringMethodCreate();
method->name = (char*)calloc(sizeof(char),bufferIndex+1);
memcpy(method->name, buffer, sizeof(char)*bufferIndex);
IJSVGTrimCharBuffer(method->name);
}
// 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;
// there has to be a method at this point, if not, something is wrong
// in the syntax
if(method != NULL) {
method->parameters = (char*)calloc(sizeof(char),bufferIndex+1);
memcpy(method->parameters, buffer, sizeof(char)*bufferIndex);
IJSVGTrimCharBuffer(method->parameters);
// now we can add
if(methodCount + 1 > currentBufferSize) {
currentBufferSize += defBufferSize;
*methods = *(IJSVGParsingStringMethod**)realloc(methods, sizeof(IJSVGParsingStringMethod*)*currentBufferSize);
}
methods[methodCount++] = method;
method = NULL;
}
// write null up until the limit we reached
memset(buffer, '\0', bufferIndex);
bufferIndex = 0;
continue;
}
// increment the buffer count
*buffer++ = currentChar;
bufferIndex++;
}
// left over
if(method != NULL) {
(void)IJSVGParsingStringMethodRelease(method), method = NULL;
}
buffer = originBuffer;
*count = methodCount;
(void)free(buffer), buffer = NULL;
return methods;
}
@implementation IJSVGParsing
@end
@@ -10,5 +10,5 @@
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
BOOL IJSVGIsMainThread(void); BOOL IJSVGIsMainThread(void);
void IJSVGBeginTransactionLock(void); BOOL IJSVGBeginTransaction(void);
void IJSVGEndTransactionLock(void); void IJSVGEndTransaction(void);
@@ -7,16 +7,25 @@
// //
#import "IJSVGTransaction.h" #import "IJSVGTransaction.h"
#import <AppKit/AppKit.h>
BOOL IJSVGIsMainThread(void) { return NSThread.isMainThread; }; BOOL IJSVGIsMainThread(void) { return NSThread.isMainThread; };
void IJSVGBeginTransactionLock(void) BOOL IJSVGBeginTransaction(void)
{ {
if(IJSVGIsMainThread() == YES) {
return NO;
}
// use nsanimationcontext as this sets a private flag of 0x4
// of the catransaction for background composites
[CATransaction begin]; [CATransaction begin];
[CATransaction setDisableActions:YES]; [CATransaction setDisableActions:YES];
[CATransaction lock];
return YES;
}; };
void IJSVGEndTransactionLock(void) void IJSVGEndTransaction(void)
{ {
[CATransaction unlock];
[CATransaction commit]; [CATransaction commit];
}; };
@@ -6,7 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved. // Copyright (c) 2014 Curtis Hard. All rights reserved.
// //
#import "IJSVGUtils.h" #import <IJSVG/IJSVGUtils.h>
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@class IJSVGTransform; @class IJSVGTransform;
@@ -14,7 +14,7 @@
typedef CGFloat (^IJSVGTransformParameterModifier)(NSInteger index, CGFloat value); typedef CGFloat (^IJSVGTransformParameterModifier)(NSInteger index, CGFloat value);
typedef void (^IJSVGTransformApplyBlock)(IJSVGTransform* transform); typedef void (^IJSVGTransformApplyBlock)(IJSVGTransform* transform);
typedef NS_OPTIONS(NSInteger, IJSVGTransformCommand) { typedef NS_ENUM(NSInteger, IJSVGTransformCommand) {
IJSVGTransformCommandMatrix, IJSVGTransformCommandMatrix,
IJSVGTransformCommandTranslate, IJSVGTransformCommandTranslate,
IJSVGTransformCommandTranslateX, IJSVGTransformCommandTranslateX,
@@ -27,11 +27,6 @@ typedef NS_OPTIONS(NSInteger, IJSVGTransformCommand) {
}; };
@interface IJSVGTransform : NSObject { @interface IJSVGTransform : NSObject {
IJSVGTransformCommand command;
CGFloat* parameters;
NSInteger parameterCount;
NSInteger sort;
} }
@property (nonatomic, assign) IJSVGTransformCommand command; @property (nonatomic, assign) IJSVGTransformCommand command;
@@ -43,10 +38,15 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
CGAffineTransform IJSVGConcatTransforms(NSArray<IJSVGTransform*>* transforms); CGAffineTransform IJSVGConcatTransforms(NSArray<IJSVGTransform*>* transforms);
NSString* IJSVGTransformAttributeString(CGAffineTransform transform); NSString* IJSVGTransformAttributeString(CGAffineTransform transform);
+ (NSArray<NSDictionary*>*)affineTransformToSVGTransformComponents:(CGAffineTransform)transform;
+ (NSString*)affineTransformToSVGTransformComponentString:(CGAffineTransform)transform
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (NSString*)affineTransformToSVGTransformComponentString:(CGAffineTransform)transform;
+ (NSArray<IJSVGTransform*>*)transformsFromAffineTransform:(CGAffineTransform)affineTransform; + (NSArray<IJSVGTransform*>*)transformsFromAffineTransform:(CGAffineTransform)affineTransform;
+ (NSArray*)transformsForString:(NSString*)string; + (NSArray<IJSVGTransform*>*)transformsForString:(NSString*)string;
+ (NSBezierPath*)transformedPath:(IJSVGPath*)path;
+ (NSString*)affineTransformToSVGMatrixString:(CGAffineTransform)affineTransform; + (NSString*)affineTransformToSVGMatrixString:(CGAffineTransform)affineTransform;
+ (NSString*)affineTransformToSVGMatrixString:(CGAffineTransform)transform
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (CGAffineTransform)CGAffineTransform; - (CGAffineTransform)CGAffineTransform;
- (CGAffineTransform)CGAffineTransformWithModifier:(IJSVGTransformParameterModifier)modifier; - (CGAffineTransform)CGAffineTransformWithModifier:(IJSVGTransformParameterModifier)modifier;
- (CGAffineTransform)stackIdentity:(CGAffineTransform)identity; - (CGAffineTransform)stackIdentity:(CGAffineTransform)identity;
@@ -8,28 +8,24 @@
#import "IJSVGMath.h" #import "IJSVGMath.h"
#import "IJSVGTransform.h" #import "IJSVGTransform.h"
#import "IJSVGParsing.h"
@implementation IJSVGTransform @implementation IJSVGTransform
@synthesize command;
@synthesize parameters;
@synthesize parameterCount;
@synthesize sort;
- (void)dealloc - (void)dealloc
{ {
free(parameters); (void)free(_parameters);
[super dealloc]; [super dealloc];
} }
- (id)copyWithZone:(NSZone*)zone - (id)copyWithZone:(NSZone*)zone
{ {
IJSVGTransform* trans = [[self.class alloc] init]; IJSVGTransform* trans = [[self.class alloc] init];
trans.command = self.command; trans.command = _command;
trans.parameters = (CGFloat*)malloc(sizeof(CGFloat) * self.parameterCount); trans.parameters = (CGFloat*)malloc(sizeof(CGFloat) * _parameterCount);
trans.sort = sort; trans.sort = _sort;
trans.parameterCount = self.parameterCount; trans.parameterCount = _parameterCount;
memcpy(trans.parameters, self.parameters, sizeof(CGFloat) * self.parameterCount); memcpy(trans.parameters, _parameters, sizeof(CGFloat) * _parameterCount);
return trans; return trans;
} }
@@ -83,19 +79,49 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
- (void)recalculateWithBounds:(CGRect)bounds - (void)recalculateWithBounds:(CGRect)bounds
{ {
CGFloat max = bounds.size.width > bounds.size.height ? bounds.size.width : bounds.size.height; CGFloat max = bounds.size.width > bounds.size.height ? bounds.size.width : bounds.size.height;
switch (self.command) { switch (_command) {
case IJSVGTransformCommandRotate: { case IJSVGTransformCommandRotate: {
if (self.parameterCount == 1) { if (_parameterCount == 1) {
return; return;
} }
self.parameters[1] = self.parameters[1] * max; _parameters[1] = _parameters[1] * max;
self.parameters[2] = self.parameters[2] * max; _parameters[2] = _parameters[2] * max;
} }
default: default:
return; return;
} }
} }
+ (IJSVGTransformCommand)commandForCommandCString:(char*)str
{
IJSVGCharBufferToLower(str);
if (strcmp(str, "matrix") == 0) {
return IJSVGTransformCommandMatrix;
}
if (strcmp(str, "translate") == 0) {
return IJSVGTransformCommandTranslate;
}
if (strcmp(str, "translatex") == 0) {
return IJSVGTransformCommandTranslateX;
}
if (strcmp(str, "translatey") == 0) {
return IJSVGTransformCommandTranslateY;
}
if (strcmp(str, "scale") == 0) {
return IJSVGTransformCommandScale;
}
if (strcmp(str, "skewx") == 0) {
return IJSVGTransformCommandSkewX;
}
if (strcmp(str, "skewy") == 0) {
return IJSVGTransformCommandSkewY;
}
if (strcmp(str, "rotate") == 0) {
return IJSVGTransformCommandRotate;
}
return IJSVGTransformCommandNotImplemented;
}
+ (IJSVGTransformCommand)commandForCommandString:(NSString*)str + (IJSVGTransformCommand)commandForCommandString:(NSString*)str
{ {
str = str.lowercaseString; str = str.lowercaseString;
@@ -137,188 +163,76 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
return 10; return 10;
} }
+ (NSArray*)transformsForString:(NSString*)string + (NSArray<IJSVGTransform*>*)transformsForString:(NSString*)string
{ {
static NSRegularExpression* _reg = nil; NSMutableArray<IJSVGTransform*>* transforms = nil;
static dispatch_once_t onceToken; transforms = [[[NSMutableArray alloc] init] autorelease];
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 const char* charString = string.UTF8String;
NSString* params = [string substringWithRange:[result rangeAtIndex:2]]; IJSVGParsingStringMethod** methods = NULL;
IJSVGTransform* transform = [[[self.class alloc] init] autorelease]; NSUInteger count = 0;
NSInteger count = 0; methods = IJSVGParsingMethodParseString(charString, &count);
transform.command = commandType; for(int i = 0; i < count; i++) {
transform.parameters = [IJSVGUtils commandParameters:params IJSVGParsingStringMethod* method = methods[i];
count:&count]; IJSVGTransformCommand commandType;
transform.parameterCount = count; commandType = [self.class commandForCommandCString:method->name];
transform.sort = [self.class sortForTransformCommand:commandType]; if(commandType == IJSVGTransformCommandNotImplemented) {
[transforms addObject:transform]; (void)IJSVGParsingStringMethodRelease(method), method = NULL;
}]; 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 scanFloatsFromCString:method->parameters
size:&count];
transform.parameterCount = count;
// add to the list of transforms to return
[transforms addObject:transform];
(void)IJSVGParsingStringMethodRelease(method), method = NULL;
} }
(void)free(methods), methods = NULL;
return transforms; return transforms;
} }
+ (NSBezierPath*)transformedPath:(IJSVGPath*)path
{
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){
.m11 = transform.parameters[0],
.m12 = transform.parameters[1],
.m21 = transform.parameters[2],
.m22 = transform.parameters[3],
.tX = transform.parameters[4],
.tY = transform.parameters[5],
};
break;
}
// skewX
case IJSVGTransformCommandSkewX: {
CGFloat degrees = transform.parameters[0];
CGFloat radians = degrees * M_PI / 180.f;
at.transformStruct = (NSAffineTransformStruct){
.m11 = 1.f,
.m12 = 0.f,
.m21 = tan(radians),
.m22 = 1.f,
.tX = 0.f,
.tY = 0.f
};
break;
}
// skewX
case IJSVGTransformCommandSkewY: {
CGFloat degrees = transform.parameters[0];
CGFloat radians = degrees * M_PI / 180.f;
at.transformStruct = (NSAffineTransformStruct){
.m11 = 1.f,
.m12 = tan(radians),
.m21 = 0.f,
.m22 = 1.f,
.tX = 0.f,
.tY = 0.f
};
break;
}
// translate
case IJSVGTransformCommandTranslate: {
if (transform.parameterCount == 1)
[at translateXBy:transform.parameters[0]
yBy:0];
else
[at translateXBy:transform.parameters[0]
yBy:transform.parameters[1]];
break;
}
// translateX
case IJSVGTransformCommandTranslateX: {
[at translateXBy:transform.parameters[0] yBy:0.f];
break;
}
// translateY
case IJSVGTransformCommandTranslateY: {
[at translateXBy:0.f yBy:transform.parameters[0]];
break;
}
// scale
case IJSVGTransformCommandScale: {
if (transform.parameterCount == 1)
[at scaleBy:transform.parameters[0]];
else
[at scaleXBy:transform.parameters[0]
yBy:transform.parameters[1]];
break;
}
// rotate
case IJSVGTransformCommandRotate: {
if (transform.parameterCount == 1)
[at rotateByDegrees:transform.parameters[0]];
else {
CGFloat centerX = transform.parameters[1];
CGFloat centerY = transform.parameters[2];
CGFloat angle = transform.parameters[0] * (M_PI / 180.f);
[at translateXBy:centerX
yBy:centerY];
[at rotateByRadians:angle];
[at translateXBy:-1.f * centerX
yBy:-1.f * centerY];
}
break;
}
// do nothing
case IJSVGTransformCommandNotImplemented: {
}
}
[cop transformUsingAffineTransform:at];
}
return cop;
}
- (CGAffineTransform)CGAffineTransform - (CGAffineTransform)CGAffineTransform
{ {
return [self CGAffineTransformWithModifier:nil]; return [self stackIdentity:CGAffineTransformIdentity];
} }
- (CGAffineTransform)stackIdentity:(CGAffineTransform)identity - (CGAffineTransform)stackIdentity:(CGAffineTransform)identity
{ {
switch (self.command) { switch (_command) {
// translate // translate
case IJSVGTransformCommandTranslate: { case IJSVGTransformCommandTranslate: {
if (self.parameterCount == 1) { if (_parameterCount == 1) {
return CGAffineTransformTranslate(identity, self.parameters[0], 0.f); return CGAffineTransformTranslate(identity, _parameters[0], 0.f);
} }
return CGAffineTransformTranslate(identity, self.parameters[0], self.parameters[1]); return CGAffineTransformTranslate(identity, _parameters[0], _parameters[1]);
} }
// translateX // translateX
case IJSVGTransformCommandTranslateX: { case IJSVGTransformCommandTranslateX: {
return CGAffineTransformTranslate(identity, self.parameters[0], 0.f); return CGAffineTransformTranslate(identity, _parameters[0], 0.f);
} }
// translateY // translateY
case IJSVGTransformCommandTranslateY: { case IJSVGTransformCommandTranslateY: {
return CGAffineTransformTranslate(identity, 0.f, self.parameters[0]); return CGAffineTransformTranslate(identity, 0.f, _parameters[0]);
} }
// rotate // rotate
case IJSVGTransformCommandRotate: { case IJSVGTransformCommandRotate: {
if (self.parameterCount == 1) { if (_parameterCount == 1) {
return CGAffineTransformRotate(identity, (self.parameters[0] / 180) * M_PI); return CGAffineTransformRotate(identity, (_parameters[0] / 180) * M_PI);
} }
CGFloat p0 = self.parameters[0]; CGFloat p0 = _parameters[0];
CGFloat p1 = self.parameters[1]; CGFloat p1 = _parameters[1];
CGFloat p2 = self.parameters[2]; CGFloat p2 = _parameters[2];
CGFloat angle = p0 * (M_PI / 180.f); CGFloat angle = p0 * (M_PI / 180.f);
identity = CGAffineTransformTranslate(identity, p1, p2); identity = CGAffineTransformTranslate(identity, p1, p2);
@@ -328,35 +242,35 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// scale // scale
case IJSVGTransformCommandScale: { case IJSVGTransformCommandScale: {
CGFloat p0 = self.parameters[0]; CGFloat p0 = _parameters[0];
if (self.parameterCount == 1) { if (_parameterCount == 1) {
return CGAffineTransformScale(identity, p0, p0); return CGAffineTransformScale(identity, p0, p0);
} }
CGFloat p1 = self.parameters[1]; CGFloat p1 = _parameters[1];
return CGAffineTransformScale(identity, p0, p1); return CGAffineTransformScale(identity, p0, p1);
} }
// matrix // matrix
case IJSVGTransformCommandMatrix: { case IJSVGTransformCommandMatrix: {
CGFloat p0 = self.parameters[0]; CGFloat p0 = _parameters[0];
CGFloat p1 = self.parameters[1]; CGFloat p1 = _parameters[1];
CGFloat p2 = self.parameters[2]; CGFloat p2 = _parameters[2];
CGFloat p3 = self.parameters[3]; CGFloat p3 = _parameters[3];
CGFloat p4 = self.parameters[4]; CGFloat p4 = _parameters[4];
CGFloat p5 = self.parameters[5]; CGFloat p5 = _parameters[5];
return CGAffineTransformMake(p0, p1, p2, p3, p4, p5); return CGAffineTransformMake(p0, p1, p2, p3, p4, p5);
} }
// skewX // skewX
case IJSVGTransformCommandSkewX: { case IJSVGTransformCommandSkewX: {
CGFloat degrees = self.parameters[0]; CGFloat degrees = _parameters[0];
CGFloat radians = degrees * M_PI / 180.f; CGFloat radians = degrees * M_PI / 180.f;
return CGAffineTransformMake(1.f, 0.f, tan(radians), 1.f, 0.f, 0.f); return CGAffineTransformMake(1.f, 0.f, tan(radians), 1.f, 0.f, 0.f);
} }
// skewY // skewY
case IJSVGTransformCommandSkewY: { case IJSVGTransformCommandSkewY: {
CGFloat degrees = self.parameters[0]; CGFloat degrees = _parameters[0];
CGFloat radians = degrees * M_PI / 180.f; CGFloat radians = degrees * M_PI / 180.f;
return CGAffineTransformMake(1.f, tan(radians), 0.f, 1.f, 0.f, 0.f); return CGAffineTransformMake(1.f, tan(radians), 0.f, 1.f, 0.f, 0.f);
} }
@@ -370,15 +284,15 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
- (CGAffineTransform)CGAffineTransformWithModifier:(IJSVGTransformParameterModifier)modifier - (CGAffineTransform)CGAffineTransformWithModifier:(IJSVGTransformParameterModifier)modifier
{ {
switch (self.command) { switch (_command) {
// matrix // matrix
case IJSVGTransformCommandMatrix: { case IJSVGTransformCommandMatrix: {
CGFloat p0 = self.parameters[0]; CGFloat p0 = _parameters[0];
CGFloat p1 = self.parameters[1]; CGFloat p1 = _parameters[1];
CGFloat p2 = self.parameters[2]; CGFloat p2 = _parameters[2];
CGFloat p3 = self.parameters[3]; CGFloat p3 = _parameters[3];
CGFloat p4 = self.parameters[4]; CGFloat p4 = _parameters[4];
CGFloat p5 = self.parameters[5]; CGFloat p5 = _parameters[5];
if (modifier != nil) { if (modifier != nil) {
p0 = modifier(0, p0); p0 = modifier(0, p0);
p1 = modifier(1, p1); p1 = modifier(1, p1);
@@ -392,11 +306,11 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// translate // translate
case IJSVGTransformCommandTranslate: { case IJSVGTransformCommandTranslate: {
CGFloat p0 = self.parameters[0]; CGFloat p0 = _parameters[0];
if (self.parameterCount == 1) { if (_parameterCount == 1) {
return CGAffineTransformMakeTranslation(p0, 0); return CGAffineTransformMakeTranslation(p0, 0);
} }
CGFloat p1 = self.parameters[1]; CGFloat p1 = _parameters[1];
if (modifier != nil) { if (modifier != nil) {
p0 = modifier(0, p0); p0 = modifier(0, p0);
p1 = modifier(1, p1); p1 = modifier(1, p1);
@@ -406,7 +320,7 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// translateX // translateX
case IJSVGTransformCommandTranslateX: { case IJSVGTransformCommandTranslateX: {
CGFloat p0 = self.parameters[0]; CGFloat p0 = _parameters[0];
if (modifier != nil) { if (modifier != nil) {
p0 = modifier(0, p0); p0 = modifier(0, p0);
} }
@@ -415,7 +329,7 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// translateY // translateY
case IJSVGTransformCommandTranslateY: { case IJSVGTransformCommandTranslateY: {
CGFloat p0 = self.parameters[0]; CGFloat p0 = _parameters[0];
if (modifier != nil) { if (modifier != nil) {
p0 = modifier(0, p0); p0 = modifier(0, p0);
} }
@@ -424,11 +338,11 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// scale // scale
case IJSVGTransformCommandScale: { case IJSVGTransformCommandScale: {
CGFloat p0 = self.parameters[0]; CGFloat p0 = _parameters[0];
if (self.parameterCount == 1) { if (_parameterCount == 1) {
return CGAffineTransformMakeScale(p0, p0); return CGAffineTransformMakeScale(p0, p0);
} }
CGFloat p1 = self.parameters[1]; CGFloat p1 = _parameters[1];
if (modifier != nil) { if (modifier != nil) {
p0 = modifier(0, p0); p0 = modifier(0, p0);
p1 = modifier(1, p1); p1 = modifier(1, p1);
@@ -438,7 +352,7 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// skewX // skewX
case IJSVGTransformCommandSkewX: { case IJSVGTransformCommandSkewX: {
CGFloat degrees = self.parameters[0]; CGFloat degrees = _parameters[0];
if (modifier != nil) { if (modifier != nil) {
degrees = modifier(0, degrees); degrees = modifier(0, degrees);
} }
@@ -448,7 +362,7 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// skewY // skewY
case IJSVGTransformCommandSkewY: { case IJSVGTransformCommandSkewY: {
CGFloat degrees = self.parameters[0]; CGFloat degrees = _parameters[0];
if (modifier != nil) { if (modifier != nil) {
degrees = modifier(0, degrees); degrees = modifier(0, degrees);
} }
@@ -458,12 +372,12 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// rotate // rotate
case IJSVGTransformCommandRotate: { case IJSVGTransformCommandRotate: {
if (self.parameterCount == 1) { if (_parameterCount == 1) {
return CGAffineTransformMakeRotation((self.parameters[0] / 180) * M_PI); return CGAffineTransformMakeRotation((_parameters[0] / 180) * M_PI);
} else { } else {
CGFloat p0 = self.parameters[0]; CGFloat p0 = _parameters[0];
CGFloat p1 = self.parameters[1]; CGFloat p1 = _parameters[1];
CGFloat p2 = self.parameters[2]; CGFloat p2 = _parameters[2];
if (modifier != nil) { if (modifier != nil) {
p0 = modifier(0, p0); p0 = modifier(0, p0);
p1 = modifier(1, p1); p1 = modifier(1, p1);
@@ -493,15 +407,199 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
return [self transformsForString:matrix]; 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 + (NSString*)affineTransformToSVGMatrixString:(CGAffineTransform)transform
{ {
return [NSString stringWithFormat:@"matrix(%@ %@ %@ %@ %@ %@)", NSArray<NSString*>* numbers = @[
IJSVGShortFloatString(transform.a), IJSVGShortFloatString(transform.a),
IJSVGShortFloatString(transform.b), IJSVGShortFloatString(transform.b),
IJSVGShortFloatString(transform.c), IJSVGShortFloatString(transform.c),
IJSVGShortFloatString(transform.d), IJSVGShortFloatString(transform.d),
IJSVGShortFloatString(transform.tx), IJSVGShortFloatString(transform.tx),
IJSVGShortFloatString(transform.ty)]; IJSVGShortFloatString(transform.ty)
];
return [NSString stringWithFormat:@"matrix(%@)",
IJSVGCompressFloatParameterArray(numbers)];
}
+ (NSArray<NSDictionary*>*)filterUselessAffineTransformComponents:(NSArray<NSDictionary*>*)components
{
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 = 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;
NSMutableArray* transforms = [[[NSMutableArray alloc] init] autorelease];
// tx, ty -> translate
if (data[4] != 0.f || data[5] != 0.f) {
[transforms addObject:@{
@"name" : @"translate",
@"data" : @[ @(data[4]), @(data[5]) ]
}];
}
// [sx, 0, tan(a).sy, sy, 0, 0] -> skewX(a).scale(sx,sy)
if (data[1] == 0.f && data[2] != 0.f) {
[transforms addObject:@{
@"name" : @"skewX",
@"data" : @[ @(IJSVGMathToFixed(IJSVGMathAtan(data[2] / sy), precision)) ]
}];
// [sx, sy.tan(a), 0, sy, 0, 0] -> skewX(a).scale(sx, sy)
} else if (data[1] != 0.f && data[2] == 0.f) {
[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) {
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) ]
}];
}
}
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
}];
}
if (rowSum != 0.f && colSum != 0.f) {
[transforms addObject:@{
@"name" : @"skewX",
@"data" : @[ @(IJSVGMathToFixed(IJSVGMathAtan(colSum / (sx * sx)), precision)) ]
}];
}
// 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));
[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 ];
}
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 transforms;
} }
- (NSString*)description - (NSString*)description
@@ -8,9 +8,20 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
typedef struct {
BOOL round;
int precision;
} IJSVGFloatingPointOptions;
typedef NS_ENUM(NSInteger, IJSVGUnitLengthType) { typedef NS_ENUM(NSInteger, IJSVGUnitLengthType) {
IJSVGUnitLengthTypeNumber, IJSVGUnitLengthTypeNumber,
IJSVGUnitLengthTypePercentage IJSVGUnitLengthTypePercentage,
IJSVGUnitLengthTypeCM,
IJSVGUnitLengthTypeMM,
IJSVGUnitLengthTypeIN,
IJSVGUnitLengthTypePT,
IJSVGUnitLengthTypePC,
IJSVGUnitLengthTypePX
}; };
typedef NS_ENUM(NSInteger, IJSVGUnitType) { typedef NS_ENUM(NSInteger, IJSVGUnitType) {
@@ -22,6 +33,7 @@ typedef NS_ENUM(NSInteger, IJSVGUnitType) {
@interface IJSVGUnitLength : NSObject @interface IJSVGUnitLength : NSObject
@property (nonatomic, assign) IJSVGUnitLengthType type; @property (nonatomic, assign) IJSVGUnitLengthType type;
@property (nonatomic, assign) IJSVGUnitLengthType originalType;
@property (nonatomic, assign) CGFloat value; @property (nonatomic, assign) CGFloat value;
@property (nonatomic, assign) BOOL inherit; @property (nonatomic, assign) BOOL inherit;
@@ -38,5 +50,6 @@ typedef NS_ENUM(NSInteger, IJSVGUnitType) {
- (CGFloat)valueAsPercentage; - (CGFloat)valueAsPercentage;
- (CGFloat)computeValue:(CGFloat)anotherValue; - (CGFloat)computeValue:(CGFloat)anotherValue;
- (NSString*)stringValue; - (NSString*)stringValue;
- (NSString*)stringValueWithFloatingPointOptions:(IJSVGFloatingPointOptions)options;
@end @end
@@ -12,10 +12,6 @@
@implementation IJSVGUnitLength @implementation IJSVGUnitLength
@synthesize value;
@synthesize type;
@synthesize inherit;
+ (IJSVGUnitLength*)unitWithFloat:(CGFloat)number + (IJSVGUnitLength*)unitWithFloat:(CGFloat)number
{ {
IJSVGUnitLength* unit = [[[self alloc] init] autorelease]; IJSVGUnitLength* unit = [[[self alloc] init] autorelease];
@@ -55,23 +51,143 @@
return unit; return unit;
} }
+ (IJSVGUnitLengthType)typeForCString:(const char*)chars
{
if(IJSVGCharBufferHasSuffix((char*)chars, "%")) {
return IJSVGUnitLengthTypePercentage;
}
if(IJSVGCharBufferHasSuffix((char*)chars, "cm")) {
return IJSVGUnitLengthTypeCM;
}
if(IJSVGCharBufferHasSuffix((char*)chars, "mm")) {
return IJSVGUnitLengthTypeMM;
}
if(IJSVGCharBufferHasSuffix((char*)chars, "in")) {
return IJSVGUnitLengthTypeIN;
}
if(IJSVGCharBufferHasSuffix((char*)chars, "pt")) {
return IJSVGUnitLengthTypePT;
}
if(IJSVGCharBufferHasSuffix((char*)chars, "pc")) {
return IJSVGUnitLengthTypePC;
}
if(IJSVGCharBufferHasSuffix((char*)chars, "px")) {
return IJSVGUnitLengthTypePX;
}
return IJSVGUnitLengthTypeNumber;
}
+ (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 + (IJSVGUnitLength*)unitWithString:(NSString*)string
{ {
// just return noting for inherit, node will deal // just return noting for inherit, node will deal
// with the rest...hopefully // with the rest...hopefully
NSCharacterSet* cSet = NSCharacterSet.whitespaceCharacterSet; if(string == nil) {
string = [string stringByTrimmingCharactersInSet:cSet]; return nil;
}
if ([string isEqualToString:@"inherit"]) { char* chars = IJSVGTimmedCharBufferCreate(string.UTF8String);
// is inherit or just nothing
size_t strl = strlen(chars);
if (strcmp(chars, "inherit") == 0 || strl == 0) {
(void)free(chars), chars = NULL;
return nil;
}
// grab the float value from the string
NSInteger length;
CGFloat* floats = [IJSVGUtils scanFloatsFromCString:chars
floatCount:1
charCount:(NSUInteger)strl
size:&length];
// not sure how this ended up but nothing returned
// even though there should had been
if(length == 0) {
(void)free(floats), floats = NULL;
return nil; return nil;
} }
IJSVGUnitLength* unit = [[[self alloc] init] autorelease]; IJSVGUnitLength* unit = [[[self alloc] init] autorelease];
unit.value = string.floatValue; unit.value = floats[0];
unit.type = IJSVGUnitLengthTypeNumber; unit.type = IJSVGUnitLengthTypeNumber;
if ([string hasSuffix:@"%"] == YES) {
unit.value /= 100.f;
unit.type = IJSVGUnitLengthTypePercentage; IJSVGUnitLengthType type = [self typeForCString:chars];
unit.originalType = type;
// memory free
(void)(free(floats)), floats = NULL;
(void)free(chars), chars = NULL;
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; return unit;
} }
@@ -79,7 +195,7 @@
- (CGFloat)computeValue:(CGFloat)anotherValue - (CGFloat)computeValue:(CGFloat)anotherValue
{ {
if (self.type == IJSVGUnitLengthTypePercentage) { if (self.type == IJSVGUnitLengthTypePercentage) {
return ((anotherValue / 100.f) * (self.value * 100.f)); return ((anotherValue / 100.f) * (_value * 100.f));
} }
return self.value; return self.value;
} }
@@ -92,15 +208,25 @@
- (NSString*)stringValue - (NSString*)stringValue
{ {
if (self.type == IJSVGUnitLengthTypePercentage) { if (self.type == IJSVGUnitLengthTypePercentage) {
return [NSString stringWithFormat:@"%@%%", IJSVGShortFloatString(self.value * 100.f)]; return [NSString stringWithFormat:@"%@%%",
IJSVGShortFloatString(self.value * 100.f)];
} }
return IJSVGShortFloatString(self.value); return IJSVGShortFloatString(self.value);
} }
- (NSString*)stringValueWithFloatingPointOptions:(IJSVGFloatingPointOptions)options
{
if (_type == IJSVGUnitLengthTypePercentage) {
return [NSString stringWithFormat:@"%@%%",
IJSVGShortFloatStringWithOptions(_value * 100.f, options)];
}
return IJSVGShortFloatStringWithOptions(_value, options);
}
- (NSString*)description - (NSString*)description
{ {
return [NSString stringWithFormat:@"%f%@", return [NSString stringWithFormat:@"%f%@",
self.value, (self.type == IJSVGUnitLengthTypePercentage ? @"%" : @"")]; _value, (_value == IJSVGUnitLengthTypePercentage ? @"%" : @"")];
} }
@end @end
@@ -0,0 +1,17 @@
//
// IJSVGUnitPoint.h
// IJSVG
//
// Created by Curtis Hard on 12/02/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGUnitLength.h>
#import <Foundation/Foundation.h>
@interface IJSVGUnitPoint : NSObject
@property (nonatomic, retain) IJSVGUnitLength* x;
@property (nonatomic, retain) IJSVGUnitLength* y;
@end
@@ -0,0 +1,29 @@
//
// IJSVGUnitPoint.m
// IJSVG
//
// Created by Curtis Hard on 12/02/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import "IJSVGUnitPoint.h"
@implementation IJSVGUnitPoint
- (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 <IJSVG/IJSVGUnitPoint.h>
#import <IJSVG/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,29 @@
//
// IJSVGUnitRect.m
// IJSVG
//
// Created by Curtis Hard on 12/02/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import "IJSVGUnitRect.h"
@implementation IJSVGUnitRect
- (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

Some files were not shown because too many files have changed in this diff Show More