Compare commits

..

125 Commits

Author SHA1 Message Date
Curtis Hard 050df84672 Adds isNoneOrTransparent to COlor 2022-04-19 18:11:02 +01:00
Curtis Hard ac61d3d6d6 Adds image generation to IJSVGLayer 2022-04-18 17:32:20 +01:00
Curtis Hard fdde1fd5f6 Fixes transforms and masking 2022-04-18 14:45:24 +01:00
Curtis Hard fe179076cc More fixes 2022-04-18 11:45:04 +01:00
Curtis Hard 01dd0d25d2 Make sure the node actually exists 2022-04-17 17:57:52 +01:00
Curtis Hard 799593d7d8 Removed log 2022-04-17 17:57:09 +01:00
Curtis Hard 0c7cfd776c Fixes stylesheets 2022-04-17 17:57:01 +01:00
Curtis Hard 757c4317d3 Adds better rendering calls into SVG 2022-04-17 12:45:44 +01:00
Curtis Hard 1727505968 Removed IJSVG_DRAWABLE_LAYER 2022-04-17 11:58:49 +01:00
Curtis Hard 775c28e91a Updated example and added better support for ratios 2022-04-16 17:50:57 +01:00
Curtis Hard ec539a12b3 More alignment stuff 2022-04-16 13:51:33 +01:00
Curtis Hard 43210c018e Fixes strokes with layer of constant fill color
- started to out viewboxes
2022-04-15 21:10:48 +01:00
Curtis Hard 3773e10f44 Fixes strokes, some patterns, changes how we parse commands ready for units 2022-04-13 21:20:53 +01:00
Curtis Hard 51fb9cc871 added referencing layers into layer tree 2022-04-12 20:00:58 +01:00
Curtis Hard be08ada5da Fixes gradients 2022-04-11 10:14:08 +01:00
Curtis Hard a3a6ee5a05 Fixes absolute transform and gradients 2022-04-08 21:56:03 +01:00
Curtis Hard cccfc0db68 Various fixes 2022-04-07 15:36:33 +01:00
Curtis Hard 19db898bdd More gradient things that dont work 2022-04-03 20:05:58 +01:00
Curtis Hard 8abbdf390a Continue of refactor 2022-04-02 13:51:47 +01:00
Curtis Hard 6e98e2cfed Removes useless properties 2022-03-31 13:36:53 +01:00
Curtis Hard 2c536a8962 Adds .children and improvements to addChild/removeChild 2022-03-30 19:52:30 +01:00
Curtis Hard 65a8c0a691 Proper clip path 2022-03-30 15:26:32 +01:00
Curtis Hard 2942cc0a58 Added IJSVGColorNode and traits 2022-03-29 20:12:53 +01:00
Curtis Hard ba7a05553e Adds frame calculations onto group 2022-03-29 11:37:28 +01:00
Curtis Hard c9a7788d98 Added correctly nested detached nodes 2022-03-29 09:34:08 +01:00
Curtis Hard 774ff48074 Refactor, IJSVGParser is not the root anymore, there is a new IJSVGRootNode as the root of the node tree 2022-03-28 20:31:27 +01:00
Curtis Hard 2eb629cd21 Overhaul of parser 2022-03-28 19:14:46 +01:00
Curtis Hard 137e34623f hacky things! 2022-03-27 22:05:19 +01:00
Curtis Hard 39b0b5bbb4 More image fixes 2022-03-27 20:45:31 +01:00
Curtis Hard 820fca2679 Merge branch 'features/patterns' of https://github.com/curthard89/IJSVG into features/patterns 2022-03-27 15:38:09 +01:00
Curtis Hard 2e5819183d More pattern things 2022-03-27 15:36:33 +01:00
Curtis Hard eff56d3c60 More pattern things, this is not fun 2022-03-27 15:35:51 +01:00
Curtis Hard 7a4fd35a64 initial pattern fixes 2022-03-25 13:21:59 +00:00
Curtis Hard 964dc3005d Merge branch 'fixes/const-memory-alloc-issues' of https://github.com/curthard89/IJSVG 2022-03-23 18:27:54 +00:00
Curtis Hard 1a8d44ec95 Fixes masking for gradients 2022-03-23 18:27:41 +00:00
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
133 changed files with 7775 additions and 19364 deletions
+1
View File
@@ -0,0 +1 @@
.DS_Store
+95 -19
View File
@@ -7,10 +7,24 @@
objects = {
/* Begin PBXBuildFile section */
590EB7A427F637EF0047CECF /* IJSVGTransformLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 590EB7A227F637EF0047CECF /* IJSVGTransformLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
590EB7A527F637EF0047CECF /* IJSVGTransformLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 590EB7A327F637EF0047CECF /* IJSVGTransformLayer.m */; };
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 */; };
594CF561238FF46C009B251B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF560238FF46C009B251B /* Foundation.framework */; };
594CF563238FF473009B251B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF562238FF473009B251B /* QuartzCore.framework */; };
5978C46A280A241200D25296 /* IJSVGRootLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5978C468280A241200D25296 /* IJSVGRootLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
5978C46B280A241200D25296 /* IJSVGRootLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5978C469280A241200D25296 /* IJSVGRootLayer.m */; };
599EB4D3238FF570004CB6BC /* libobjc.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 599EB4D2238FF535004CB6BC /* libobjc.tbd */; };
59A24EBC23F480EA0090C374 /* IJSVGUnitSize.h in Headers */ = {isa = PBXBuildFile; fileRef = 59A24EBA23F480EA0090C374 /* IJSVGUnitSize.h */; settings = {ATTRIBUTES = (Public, ); }; };
59A24EBD23F480EA0090C374 /* IJSVGUnitSize.m in Sources */ = {isa = PBXBuildFile; fileRef = 59A24EBB23F480EA0090C374 /* IJSVGUnitSize.m */; };
59E7CFAF23B148600077D599 /* IJSVGCommandParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 59E7CFAD23B148600077D599 /* IJSVGCommandParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
59E7CFB023B148600077D599 /* IJSVGCommandParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E7CFAE23B148600077D599 /* IJSVGCommandParser.m */; };
59EB75D623905F7300F5AE63 /* IJSVGLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 59EB756523905F6B00F5AE63 /* IJSVGLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -22,7 +36,6 @@
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 */; };
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, ); }; };
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 */; };
@@ -49,12 +62,10 @@
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 */; };
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 */; };
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 */; };
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 */; };
59EB760123905F7300F5AE63 /* IJSVGLayerTree.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB759023905F6D00F5AE63 /* IJSVGLayerTree.m */; };
59EB760223905F7300F5AE63 /* IJSVGCommandVerticalLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB759123905F6D00F5AE63 /* IJSVGCommandVerticalLine.m */; };
@@ -105,7 +116,6 @@
59EB763123905F7300F5AE63 /* IJSVGText.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C023905F7100F5AE63 /* IJSVGText.m */; };
59EB763223905F7300F5AE63 /* IJSVGView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C123905F7100F5AE63 /* IJSVGView.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 */; };
59EB763623905F7300F5AE63 /* IJSVGImageLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C523905F7100F5AE63 /* IJSVGImageLayer.m */; };
59EB763723905F7300F5AE63 /* IJSVGCommandClose.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75C623905F7100F5AE63 /* IJSVGCommandClose.m */; };
@@ -124,15 +134,37 @@
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, ); }; };
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 */; };
59F9EAF82808BB5F00188ACB /* IJSVGViewBox.h in Headers */ = {isa = PBXBuildFile; fileRef = 59F9EAF62808BB5F00188ACB /* IJSVGViewBox.h */; settings = {ATTRIBUTES = (Public, ); }; };
59F9EAF92808BB5F00188ACB /* IJSVGViewBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F9EAF72808BB5F00188ACB /* IJSVGViewBox.m */; };
59FCC09427F2394D00BB924E /* IJSVGRootNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 59FCC09227F2394D00BB924E /* IJSVGRootNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
59FCC09527F2394D00BB924E /* IJSVGRootNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 59FCC09327F2394D00BB924E /* IJSVGRootNode.m */; };
59FDBF0027F3454800AF7038 /* IJSVGColorNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 59FDBEFE27F3454800AF7038 /* IJSVGColorNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
59FDBF0127F3454800AF7038 /* IJSVGColorNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 59FDBEFF27F3454800AF7038 /* IJSVGColorNode.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
590EB7A227F637EF0047CECF /* IJSVGTransformLayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGTransformLayer.h; sourceTree = "<group>"; };
590EB7A327F637EF0047CECF /* IJSVGTransformLayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGTransformLayer.m; sourceTree = "<group>"; };
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; };
594CF473238FF38E009B251B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
594CF55E238FF462009B251B /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
594CF560238FF46C009B251B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
594CF562238FF473009B251B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
5978C468280A241200D25296 /* IJSVGRootLayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGRootLayer.h; sourceTree = "<group>"; };
5978C469280A241200D25296 /* IJSVGRootLayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGRootLayer.m; sourceTree = "<group>"; };
599EB4D2238FF535004CB6BC /* libobjc.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libobjc.tbd; path = usr/lib/libobjc.tbd; sourceTree = SDKROOT; };
59A24EBA23F480EA0090C374 /* IJSVGUnitSize.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGUnitSize.h; sourceTree = "<group>"; };
59A24EBB23F480EA0090C374 /* IJSVGUnitSize.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGUnitSize.m; sourceTree = "<group>"; };
59E7CFAD23B148600077D599 /* IJSVGCommandParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = IJSVGCommandParser.h; path = IJSVG/Source/Parsing/IJSVGCommandParser.h; sourceTree = SOURCE_ROOT; };
59E7CFAE23B148600077D599 /* IJSVGCommandParser.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = IJSVGCommandParser.m; path = IJSVG/Source/Parsing/IJSVGCommandParser.m; sourceTree = SOURCE_ROOT; };
59EB756523905F6B00F5AE63 /* IJSVGLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGLayer.h; sourceTree = "<group>"; };
@@ -144,7 +176,6 @@
59EB756B23905F6B00F5AE63 /* IJSVGText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGText.h; sourceTree = "<group>"; };
59EB756C23905F6B00F5AE63 /* IJSVGCommandQuadraticCurve.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGCommandQuadraticCurve.m; sourceTree = "<group>"; };
59EB756D23905F6C00F5AE63 /* IJSVGCommandVerticalLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGCommandVerticalLine.h; sourceTree = "<group>"; };
59EB756E23905F6C00F5AE63 /* IJSVGWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGWriter.h; sourceTree = "<group>"; };
59EB756F23905F6C00F5AE63 /* IJSVGStyleSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGStyleSheet.h; sourceTree = "<group>"; };
59EB757023905F6C00F5AE63 /* IJSVGCommandSmoothCurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGCommandSmoothCurve.h; sourceTree = "<group>"; };
59EB757123905F6C00F5AE63 /* IJSVGColorList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGColorList.m; sourceTree = "<group>"; };
@@ -171,12 +202,10 @@
59EB758623905F6C00F5AE63 /* IJSVGDef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGDef.h; sourceTree = "<group>"; };
59EB758723905F6C00F5AE63 /* IJSVGCommandHorizontalLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGCommandHorizontalLine.m; sourceTree = "<group>"; };
59EB758823905F6D00F5AE63 /* IJSVGFontConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGFontConverter.h; sourceTree = "<group>"; };
59EB758923905F6D00F5AE63 /* IJSVGWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGWriter.m; sourceTree = "<group>"; };
59EB758A23905F6D00F5AE63 /* IJSVGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGPath.m; sourceTree = "<group>"; };
59EB758B23905F6D00F5AE63 /* IJSVGGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGGroup.h; sourceTree = "<group>"; };
59EB758C23905F6D00F5AE63 /* IJSVGPatternLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGPatternLayer.m; sourceTree = "<group>"; };
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>"; };
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>"; };
@@ -227,7 +256,6 @@
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>"; };
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>"; };
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>"; };
@@ -246,6 +274,14 @@
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>"; };
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>"; };
59F9EAF62808BB5F00188ACB /* IJSVGViewBox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGViewBox.h; sourceTree = "<group>"; };
59F9EAF72808BB5F00188ACB /* IJSVGViewBox.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGViewBox.m; sourceTree = "<group>"; };
59FCC09227F2394D00BB924E /* IJSVGRootNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGRootNode.h; sourceTree = "<group>"; };
59FCC09327F2394D00BB924E /* IJSVGRootNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGRootNode.m; sourceTree = "<group>"; };
59FDBEFE27F3454800AF7038 /* IJSVGColorNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGColorNode.h; sourceTree = "<group>"; };
59FDBEFF27F3454800AF7038 /* IJSVGColorNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGColorNode.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -316,6 +352,8 @@
59EB758523905F6C00F5AE63 /* IJSVGColor.m */,
59EB75A923905F6F00F5AE63 /* IJSVGColorList.h */,
59EB757123905F6C00F5AE63 /* IJSVGColorList.m */,
59F36506262F1ABB00BCE3FD /* IJSVGColorType.h */,
59F36507262F1ABB00BCE3FD /* IJSVGColorType.m */,
);
path = Colors;
sourceTree = "<group>";
@@ -333,12 +371,14 @@
59EB757B23905F6C00F5AE63 /* IJSVGLayer.m */,
59EB75BF23905F7000F5AE63 /* IJSVGPatternLayer.h */,
59EB758C23905F6D00F5AE63 /* IJSVGPatternLayer.m */,
59EB757223905F6C00F5AE63 /* IJSVGRadialGradient.h */,
59EB75A623905F6F00F5AE63 /* IJSVGRadialGradient.m */,
59EB759723905F6D00F5AE63 /* IJSVGShapeLayer.h */,
59EB757A23905F6C00F5AE63 /* IJSVGShapeLayer.m */,
59EB75A423905F6E00F5AE63 /* IJSVGStrokeLayer.h */,
59EB757423905F6C00F5AE63 /* IJSVGStrokeLayer.m */,
590EB7A227F637EF0047CECF /* IJSVGTransformLayer.h */,
590EB7A327F637EF0047CECF /* IJSVGTransformLayer.m */,
5978C468280A241200D25296 /* IJSVGRootLayer.h */,
5978C469280A241200D25296 /* IJSVGRootLayer.m */,
);
path = Layers;
sourceTree = "<group>";
@@ -363,6 +403,8 @@
59EB756923905F6B00F5AE63 /* IJSVGDef.m */,
59EB759F23905F6E00F5AE63 /* IJSVGLinearGradient.h */,
59EB75C223905F7100F5AE63 /* IJSVGLinearGradient.m */,
59EB757223905F6C00F5AE63 /* IJSVGRadialGradient.h */,
59EB75A623905F6F00F5AE63 /* IJSVGRadialGradient.m */,
59EB75AB23905F6F00F5AE63 /* IJSVGGradient.h */,
59EB75D223905F7300F5AE63 /* IJSVGGradient.m */,
59EB758B23905F6D00F5AE63 /* IJSVGGroup.h */,
@@ -377,6 +419,10 @@
59EB75AA23905F6F00F5AE63 /* IJSVGPattern.m */,
59EB756B23905F6B00F5AE63 /* IJSVGText.h */,
59EB75C023905F7100F5AE63 /* IJSVGText.m */,
59FCC09227F2394D00BB924E /* IJSVGRootNode.h */,
59FCC09327F2394D00BB924E /* IJSVGRootNode.m */,
59FDBEFE27F3454800AF7038 /* IJSVGColorNode.h */,
59FDBEFF27F3454800AF7038 /* IJSVGColorNode.m */,
);
path = Nodes;
sourceTree = "<group>";
@@ -396,6 +442,16 @@
59EB75BA23905F7000F5AE63 /* IJSVGUnitLength.m */,
59EB759423905F6D00F5AE63 /* IJSVGUtils.h */,
59EB758023905F6C00F5AE63 /* IJSVGUtils.m */,
5919E65523F47FF60051873A /* IJSVGUnitRect.h */,
5919E65623F47FF60051873A /* IJSVGUnitRect.m */,
5919E65923F480330051873A /* IJSVGUnitPoint.h */,
5919E65A23F480330051873A /* IJSVGUnitPoint.m */,
59A24EBA23F480EA0090C374 /* IJSVGUnitSize.h */,
59A24EBB23F480EA0090C374 /* IJSVGUnitSize.m */,
591A3E4B25CC91F800AD45B7 /* IJSVGParsing.h */,
591A3E4C25CC91F800AD45B7 /* IJSVGParsing.m */,
59F9EAF62808BB5F00188ACB /* IJSVGViewBox.h */,
59F9EAF72808BB5F00188ACB /* IJSVGViewBox.m */,
);
path = Utils;
sourceTree = "<group>";
@@ -427,10 +483,10 @@
592ABBEB2397A11800F44380 /* Additions */ = {
isa = PBXGroup;
children = (
59EB75C323905F7100F5AE63 /* IJSVGBezierPathAdditions.h */,
59EB758E23905F6D00F5AE63 /* IJSVGBezierPathAdditions.m */,
59EB75B823905F7000F5AE63 /* IJSVGStringAdditions.h */,
59EB757823905F6C00F5AE63 /* IJSVGStringAdditions.m */,
594A10D8248D7C90001A3181 /* NSImage+IJSVGAdditions.h */,
594A10D9248D7C90001A3181 /* NSImage+IJSVGAdditions.m */,
);
path = Additions;
sourceTree = "<group>";
@@ -484,8 +540,6 @@
59EB757D23905F6C00F5AE63 /* IJSVGImageRep.m */,
59EB75CD23905F7200F5AE63 /* IJSVGView.h */,
59EB75C123905F7100F5AE63 /* IJSVGView.m */,
59EB756E23905F6C00F5AE63 /* IJSVGWriter.h */,
59EB758923905F6D00F5AE63 /* IJSVGWriter.m */,
);
path = Core;
sourceTree = "<group>";
@@ -517,7 +571,6 @@
files = (
59EB762623905F7300F5AE63 /* IJSVGImage.h in Headers */,
59EB760823905F7300F5AE63 /* IJSVGShapeLayer.h in Headers */,
59EB75DF23905F7300F5AE63 /* IJSVGWriter.h in Headers */,
59EB760F23905F7300F5AE63 /* IJSVGCommandEllipticalArc.h in Headers */,
59EB75F523905F7300F5AE63 /* IJSVGError.h in Headers */,
59EB763E23905F7300F5AE63 /* IJSVGView.h in Headers */,
@@ -562,15 +615,25 @@
59EB75D623905F7300F5AE63 /* IJSVGLayer.h in Headers */,
59EB763023905F7300F5AE63 /* IJSVGPatternLayer.h in Headers */,
59EB75F023905F7300F5AE63 /* IJSVGStyleSheetSelectorRaw.h in Headers */,
59EB763423905F7300F5AE63 /* IJSVGBezierPathAdditions.h in Headers */,
59EB761823905F7300F5AE63 /* IJSVGParser.h in Headers */,
59EB761E23905F7300F5AE63 /* IJSVGGroupLayer.h in Headers */,
59EB761D23905F7300F5AE63 /* IJSVGStyle.h in Headers */,
5919E65723F47FF60051873A /* IJSVGUnitRect.h in Headers */,
5919E65B23F480330051873A /* IJSVGUnitPoint.h in Headers */,
591A3E4D25CC91F800AD45B7 /* IJSVGParsing.h in Headers */,
59A24EBC23F480EA0090C374 /* IJSVGUnitSize.h in Headers */,
59EB764523905F7300F5AE63 /* IJSVGExporter.h in Headers */,
59F36508262F1ABB00BCE3FD /* IJSVGColorType.h in Headers */,
59E7CFAF23B148600077D599 /* IJSVGCommandParser.h in Headers */,
59EB762823905F7300F5AE63 /* IJSVGCommandClose.h in Headers */,
59EB75E423905F7300F5AE63 /* IJSVGGradientUnitLength.h in Headers */,
59FCC09427F2394D00BB924E /* IJSVGRootNode.h in Headers */,
594A10DA248D7C90001A3181 /* NSImage+IJSVGAdditions.h in Headers */,
59EB762D23905F7300F5AE63 /* IJSVGLayerTree.h in Headers */,
59FDBF0027F3454800AF7038 /* IJSVGColorNode.h in Headers */,
590EB7A427F637EF0047CECF /* IJSVGTransformLayer.h in Headers */,
5978C46A280A241200D25296 /* IJSVGRootLayer.h in Headers */,
59F9EAF82808BB5F00188ACB /* IJSVGViewBox.h in Headers */,
59EB760B23905F7300F5AE63 /* IJSVGStyleSheetSelector.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -602,7 +665,7 @@
594CF466238FF38E009B251B /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1100;
LastUpgradeCheck = 1220;
ORGANIZATIONNAME = "Curtis Hard";
TargetAttributes = {
594CF46E238FF38E009B251B = {
@@ -644,16 +707,19 @@
buildActionMask = 2147483647;
files = (
59EB763123905F7300F5AE63 /* IJSVGText.m in Sources */,
59A24EBD23F480EA0090C374 /* IJSVGUnitSize.m in Sources */,
59EB760223905F7300F5AE63 /* IJSVGCommandVerticalLine.m in Sources */,
59EB75FD23905F7300F5AE63 /* IJSVGPatternLayer.m in Sources */,
59EB763923905F7300F5AE63 /* IJSVGCommandSmoothQuadraticCurve.m in Sources */,
59EB75D823905F7300F5AE63 /* IJSVGStyleSheetRule.m in Sources */,
59EB75FB23905F7300F5AE63 /* IJSVGPath.m in Sources */,
59F36509262F1ABB00BCE3FD /* IJSVGColorType.m in Sources */,
59EB760123905F7300F5AE63 /* IJSVGLayerTree.m in Sources */,
59EB763A23905F7300F5AE63 /* IJSVGForeignObject.m in Sources */,
59EB761123905F7300F5AE63 /* IJSVGCommand.m in Sources */,
59EB760923905F7300F5AE63 /* IJSVGCommandCurve.m in Sources */,
59EB763323905F7300F5AE63 /* IJSVGLinearGradient.m in Sources */,
594A10DB248D7C90001A3181 /* NSImage+IJSVGAdditions.m in Sources */,
59EB761923905F7300F5AE63 /* IJSVGCommandSmoothCurve.m in Sources */,
59EB761323905F7300F5AE63 /* IJSVG.m in Sources */,
59EB763623905F7300F5AE63 /* IJSVGImageLayer.m in Sources */,
@@ -663,22 +729,25 @@
59EB763523905F7300F5AE63 /* IJSVGStyleSheetSelector.m in Sources */,
59E7CFB023B148600077D599 /* IJSVGCommandParser.m in Sources */,
59EB760723905F7300F5AE63 /* IJSVGNode.m in Sources */,
5919E65C23F480330051873A /* IJSVGUnitPoint.m in Sources */,
59EB75E923905F7300F5AE63 /* IJSVGStringAdditions.m in Sources */,
59FDBF0127F3454800AF7038 /* IJSVGColorNode.m in Sources */,
59EB761723905F7300F5AE63 /* IJSVGRadialGradient.m in Sources */,
59EB75E223905F7300F5AE63 /* IJSVGColorList.m in Sources */,
59EB75ED23905F7300F5AE63 /* IJSVGFontConverter.m in Sources */,
59EB763C23905F7300F5AE63 /* IJSVGCommandEllipticalArc.m in Sources */,
59EB75E723905F7300F5AE63 /* IJSVGImage.m in Sources */,
59EB75FF23905F7300F5AE63 /* IJSVGBezierPathAdditions.m in Sources */,
59EB75E623905F7300F5AE63 /* IJSVGStyle.m in Sources */,
59EB75EB23905F7300F5AE63 /* IJSVGShapeLayer.m in Sources */,
59EB75F623905F7300F5AE63 /* IJSVGColor.m in Sources */,
59EB75F323905F7300F5AE63 /* IJSVGGroupLayer.m in Sources */,
591A3E4E25CC91F800AD45B7 /* IJSVGParsing.m in Sources */,
5919E65823F47FF60051873A /* IJSVGUnitRect.m in Sources */,
5978C46B280A241200D25296 /* IJSVGRootLayer.m in Sources */,
59EB762523905F7300F5AE63 /* IJSVGTransform.m in Sources */,
59EB760023905F7300F5AE63 /* IJSVGGradientLayer.m in Sources */,
59EB762B23905F7300F5AE63 /* IJSVGUnitLength.m in Sources */,
59EB75F823905F7300F5AE63 /* IJSVGCommandHorizontalLine.m in Sources */,
59EB75FA23905F7300F5AE63 /* IJSVGWriter.m in Sources */,
59EB764623905F7300F5AE63 /* IJSVGRendering.m in Sources */,
59EB762E23905F7300F5AE63 /* IJSVGExporter.m in Sources */,
59EB761623905F7300F5AE63 /* IJSVGParser.m in Sources */,
@@ -686,14 +755,17 @@
59EB762423905F7300F5AE63 /* IJSVGTransaction.m in Sources */,
59EB75DA23905F7300F5AE63 /* IJSVGDef.m in Sources */,
59EB763723905F7300F5AE63 /* IJSVGCommandClose.m in Sources */,
59F9EAF92808BB5F00188ACB /* IJSVGViewBox.m in Sources */,
59EB764323905F7300F5AE63 /* IJSVGGradient.m in Sources */,
59EB762A23905F7300F5AE63 /* IJSVGGradientUnitLength.m in Sources */,
59EB764223905F7300F5AE63 /* IJSVGStyleSheet.m in Sources */,
59EB764023905F7300F5AE63 /* IJSVGExporterPathInstruction.m in Sources */,
59EB75FE23905F7300F5AE63 /* IJSVGRenderingStyle.m in Sources */,
59EB75F223905F7300F5AE63 /* IJSVGStyleSheetSelectorRaw.m in Sources */,
590EB7A527F637EF0047CECF /* IJSVGTransformLayer.m in Sources */,
59EB75EE23905F7300F5AE63 /* IJSVGImageRep.m in Sources */,
59EB762223905F7300F5AE63 /* IJSVGMath.m in Sources */,
59FCC09527F2394D00BB924E /* IJSVGRootNode.m in Sources */,
59EB763D23905F7300F5AE63 /* IJSVGGroup.m in Sources */,
59EB761F23905F7300F5AE63 /* IJSVGCommandLineTo.m in Sources */,
59EB75F123905F7300F5AE63 /* IJSVGUtils.m in Sources */,
@@ -731,6 +803,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -746,6 +819,7 @@
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREFIX_HEADER = "";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
@@ -792,6 +866,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -805,6 +880,7 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREFIX_HEADER = "";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1130"
LastUpgradeVersion = "1250"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -15,7 +15,7 @@
<key>594CF46E238FF38E009B251B</key>
<dict>
<key>primary</key>
<true/>
<true />
</dict>
</dict>
</dict>
+1 -1
View File
@@ -19,6 +19,6 @@
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 Curtis Hard. All rights reserved.</string>
<string>Copyright © 2020 Curtis Hard. All rights reserved.</string>
</dict>
</plist>
@@ -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_containsAlpha;
- (NSArray*)ijsvg_componentsSplitByWhiteSpace;
- (BOOL)ijsvg_isHexString;
@end
@@ -7,51 +7,38 @@
//
#import "IJSVGStringAdditions.h"
#import "IJSVGUtils.h"
@implementation NSString (IJSVGAdditions)
- (NSArray<NSString*>*)ijsvg_componentsSeparatedByChars:(const char*)aChar
{
NSMutableArray* comp = [[[NSMutableArray alloc] init] autorelease];
NSInteger length = self.length;
unichar* chars = (unichar*)calloc(sizeof(unichar), self.length);
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);
}
char* chars = (char*)self.UTF8String;
if(chars == NULL || strlen(chars) == 0) {
return @[];
}
free(chars);
return comp;
NSMutableArray<NSString*>* strings = nil;
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
{
const char* buffer = self.UTF8String;
unsigned long length = strlen(buffer);
for (int i = 0; i < length; i++) {
if (isalpha(buffer[i])) {
char currentChar;
while((currentChar = *buffer++) ) {
if (isalpha(currentChar)) {
return YES;
}
}
@@ -61,9 +48,9 @@
- (BOOL)ijsvg_isNumeric
{
const char* buffer = self.UTF8String;
unsigned long length = strlen(buffer);
for (int i = 0; i < length; i++) {
if (!isnumber(buffer[i])) {
char currentChar;
while((currentChar = *buffer++) ) {
if (!isnumber(currentChar)) {
return NO;
}
}
@@ -75,4 +62,10 @@
return [self ijsvg_componentsSeparatedByChars:"\t\n\r "];
}
- (BOOL)ijsvg_isHexString
{
const char* chars = self.UTF8String;
return IJSVGCharBufferIsHEX((char*)chars);
}
@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
};
extern NSString* const IJSVGColorCurrentColorName;
@interface IJSVGColor : NSObject
CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightness);
@@ -179,6 +181,7 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
+ (NSString*)colorStringFromColor:(NSColor*)color;
+ (NSColor*)colorFromHEXInteger:(NSInteger)hex;
+ (NSColor*)computeColor:(id)colour;
+ (BOOL)isNoneOrTransparent:(NSString*)string;
+ (NSColor*)colorFromString:(NSString*)string;
+ (NSColor*)colorFromHEXString:(NSString*)string;
+ (NSColor*)colorFromHEXString:(NSString*)string
@@ -8,11 +8,16 @@
#import "IJSVGColor.h"
#import "IJSVGUtils.h"
#import "IJSVGStringAdditions.h"
#import "IJSVGParsing.h"
NSString* const IJSVGColorCurrentColorName = @"currentColor";
@implementation IJSVGColor
static NSDictionary* _colorTree = nil;
CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightness)
{
hue *= (1.f / 360.f);
@@ -239,39 +244,59 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
alpha:a]];
}
+ (BOOL)isNoneOrTransparent:(NSString*)string
{
const char* str = string.lowercaseString.UTF8String;
return strcmp(str, "none") == 0 || strcmp(str, "transparent") == 0;
}
+ (NSColor*)colorFromString:(NSString*)string
{
NSCharacterSet* set = NSCharacterSet.whitespaceAndNewlineCharacterSet;
string = [string stringByTrimmingCharactersInSet:set];
if ([string length] < 3) {
// swap over to C for performance
if(string == nil) {
return nil;
}
NSColor* color = nil;
string = [string lowercaseString];
if ([self.class isHex:string] == NO) {
color = [self.class colorFromPredefinedColorName:string];
if (color != nil) {
return color;
}
const char* oString = string.UTF8String;
if(strlen(oString) == 0) {
return nil;
}
// is simply a clear color, dont fill
if ([string.lowercaseString isEqualToString:@"none"] ||
[string.lowercaseString isEqualToString:@"transparent"]) {
return [self computeColorSpace:NSColor.clearColor];
char* str = IJSVGTimmedCharBufferCreate(oString);
if (IJSVGCharBufferIsHEX(str) == YES) {
(void)free(str), str = NULL;
return [self.class colorFromHEXString:string];
}
// is it RGB?
if ([string hasPrefix:@"rgb"] == YES) {
NSRange range = [IJSVGUtils rangeOfParentheses:string];
NSString* rgbString = [string substringWithRange:range];
NSArray* parts = [rgbString ijsvg_componentsSeparatedByChars:","];
if (IJSVGCharBufferHasPrefix(str, "rgb") == YES) {
NSUInteger count = 0;
IJSVGParsingStringMethod** methods = NULL;
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%";
if (parts.count == 4) {
alpha = parts[3];
}
IJSVGParsingStringMethodsRelease(methods, count);
methods = NULL;
return [self colorFromRString:parts[0]
gString:parts[1]
bString:parts[2]
@@ -279,10 +304,10 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
}
// is it HSL?
if ([string hasPrefix:@"hsl"]) {
if (IJSVGCharBufferHasPrefix(str, "hsl")) {
NSInteger count = 0;
CGFloat* params = [IJSVGUtils commandParameters:string
count:&count];
CGFloat* params = [IJSVGUtils scanFloatsFromCString:str
size:&count];
CGFloat alpha = 1;
if (count == 4) {
alpha = params[3];
@@ -290,21 +315,30 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
// convert HSL to HSB
CGFloat* hsb = IJSVGColorCSSHSLToHSB(params[0], params[1], params[2]);
color = [NSColor colorWithDeviceHue:hsb[0]
saturation:hsb[1]
brightness:hsb[2]
alpha:alpha];
NSColor* color = [NSColor colorWithDeviceHue:hsb[0]
saturation:hsb[1]
brightness:hsb[2]
alpha:alpha];
color = [self computeColorSpace:color];
// memory clean!
free(hsb);
free(params);
(void)free(str), str = NULL;
(void)free(hsb), hsb = NULL;
(void)free(params), params = NULL;
return color;
}
color = [self.class colorFromHEXString:string];
return color;
// is simply a clear color, dont fill
if (strcmp(str, "none") == 0 ||
strcmp(str, "transparent") == 0) {
(void)free(str), str = NULL;
return nil;
}
// could return nil
(void)free(str), str = NULL;
return [self.class colorFromPredefinedColorName:string];
}
+ (NSColor*)colorFromPredefinedColorName:(NSString*)name
@@ -707,14 +741,7 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
+ (BOOL)isHex:(NSString*)string
{
const char* validList = "0123456789ABCDEFabcdef#";
for (NSInteger i = 0; i < string.length; i++) {
char c = [string characterAtIndex:i];
if (strchr(validList, c) == NULL) {
return NO;
}
}
return YES;
return string.ijsvg_isHexString;
}
+ (unsigned long)lengthOfHEXInteger:(NSUInteger)hex
@@ -760,28 +787,38 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
containsAlphaComponent:(BOOL*)containsAlphaComponent
{
// absolutely no string
if (string == nil || string.length == 0 || ![self.class isHex:string]) {
if(string == nil) {
return nil;
}
char* str = (char*)string.UTF8String;
size_t length = strlen(str);
if (length == 0 || IJSVGCharBufferIsHEX(str) == NO) {
return nil;
}
if ([string hasPrefix:@"#"] == YES) {
string = [string substringFromIndex:1];
// remove the hash from the front of the string
if(str[0] == '#') {
length--;
str++;
}
// whats the length?
NSUInteger length = string.length;
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) {
// shorthand...
NSMutableString* str = [[[NSMutableString alloc] init] autorelease];
for (NSInteger i = 0; i < length; i++) {
NSString* sub = [string substringWithRange:NSMakeRange(i, 1)];
[str appendFormat:@"%@%@", sub, sub];
char* chars = NULL;
chars = (char*)calloc(sizeof(char),length*2+1);
for(int i = 0; i < length; i++) {
chars[i*2] = chars[i*2+1] = str[i];
}
string = str;
hex = strtoul(chars, NULL, 16);
(void)free(chars), chars = NULL;
} else {
hex = strtoul(str, NULL, 16);
}
// now convert rest to hex
unsigned long hex = [self HEXFromArbitraryHexString:string];
if (containsAlphaComponent != nil) {
*containsAlphaComponent = [self HEXContainsAlphaComponent:hex];
}
@@ -6,16 +6,19 @@
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import "IJSVGColor.h"
#import <IJSVG/IJSVGColor.h>
#import <IJSVG/IJSVGColorType.h>
#import <Foundation/Foundation.h>
@interface IJSVGColorList : NSObject <NSCopying> {
@private
NSMutableDictionary<NSColor*, NSColor*>* _replacementColorTree;
NSMutableSet<NSColor*>* _colors;
NSMutableSet<IJSVGColorType*>* _colors;
}
@property (nonatomic, assign, readonly) NSUInteger count;
- (NSColor*)proposedColorForColor:(NSColor*)color;
- (void)removeAllReplacementColors;
- (void)removeReplacementColor:(NSColor*)color;
@@ -25,7 +28,8 @@
clearExistingColors:(BOOL)clearExistingColors;
- (void)addColorsFromList:(IJSVGColorList*)sheet;
- (NSSet<NSColor*>*)colors;
- (void)addColor:(NSColor*)color;
- (NSSet<IJSVGColorType*>*)colors;
- (void)addColor:(IJSVGColorType*)color;
- (NSDictionary<NSColor*, NSColor*>*)replacementColors;
@end
@@ -89,24 +89,48 @@
}
}
- (NSSet<NSColor*>*)colors
- (NSSet<IJSVGColorType*>*)colors
{
return [NSSet setWithSet:_colors];
}
- (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
@@ -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.
//
#import "IJSVGCommandParser.h"
#import "IJSVGPath.h"
#import <IJSVG/IJSVGCommandParser.h>
#import <IJSVG/IJSVGPath.h>
#import <Foundation/Foundation.h>
static const NSInteger IJSVGCustomVariableParameterCount = NSNotFound;
@@ -17,20 +17,11 @@ typedef NS_ENUM(NSInteger, IJSVGCommandType) {
kIJSVGCommandTypeRelative
};
@interface IJSVGCommand : NSObject {
NSString* commandString;
char command;
CGFloat* parameters;
NSInteger parameterCount;
NSArray<IJSVGCommand*>* subCommands;
NSInteger requiredParameters;
IJSVGCommandType type;
IJSVGCommand* previousCommand;
@interface IJSVGCommand : NSObject <NSCopying> {
@private
NSInteger _currentIndex;
BOOL isSubCommand;
}
@property (nonatomic, copy) NSString* commandString;
@property (nonatomic, assign) char command;
@property (nonatomic, assign) CGFloat* parameters;
@property (nonatomic, assign) NSInteger parameterCount;
@@ -49,17 +40,30 @@ typedef NS_ENUM(NSInteger, IJSVGCommandType) {
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(IJSVGPath*)path;
path:(CGMutablePathRef)path;
+ (void)parseParams:(CGFloat*)params
paramCount:(NSInteger)paramCount
intoArray:(NSMutableArray<IJSVGCommand*>*)commands
parentCommand:(IJSVGCommand*)parentCommand;
- (id)initWithCommandString:(NSString*)str
dataStream:(IJSVGPathDataStream*)dataStream;
+ (NSArray<IJSVGCommand*>*)commandsForDataCharacters:(const char*)buffer;
+ (NSArray<IJSVGCommand*>*)commandsForDataCharacters:(const char*)buffer
dataStream:(IJSVGPathDataStream*)dataStream;
+ (CGMutablePathRef)newPathForCommandsArray:(NSArray<IJSVGCommand*>*)commands;
+ (NSArray<IJSVGCommand*>*)convertCommands:(NSArray<IJSVGCommand*>*)commands
toUnits:(IJSVGUnitType)unitType
bounds:(CGRect)bounds;
- (id)initWithCommandStringBuffer:(const char*)str
dataStream:(IJSVGPathDataStream*)dataStream;
- (IJSVGCommand*)subcommandWithParameters:(CGFloat*)subParams
paramCount:(NSInteger)paramCount
previousCommand:(IJSVGCommand*)command;
- (void)convertToUnits:(IJSVGUnitType)units
boundingBox:(CGRect)boundingBox;
- (IJSVGCommand*)commandByConvertingToUnits:(IJSVGUnitType)unitType
boundingBox:(CGRect)boundingBox;
- (CGFloat)readFloat;
- (NSPoint)readPoint;
@@ -22,15 +22,6 @@
@implementation IJSVGCommand
@synthesize commandString;
@synthesize command;
@synthesize parameterCount;
@synthesize parameters;
@synthesize subCommands;
@synthesize type;
@synthesize previousCommand;
@synthesize isSubCommand;
+ (BOOL)requiresCustomParameterParsing
{
return NO;
@@ -51,7 +42,7 @@
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
path:(CGMutablePathRef)path
{
}
@@ -101,36 +92,122 @@
return nil;
}
+ (CGMutablePathRef)newPathForCommandsArray:(NSArray<IJSVGCommand*>*)commands
{
CGMutablePathRef path = CGPathCreateMutable();
IJSVGCommand* preCommand = nil;
for(IJSVGCommand* command in commands) {
for (IJSVGCommand* subCommand in command.subCommands) {
[command.class runWithParams:subCommand.parameters
paramCount:subCommand.parameterCount
command:subCommand
previousCommand:preCommand
type:subCommand.type
path:path];
preCommand = subCommand;
}
}
return path;
}
+ (NSArray<IJSVGCommand*>*)commandsForDataCharacters:(const char*)buffer
{
IJSVGPathDataStream* stream = IJSVGPathDataStreamCreateDefault();
NSArray<IJSVGCommand*>* commands = [self commandsForDataCharacters:buffer
dataStream:stream];
IJSVGPathDataStreamRelease(stream);
return commands;
}
+ (NSArray<IJSVGCommand*>*)commandsForDataCharacters:(const char*)buffer
dataStream:(IJSVGPathDataStream*)dataStream
{
NSMutableArray<IJSVGCommand*>* commands = [[[NSMutableArray alloc] init] autorelease];
NSUInteger len = strlen(buffer);
NSUInteger lastIndex = len - 1;
// make sure we plus 1 for the null byte
char* charBuffer = (char*)malloc(sizeof(char)*(len + 1));
NSInteger start = 0;
for (NSInteger i = 0; i < len; i++) {
char nextChar = buffer[i + 1];
BOOL atEnd = i == lastIndex;
BOOL isStartCommand = IJSVGIsLegalCommandCharacter(nextChar);
if (isStartCommand == YES || atEnd == YES) {
// copy memory from current buffer
NSInteger index = ((i + 1) - start);
memcpy(&charBuffer[0], &buffer[start], sizeof(char)*index);
charBuffer[index] = '\0';
// create the command from the substring
unsigned long length = index + 1;
size_t mlength = sizeof(char)*length;
char* commandString = (char*)malloc(mlength);
memcpy(commandString, &charBuffer[0], mlength);
// reset start position
start = (i + 1);
// previous command is actual subcommand
Class commandClass = [IJSVGCommand commandClassForCommandChar:commandString[0]];
IJSVGCommand* command = nil;
command = (IJSVGCommand*)[[[commandClass alloc] initWithCommandStringBuffer:commandString
dataStream:dataStream] autorelease];
[commands addObject:command];
// free the memory as at this point, we are done with it
(void)free(commandString), commandString = NULL;
}
}
(void)free(charBuffer), charBuffer = NULL;
return commands;
}
+ (NSArray<IJSVGCommand*>*)convertCommands:(NSArray<IJSVGCommand*>*)commands
toUnits:(IJSVGUnitType)unitType
bounds:(CGRect)bounds
{
NSMutableArray<IJSVGCommand*>* newCommands = nil;
newCommands = [[[NSMutableArray alloc] initWithCapacity:commands.count] autorelease];
for(IJSVGCommand* command in commands) {
IJSVGCommand* nCommand = [command commandByConvertingToUnits:unitType
boundingBox:bounds];
[newCommands addObject:nCommand];
}
return newCommands;
}
- (void)dealloc
{
(void)([commandString release]), commandString = nil;
(void)([subCommands release]), subCommands = nil;
if (parameters) {
(void)(free(parameters)), parameters = nil;
(void)([_subCommands release]), _subCommands = nil;
if (_parameters) {
(void)(free(_parameters)), _parameters = nil;
}
[super dealloc];
}
- (id)initWithCommandString:(NSString*)str
dataStream:(IJSVGPathDataStream*)dataStream
- (id)initWithCommandStringBuffer:(const char*)str
dataStream:(IJSVGPathDataStream*)dataStream
{
if ((self = [super init]) != nil) {
// work out the basics
_currentIndex = 0;
command = [str characterAtIndex:0];
type = [IJSVGUtils typeForCommandChar:command];
_command = str[0];
_type = [IJSVGUtils typeForCommandChar:_command];
NSInteger sets = 0;
NSInteger paramCount = [self.class requiredParameterCount];
IJSVGPathDataSequence* sequence = [self.class pathDataSequence];
parameters = IJSVGParsePathDataStreamSequence(str.UTF8String, str.length,
dataStream, sequence, [self.class requiredParameterCount], &sets);
_parameters = IJSVGParsePathDataStreamSequence(str, strlen(str),
dataStream, sequence, paramCount, &sets);
if (sets <= 1) {
CGFloat* subParams = [self parametersFromIndexOffset:0];
IJSVGCommand* command = [self subcommandWithParameters:subParams
paramCount:paramCount
previousCommand:nil];
subCommands = @[ command ].retain;
_subCommands = @[ command ].retain;
} else {
NSMutableArray<IJSVGCommand*>* subCommandArray = nil;
subCommandArray = [[NSMutableArray alloc] initWithCapacity:sets].autorelease;
@@ -154,12 +231,43 @@
}
// store the retained value
subCommands = subCommandArray.copy;
_subCommands = subCommandArray.copy;
}
}
return self;
}
- (void)setSubCommands:(NSArray<IJSVGCommand*>*)subCommands
{
(void)[_subCommands release], _subCommands = nil;
_subCommands = subCommands.retain;
}
- (id)copyWithZone:(NSZone *)zone
{
IJSVGCommand* command = [[self.class alloc] init];
command.type = self.type;
command.command = self.command;
command.isSubCommand = self.isSubCommand;
command.parameterCount = self.parameterCount;
size_t memsize = sizeof(CGFloat)*self.parameterCount;
command.parameters = (CGFloat*)malloc(memsize);
memcpy(command.parameters, self.parameters, memsize);
IJSVGCommand* lastCommand = nil;
NSMutableArray<IJSVGCommand*>* subcommands = nil;
subcommands = [[[NSMutableArray alloc] initWithCapacity:self.subCommands.count] autorelease];
for(IJSVGCommand* subcommand in self.subCommands) {
IJSVGCommand* subCopy = [[subcommand copy] autorelease];
subCopy.previousCommand = lastCommand;
subCopy.isSubCommand = lastCommand != nil;
[subcommands addObject:subCopy];
}
command.subCommands = subcommands;
return command;
}
- (CGFloat*)parametersFromIndexOffset:(NSInteger)index
{
CGFloat* subParams = 0;
@@ -188,15 +296,15 @@
- (CGFloat)readFloat
{
CGFloat f = parameters[_currentIndex];
CGFloat f = _parameters[_currentIndex];
_currentIndex++;
return f;
}
- (NSPoint)readPoint
{
CGFloat x = parameters[_currentIndex];
CGFloat y = parameters[_currentIndex + 1];
CGFloat x = _parameters[_currentIndex];
CGFloat y = _parameters[_currentIndex + 1];
_currentIndex += 2;
return NSMakePoint(x, y);
}
@@ -211,4 +319,34 @@
_currentIndex = 0;
}
- (void)convertToUnits:(IJSVGUnitType)units
boundingBox:(CGRect)boundingBox
{
for(IJSVGCommand* command in _subCommands) {
[command convertToUnits:units
boundingBox:boundingBox];
}
}
- (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;
}
- (IJSVGCommand*)commandByConvertingToUnits:(IJSVGUnitType)unitType
boundingBox:(CGRect)boundingBox
{
IJSVGCommand* copy = self.copy;
[copy convertToUnits:unitType
boundingBox:boundingBox];
return [copy autorelease];
}
@end
@@ -20,9 +20,9 @@
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
path:(CGMutablePathRef)path
{
[path close];
CGPathCloseSubpath(path);
}
@end
@@ -20,17 +20,19 @@
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
path:(CGMutablePathRef)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[path.path curveToPoint:NSMakePoint(params[4], params[5])
controlPoint1:NSMakePoint(params[0], params[1])
controlPoint2:NSMakePoint(params[2], params[3])];
CGPathAddCurveToPoint(path, NULL, params[0], params[1],
params[2], params[3],
params[4], params[5]);
return;
}
[path.path relativeCurveToPoint:NSMakePoint(params[4], params[5])
controlPoint1:NSMakePoint(params[0], params[1])
controlPoint2:NSMakePoint(params[2], params[3])];
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPathAddCurveToPoint(path, NULL,
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
@@ -20,7 +20,8 @@ static IJSVGPathDataSequence* _sequence;
+ (IJSVGPathDataSequence*)pathDataSequence
{
if(_sequence == NULL) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sequence = (IJSVGPathDataSequence*)malloc(sizeof(IJSVGPathDataSequence) * 7);
_sequence[0] = kIJSVGPathDataSequenceTypeFloat;
_sequence[1] = kIJSVGPathDataSequenceTypeFloat;
@@ -29,96 +30,110 @@ static IJSVGPathDataSequence* _sequence;
_sequence[4] = kIJSVGPathDataSequenceTypeFlag;
_sequence[5] = kIJSVGPathDataSequenceTypeFloat;
_sequence[6] = kIJSVGPathDataSequenceTypeFloat;
}
});
return _sequence;
}
// modified from https://github.com/SVGKit/SVGKit/blob/880c94a5b77b6f22beb491a7a7e02ace220c32af/Source/Parsers/SVGKPointsAndPathsParser.m
+ (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
path:(CGMutablePathRef)path
{
CGPoint radii = CGPointZero;
CGPoint arcEndPoint = CGPointZero;
CGPoint arcStartPoint = path.currentPoint;
CGFloat xAxisRotation = 0;
BOOL largeArcFlag = 0;
BOOL sweepFlag = 0;
CGPoint pathCurrentPoint = CGPathGetCurrentPoint(path);
CGFloat xAxisRotation = 0.f;
BOOL largeArcFlag = NO;
BOOL sweepFlag = NO;
radii = [currentCommand readPoint];
xAxisRotation = [currentCommand readFloat];
largeArcFlag = [currentCommand readBOOL];
sweepFlag = [currentCommand readBOOL];
arcEndPoint = [currentCommand readPoint];
CGFloat rx = fabs(radii.x);
CGFloat ry = fabs(radii.y);
xAxisRotation *= M_PI / 180.f;
xAxisRotation = fmod(xAxisRotation, 2.f * M_PI);
if (type == kIJSVGCommandTypeRelative) {
arcEndPoint.x += path.currentPoint.x;
arcEndPoint.y += path.currentPoint.y;
arcEndPoint.x += pathCurrentPoint.x;
arcEndPoint.y += pathCurrentPoint.y;
}
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);
CGFloat radiiAdjustment = pow(currentPoint.x, 2) / pow(radii.x, 2) + pow(currentPoint.y, 2) / pow(radii.y, 2);
radii.x *= (radiiAdjustment > 1) ? sqrt(radiiAdjustment) : 1;
radii.y *= (radiiAdjustment > 1) ? sqrt(radiiAdjustment) : 1;
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)));
sweep = (sweep != sweep) ? 0 : sweep;
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 startAngle = angle(CGPointMake(1, 0), CGPointMake((currentPoint.x - preCenterPoint.x) / radii.x, (currentPoint.y - preCenterPoint.y) / radii.y));
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)) {
CGFloat x1 = pathCurrentPoint.x;
CGFloat y1 = pathCurrentPoint.y;
CGFloat x2 = arcEndPoint.x;
CGFloat y2 = arcEndPoint.y;
if (rx == 0.f || ry == 0.f) {
CGPathAddLineToPoint(path, NULL, x2, y2);
return;
}
CGFloat cosPhi = cos(xAxisRotation);
CGFloat sinPhi = sin(xAxisRotation);
CGFloat x1p = cosPhi * (x1 - x2) / 2.f + sinPhi * (y1 - y2) / 2.f;
CGFloat y1p = -sinPhi * (x1 - x2) / 2.f + cosPhi * (y1 - y2) / 2.f;
CGFloat rx_2 = rx * rx;
CGFloat ry_2 = ry * ry;
CGFloat xp_2 = x1p * x1p;
CGFloat yp_2 = y1p * y1p;
CGFloat radius = MAX(radii.x, radii.y);
CGPoint scale = (radii.x > radii.y) ? CGPointMake(1, radii.y / radii.x) : CGPointMake(radii.x / radii.y, 1);
CGFloat delta = xp_2 / rx_2 + yp_2 / ry_2;
if (delta > 1.f) {
rx *= sqrt(delta);
ry *= sqrt(delta);
rx_2 = rx * rx;
ry_2 = ry * ry;
}
CGFloat sign = (largeArcFlag == sweepFlag) ? -1.f : 1.f;
CGFloat numerator = MAX(0.f, rx_2 * ry_2 - rx_2 * yp_2 - ry_2 * xp_2);
CGFloat denom = rx_2 * yp_2 + ry_2 * xp_2;
CGFloat lhs = denom == 0.f ? 0.f : sign * sqrt(numerator / denom);
CGFloat cxp = lhs * (rx * y1p) / ry;
CGFloat cyp = lhs * -((ry * x1p) / rx);
CGFloat cx = cosPhi * cxp + -sinPhi * cyp + (x1 + x2) / 2.f;
CGFloat cy = cxp * sinPhi + cyp * cosPhi + (y1 + y2) / 2.f;
NSAffineTransform* trans = NSAffineTransform.transform;
[trans translateXBy:-centerPoint.x yBy:-centerPoint.y];
[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);
trans = NSAffineTransform.transform;
[trans rotateByRadians:-xAxisRotation];
[path.path transformUsingAffineTransform:trans];
trans = NSAffineTransform.transform;
[trans scaleXBy:(1 / scale.x) yBy:(1 / scale.y)];
[path.path transformUsingAffineTransform:trans];
[path.path appendBezierPathWithArcWithCenter:NSZeroPoint
radius:radius
startAngle:radians_to_degrees(startAngle)
endAngle:radians_to_degrees(startAngle + angleDelta)
clockwise:!sweepFlag];
trans = NSAffineTransform.transform;
[trans scaleXBy:scale.x yBy:scale.y];
[path.path transformUsingAffineTransform:trans];
trans = NSAffineTransform.transform;
[trans rotateByRadians:xAxisRotation];
[path.path transformUsingAffineTransform:trans];
trans = NSAffineTransform.transform;
[trans translateXBy:centerPoint.x yBy:centerPoint.y];
[path.path transformUsingAffineTransform:trans];
CGPathAddRelativeArc(path, &transform, 0.f, 0.f, 1.f,
startAngle, angleDelta);
}
@end
@@ -20,13 +20,15 @@
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
path:(CGMutablePathRef)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[path.path lineToPoint:NSMakePoint(params[0], path.currentPoint.y)];
CGPathAddLineToPoint(path, NULL, params[0], CGPathGetCurrentPoint(path).y);
return;
}
[path.path relativeLineToPoint:NSMakePoint(params[0], 0.f)];
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPathAddLineToPoint(path, NULL, currentPoint.x + params[0],
currentPoint.y);
}
@end
@@ -20,15 +20,26 @@
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
path:(CGMutablePathRef)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[path.path lineToPoint:NSMakePoint(params[0], params[1])];
CGPathAddLineToPoint(path, NULL, params[0], params[1]);
return;
}
NSPoint point = NSMakePoint(path.currentPoint.x + params[0],
path.currentPoint.y + params[1]);
[path.path lineToPoint:point];
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPathAddLineToPoint(path, NULL, currentPoint.x + params[0],
currentPoint.y + params[1]);
}
- (void)convertToUnits:(IJSVGUnitType)units
boundingBox:(CGRect)boundingBox
{
if(units == IJSVGUnitObjectBoundingBox) {
self.parameters[0] = [[IJSVGUnitLength unitWithPercentageFloat:self.parameters[0]] computeValue:boundingBox.size.width];
self.parameters[1] = [[IJSVGUnitLength unitWithPercentageFloat:self.parameters[1]] computeValue:boundingBox.size.height];
}
[super convertToUnits:units
boundingBox:boundingBox];
}
@end
@@ -21,7 +21,7 @@
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
path:(CGMutablePathRef)path
{
// move to's allow more then one move to, but if there are more then one,
// we need to run the line to instead...who knew!
@@ -35,17 +35,31 @@
return;
}
// actual move to command
if (type == kIJSVGCommandTypeAbsolute) {
[path.path moveToPoint:NSMakePoint(params[0], params[1])];
// actual move to command - do a moveToPoint only
// if the type is absolute, or its possible the type is
// relative but there is no previous command which means
// there is no current point. Asking for current point on an empty
// path will result in an exception being thrown
if (type == kIJSVGCommandTypeAbsolute || command == nil) {
CGPathMoveToPoint(path, NULL,
params[0], params[1]);
return;
}
@try {
[path.path relativeMoveToPoint:NSMakePoint(params[0], params[1])];
}
@catch (NSException* exception) {
[path.path moveToPoint:NSMakePoint(params[0], params[1])];
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPathMoveToPoint(path, NULL,
currentPoint.x + params[0],
currentPoint.y + params[1]);
}
- (void)convertToUnits:(IJSVGUnitType)units
boundingBox:(CGRect)boundingBox
{
if(units == IJSVGUnitObjectBoundingBox) {
self.parameters[0] = [[IJSVGUnitLength unitWithPercentageFloat:self.parameters[0]] computeValue:boundingBox.size.width];
self.parameters[1] = [[IJSVGUnitLength unitWithPercentageFloat:self.parameters[1]] computeValue:boundingBox.size.height];
}
[super convertToUnits:units
boundingBox:boundingBox];
}
@end
@@ -21,15 +21,17 @@
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
path:(CGMutablePathRef)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[path.path addQuadCurveToPoint:NSMakePoint(params[2], params[3])
controlPoint:NSMakePoint(params[0], params[1])];
CGPathAddQuadCurveToPoint(path, NULL, params[0], params[1],
params[2], params[3]);
return;
}
[path.path addQuadCurveToPoint:NSMakePoint(path.currentPoint.x + params[2], path.currentPoint.y + params[3])
controlPoint:NSMakePoint(path.currentPoint.x + params[0], path.currentPoint.y + params[1])];
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPathAddQuadCurveToPoint(path, NULL,
currentPoint.x + params[0], currentPoint.y + params[1],
currentPoint.x + params[2], currentPoint.y + params[3]);
}
@end
@@ -22,43 +22,43 @@
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
path:(CGMutablePathRef)path
{
NSPoint firstControl = NSMakePoint(path.currentPoint.x, path.currentPoint.y);
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPoint firstControl = CGPointMake(currentPoint.x, currentPoint.y);
if (command != nil) {
if (command.class == [IJSVGCommandCurve class] || command.class == self.class) {
if (command.class == [IJSVGCommandCurve class]) {
if (command.type == kIJSVGCommandTypeAbsolute) {
firstControl = NSMakePoint(-1 * command.parameters[2] + 2 * path.currentPoint.x,
-1 * command.parameters[3] + 2 * path.currentPoint.y);
firstControl = CGPointMake(-1 * command.parameters[2] + 2 * currentPoint.x,
-1 * command.parameters[3] + 2 * currentPoint.y);
} else {
NSPoint oldPoint = NSMakePoint(path.currentPoint.x - command.parameters[4],
path.currentPoint.y - command.parameters[5]);
firstControl = NSMakePoint(-1 * (command.parameters[2] + oldPoint.x) + 2 * path.currentPoint.x,
-1 * (command.parameters[3] + oldPoint.y) + 2 * path.currentPoint.y);
NSPoint oldPoint = CGPointMake(currentPoint.x - command.parameters[4],
currentPoint.y - command.parameters[5]);
firstControl = CGPointMake(-1 * (command.parameters[2] + oldPoint.x) + 2 * currentPoint.x,
-1 * (command.parameters[3] + oldPoint.y) + 2 * currentPoint.y);
}
} else {
if (command.type == kIJSVGCommandTypeAbsolute) {
firstControl = NSMakePoint(-1 * command.parameters[0] + 2 * path.currentPoint.x,
-1 * command.parameters[1] + 2 * path.currentPoint.y);
firstControl = CGPointMake(-1 * command.parameters[0] + 2 * currentPoint.x,
-1 * command.parameters[1] + 2 * currentPoint.y);
} else {
NSPoint oldPoint = NSMakePoint(path.currentPoint.x - command.parameters[2],
path.currentPoint.y - command.parameters[3]);
firstControl = NSMakePoint(-1 * (command.parameters[0] + oldPoint.x) + 2 * path.currentPoint.x,
-1 * (command.parameters[1] + oldPoint.y) + 2 * path.currentPoint.y);
NSPoint oldPoint = CGPointMake(currentPoint.x - command.parameters[2],
currentPoint.y - command.parameters[3]);
firstControl = CGPointMake(-1 * (command.parameters[0] + oldPoint.x) + 2 * currentPoint.x,
-1 * (command.parameters[1] + oldPoint.y) + 2 * currentPoint.y);
}
}
}
}
if (type == kIJSVGCommandTypeAbsolute) {
[path.path curveToPoint:NSMakePoint(params[2], params[3])
controlPoint1:NSMakePoint(firstControl.x, firstControl.y)
controlPoint2:NSMakePoint(params[0], params[1])];
CGPathAddCurveToPoint(path, NULL, firstControl.x, firstControl.y,
params[0], params[1], params[2], params[3]);
return;
}
[path.path curveToPoint:NSMakePoint(path.currentPoint.x + params[2], path.currentPoint.y + params[3])
controlPoint1:NSMakePoint(firstControl.x, firstControl.y)
controlPoint2:NSMakePoint(path.currentPoint.x + params[0], path.currentPoint.y + params[1])];
CGPathAddCurveToPoint(path, NULL, firstControl.x, firstControl.y,
currentPoint.x + params[0], currentPoint.y + params[1],
currentPoint.x + params[2], currentPoint.y + params[3]);
}
@end
@@ -22,35 +22,37 @@
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
path:(CGMutablePathRef)path
{
NSPoint commandPoint = NSMakePoint(path.currentPoint.x, path.currentPoint.y);
CGPoint lastControlPoint = IJSVGPathGetLastQuadraticCommandPoint(path);
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPoint commandPoint = CGPointMake(currentPoint.x, currentPoint.y);
if (command != nil) {
if (command.class == IJSVGCommandQuadraticCurve.class) {
// quadratic curve
if (command.type == kIJSVGCommandTypeAbsolute) {
commandPoint = NSMakePoint(-1 * command.parameters[0] + 2 * path.currentPoint.x,
-1 * command.parameters[1] + 2 * path.currentPoint.y);
commandPoint = NSMakePoint(-1 * command.parameters[0] + 2 * currentPoint.x,
-1 * command.parameters[1] + 2 * currentPoint.y);
} else {
NSPoint oldPoint = CGPointMake(path.currentPoint.x - command.parameters[2],
path.currentPoint.y - command.parameters[3]);
commandPoint = CGPointMake(-1 * (command.parameters[0] + oldPoint.x) + 2 * (path.currentPoint.x),
-1 * (command.parameters[1] + oldPoint.y) + 2 * path.currentPoint.y);
CGPoint oldPoint = CGPointMake(currentPoint.x - command.parameters[2],
currentPoint.y - command.parameters[3]);
commandPoint = CGPointMake(-1 * (command.parameters[0] + oldPoint.x) + 2 * (currentPoint.x),
-1 * (command.parameters[1] + oldPoint.y) + 2 * currentPoint.y);
}
} else if (command.class == self.class) {
// smooth quadratic curve
commandPoint = CGPointMake(-1 * (path.lastControlPoint.x) + 2 * (path.currentPoint.x),
-1 * (path.lastControlPoint.y) + 2 * path.currentPoint.y);
commandPoint = CGPointMake(-1 * (lastControlPoint.x) + 2 * (currentPoint.x),
-1 * (lastControlPoint.y) + 2 * currentPoint.y);
}
}
path.lastControlPoint = commandPoint;
// path.lastControlPoint = commandPoint;
if (type == kIJSVGCommandTypeAbsolute) {
[path.path addQuadCurveToPoint:NSMakePoint(params[0], params[1])
controlPoint:commandPoint];
CGPathAddQuadCurveToPoint(path, NULL, commandPoint.x, commandPoint.y,
params[0], params[1]);
return;
}
[path.path addQuadCurveToPoint:NSMakePoint(path.currentPoint.x + params[0], path.currentPoint.y + params[1])
controlPoint:commandPoint];
CGPathAddQuadCurveToPoint(path, NULL, commandPoint.x, commandPoint.y,
currentPoint.x + params[0], currentPoint.y + params[1]);
}
@end
@@ -20,13 +20,14 @@
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
path:(CGMutablePathRef)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[path.path lineToPoint:NSMakePoint(path.currentPoint.x, params[0])];
CGPathAddLineToPoint(path, NULL, CGPathGetCurrentPoint(path).x, params[0]);
return;
}
[path.path relativeLineToPoint:NSMakePoint(0.f, params[0])];
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPathAddLineToPoint(path, NULL, currentPoint.x, currentPoint.y + params[0]);
}
@end
+49 -36
View File
@@ -6,54 +6,50 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGBezierPathAdditions.h"
#import "IJSVGColorList.h"
#import "IJSVGExporter.h"
#import "IJSVGGradientLayer.h"
#import "IJSVGGroupLayer.h"
#import "IJSVGImageLayer.h"
#import "IJSVGLayerTree.h"
#import "IJSVGParser.h"
#import "IJSVGRendering.h"
#import "IJSVGRenderingStyle.h"
#import "IJSVGTransaction.h"
#import <IJSVG/IJSVGColorList.h>
#import <IJSVG/IJSVGRootNode.h>
#import <IJSVG/IJSVGExporter.h>
#import <IJSVG/IJSVGGradientLayer.h>
#import <IJSVG/IJSVGGroupLayer.h>
#import <IJSVG/IJSVGRootLayer.h>
#import <IJSVG/IJSVGImageLayer.h>
#import <IJSVG/IJSVGLayerTree.h>
#import <IJSVG/IJSVGParser.h>
#import <IJSVG/IJSVGRendering.h>
#import <IJSVG/IJSVGRenderingStyle.h>
#import <IJSVG/IJSVGTransaction.h>
#import <Foundation/Foundation.h>
@class IJSVG;
typedef NS_OPTIONS(NSInteger, IJSVGMatchPropertiesMask) {
IJSVGMatchPropertyNone = 0,
IJSVGMatchPropertyContainsMaskedElement = 1 << 0,
IJSVGMatchPropertyContainsStrokedElement = 1 << 1
};
@protocol IJSVGDelegate <NSObject, IJSVGParserDelegate>
@optional
- (BOOL)svg:(IJSVG*)svg
shouldHandleForeignObject:(IJSVGForeignObject*)foreignObject;
- (void)svg:(IJSVG*)svg
handleForeignObject:(IJSVGForeignObject*)foreignObject
document:(NSXMLDocument*)document;
- (void)svg:(IJSVG*)svg
foundSubSVG:(IJSVG*)subSVG
withSVGString:(NSString*)subSVGString;
foundSubSVG:(IJSVG*)subSVG
withSVGString:(NSString*)subSVGString;
@end
@interface IJSVG : NSObject <NSPasteboardWriting, IJSVGParserDelegate> {
@private
IJSVGParser* _group;
IJSVGRootNode* _rootNode;
CGFloat _scale;
CGFloat _clipScale;
id<IJSVGDelegate> _delegate;
IJSVGLayer* _layerTree;
IJSVGLayerTree* _layerTree;
CGRect _viewBox;
CGSize _proposedViewSize;
CGFloat _backingScaleFactor;
CGFloat _lastProposedBackingScale;
IJSVGRenderQuality _lastProposedRenderQuality;
CGFloat _backingScale;
NSMutableDictionary* _replacementColors;
struct {
unsigned int shouldHandleForeignObject : 1;
unsigned int handleForeignObject : 1;
unsigned int shouldHandleSubSVG : 1;
} _respondsTo;
}
@@ -68,7 +64,12 @@
// fillColor, strokeColor, pattern and gradient fill
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
@property (nonatomic, assign) BOOL clipToViewport;
@property (nonatomic, retain) IJSVGRenderingStyle* style;
@property (nonatomic, retain) IJSVGRenderingStyle* renderingStyle;
@property (nonatomic, readonly) IJSVGUnitSize * intrinsicSize;
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* desc;
@property (nonatomic, retain) IJSVGLayerTree* layerTree;
@property (nonatomic, retain) IJSVGRootLayer* rootLayer;
- (void)prepForDrawingInView:(NSView*)view;
- (BOOL)isFont;
@@ -76,13 +77,10 @@
- (NSRect)viewBox;
- (NSArray<IJSVGPath*>*)glyphs;
- (NSString*)identifier;
- (IJSVGLayer*)layer;
- (IJSVGLayer*)layerWithTree:(IJSVGLayerTree*)tree;
- (NSArray<IJSVG*>*)subSVGs:(BOOL)recursive;
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options;
- (CGFloat)computeBackingScale:(CGFloat)scale;
- (void)discardDOM;
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (id)svgNamed:(NSString*)string;
+ (id)svgNamed:(NSString*)string
@@ -101,6 +99,10 @@
- (id)initWithSVGString:(NSString*)string
error:(NSError**)error;
- (id)initWithSVGData:(NSData*)data;
- (id)initWithSVGData:(NSData*)data
error:(NSError**)error;
- (id)initWithFile:(NSString*)file;
- (id)initWithFile:(NSString*)file
error:(NSError**)error;
@@ -117,11 +119,21 @@
- (id)initWithFilePathURL:(NSURL*)aURL
error:(NSError**)error
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
error:(NSError**)error;
- (NSImage*)imageWithSize:(NSSize)aSize
flipped:(BOOL)flipped;
- (NSImage*)imageWithSize:(NSSize)aSize
flipped:(BOOL)flipped
error:(NSError**)error;
- (NSImage*)imageByMaintainingAspectRatioWithSize:(NSSize)aSize
flipped:(BOOL)flipped
error:(NSError**)error;
@@ -146,12 +158,13 @@
- (NSData*)PDFDataWithRect:(NSRect)rect
error:(NSError**)error;
- (void)beginVectorDraw;
- (void)endVectorDraw;
- (NSRect)computeOriginalDrawingFrameWithSize:(NSSize)aSize;
- (void)setNeedsDisplay;
// colors
- (IJSVGColorList*)computedColorList:(BOOL*)hasPatternFills;
- (IJSVGColorList*)colorList;
- (void)performBlock:(dispatch_block_t)block;
// matching
- (BOOL)matchesPropertiesWithMask:(IJSVGMatchPropertiesMask)mask;
@end
+394 -285
View File
@@ -12,23 +12,29 @@
@implementation IJSVG
@synthesize renderingBackingScaleHelper;
@synthesize clipToViewport;
@synthesize renderQuality;
@synthesize style = _style;
// these are explicitly implemented
@synthesize title = _title;
@synthesize desc = _desc;
- (void)dealloc
{
// this can all be called on the background thread to be released
(void)([renderingBackingScaleHelper release]),
renderingBackingScaleHelper = nil;
BOOL hasTransaction = IJSVGBeginTransaction();
(void)([_renderingBackingScaleHelper release]), _renderingBackingScaleHelper = nil;
(void)([_replacementColors release]), _replacementColors = nil;
(void)([_style release]), _style = nil;
(void)([_group release]), _group = nil;
(void)([_renderingStyle release]), _renderingStyle = nil;
(void)([_rootNode release]), _rootNode = nil;
(void)([_intrinsicSize release]), _intrinsicSize = nil;
(void)([_title release]), _title = nil;
(void)([_desc release]), _desc = nil;
(void)([_rootLayer release]), _rootLayer = nil;
// kill any memory that has been around
(void)([_layerTree release]), _layerTree = nil;
[super dealloc];
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
}
+ (id)svgNamed:(NSString*)string
@@ -64,12 +70,35 @@
ext = @"svg";
}
if ((str = [bundle pathForResource:[string stringByDeletingPathExtension]
ofType:ext])
!= nil) {
ofType:ext]) != nil) {
return [[[self alloc] initWithFile:str
error:error
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;
}
@@ -79,10 +108,17 @@
__block IJSVGImageLayer* imageLayer = nil;
// create the layers we require
IJSVGImage* imageNode = [[[IJSVGImage alloc] init] autorelease];
imageNode.image = image;
BOOL hasTransaction = IJSVGBeginTransaction();
layer = [[[IJSVGGroupLayer alloc] init] autorelease];
imageLayer =
[[[IJSVGImageLayer alloc] initWithImage:image] autorelease];
[[[IJSVGImageLayer alloc] initWithImage:imageNode] autorelease];
[layer addSublayer:imageLayer];
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
// return the initialized SVG
return [self initWithSVGLayer:layer
@@ -95,7 +131,6 @@
// this completely bypasses passing of files
if ((self = [super init]) != nil) {
// keep the layer tree
_layerTree = [group retain];
_viewBox = viewBox;
// any setups
@@ -115,7 +150,7 @@
error:(NSError**)error
delegate:(id<IJSVGDelegate>)delegate
{
return [self initWithFilePathURL:[NSURL fileURLWithPath:file]
return [self initWithFilePathURL:[NSURL fileURLWithPath:file isDirectory:NO]
error:error
delegate:delegate];
}
@@ -173,15 +208,15 @@
[self _checkDelegate];
// create the group
_group = [[IJSVGParser groupForFileURL:aURL
error:&anError
delegate:self] retain];
IJSVGParser* parser = [IJSVGParser groupForFileURL:aURL
error:&anError
delegate:self];
_rootNode = parser.rootNode.retain;
[self _setupBasicInfoFromGroup];
[self _setupBasicsFromAnyInitializer];
// something went wrong...
if (_group == nil) {
if (_rootNode == nil) {
if (error != NULL) {
*error = anError;
}
@@ -192,6 +227,21 @@
return self;
}
- (id)initWithSVGData:(NSData*)data
{
return [self initWithSVGData:data
error:nil];
}
- (id)initWithSVGData:(NSData*)data
error:(NSError**)error
{
NSString* svgString = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
return [self initWithSVGString:svgString.autorelease
error:error];
}
- (id)initWithSVGString:(NSString*)string
{
return [self initWithSVGString:string
@@ -219,15 +269,16 @@
[self _checkDelegate];
// setup the parser
_group = [[IJSVGParser alloc] initWithSVGString:string
error:&anError
delegate:self];
IJSVGParser* parser = [[[IJSVGParser alloc] initWithSVGString:string
error:&anError
delegate:self] autorelease];
_rootNode = parser.rootNode.retain;
[self _setupBasicInfoFromGroup];
[self _setupBasicsFromAnyInitializer];
// something went wrong :(
if (_group == nil) {
if (_rootNode == nil) {
if (error != NULL) {
*error = anError;
}
@@ -238,49 +289,61 @@
return self;
}
- (void)discardDOM
- (void)performBlock:(dispatch_block_t)block
{
// if we discard, we can no longer create a tree, so lets create tree
// upfront before we kill anything
[self layer];
// now clear memory
(void)([_group release]), _group = nil;
BOOL hasTransaction = IJSVGBeginTransaction();
block();
if (hasTransaction == YES) {
IJSVGEndTransaction();
}
}
- (void)_setupBasicInfoFromGroup
{
// store the viewbox
_viewBox = _group.viewBox;
_proposedViewSize = _group.proposedViewSize;
_viewBox = [_rootNode.viewBox computeValue:CGSizeZero];
_intrinsicSize = _rootNode.intrinsicSize.retain;
}
- (void)_setupBasicsFromAnyInitializer
{
self.style = [[[IJSVGRenderingStyle alloc] init] autorelease];
self.renderingStyle = [[[IJSVGRenderingStyle alloc] init] autorelease];
self.clipToViewport = YES;
self.renderQuality = kIJSVGRenderQualityFullResolution;
// setup low level backing scale
_lastProposedBackingScale = 0.f;
self.renderingBackingScaleHelper = ^CGFloat {
return 1.f;
return NSScreen.mainScreen.backingScaleFactor;
};
}
- (void)setTitle:(NSString*)title
{
_rootNode.title = title;
}
- (NSString*)title
{
return _rootNode.title;
}
- (void)setDesc:(NSString*)description
{
_rootNode.desc = description;
}
- (NSString*)desc
{
return _rootNode.desc;
}
- (NSString*)identifier
{
return _group.identifier;
return _rootNode.identifier;
}
- (void)_checkDelegate
{
_respondsTo.shouldHandleForeignObject =
[_delegate respondsToSelector:@selector(svg:shouldHandleForeignObject:)];
_respondsTo.handleForeignObject = [_delegate
respondsToSelector:@selector(svg:handleForeignObject:document:)];
_respondsTo.shouldHandleSubSVG =
[_delegate respondsToSelector:@selector(svg:foundSubSVG:withSVGString:)];
_respondsTo.shouldHandleSubSVG = [_delegate respondsToSelector:@selector(svg:foundSubSVG:withSVGString:)];
}
- (NSRect)viewBox
@@ -290,30 +353,39 @@
- (IJSVGGroup*)rootNode
{
return _group;
return _rootNode;
}
- (BOOL)isFont
{
return [_group isFont];
return [_rootNode isFont];
}
- (NSArray<IJSVGPath*>*)glyphs
{
return [_group glyphs];
return [_rootNode glyphs];
}
- (NSArray<IJSVG*>*)subSVGs:(BOOL)recursive
{
return [_group subSVGs:recursive];
return [_rootNode subSVGs:recursive];
}
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options
{
IJSVGExporter* exporter =
[[[IJSVGExporter alloc] initWithSVG:self
size:self.viewBox.size
options:options] autorelease];
IJSVGExporter* exporter = [[[IJSVGExporter alloc] initWithSVG:self
size:self.viewBox.size
options:options] autorelease];
return [exporter SVGString];
}
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
IJSVGExporter* exporter = [[[IJSVGExporter alloc] initWithSVG:self
size:self.viewBox.size
options:options
floatingPointOptions:floatingPointOptions] autorelease];
return [exporter SVGString];
}
@@ -340,11 +412,19 @@
error:nil];
}
- (NSSize)computeSVGSizeWithRenderSize:(NSSize)size
{
IJSVGUnitSize* svgSize = _intrinsicSize;
return NSMakeSize([svgSize.width computeValue:size.width],
[svgSize.height computeValue:size.height]);
}
- (NSRect)computeOriginalDrawingFrameWithSize:(NSSize)aSize
{
[self _beginDraw:(NSRect){ .origin = CGPointZero, .size = aSize }];
return NSMakeRect(0.f, 0.f, _proposedViewSize.width * _clipScale,
_proposedViewSize.height * _clipScale);
NSSize propSize = [self computeSVGSizeWithRenderSize:aSize];
[self _beginDraw:(NSRect) { .origin = CGPointZero, .size = aSize }];
return NSMakeRect(0.f, 0.f, propSize.width * _clipScale,
propSize.height * _clipScale);
}
- (CGImageRef)newCGImageRefWithSize:(CGSize)size
@@ -353,7 +433,7 @@
{
// setup the drawing rect, this is used for both the intial drawing
// and the backing scale helper block
NSRect rect = (CGRect){
NSRect rect = (CGRect) {
.origin = CGPointZero,
.size = (CGSize)size
};
@@ -362,7 +442,7 @@
[self _beginDraw:rect];
// make sure we setup the scale based on the backing scale factor
CGFloat scale = [self backindScaleFactor:NULL];
CGFloat scale = [self backingScaleFactor];
// create the context and colorspace
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
@@ -422,7 +502,7 @@
- (NSData*)PDFData:(NSError**)error
{
return [self
PDFDataWithRect:(NSRect){ .origin = NSZeroPoint, .size = _viewBox.size }
PDFDataWithRect:(NSRect) { .origin = NSZeroPoint, .size = _viewBox.size }
error:error];
}
@@ -452,10 +532,10 @@
CGContextTranslateCTM(context, 0, -box.size.height);
// make sure we set the masks to path bits n bobs
[self _beginVectorDraw];
// [self _beginVectorDraw];
// draw the icon
[self _drawInRect:(NSRect)box context:context error:error];
[self _endVectorDraw];
// [self _endVectorDraw];
CGContextEndPage(context);
@@ -466,35 +546,39 @@
return data;
}
- (void)endVectorDraw
{
[self _endVectorDraw];
}
//- (void)endVectorDraw
//{
// [self _endVectorDraw];
//}
//
//- (void)beginVectorDraw
//{
// [self _beginVectorDraw];
//}
- (void)beginVectorDraw
{
[self _beginVectorDraw];
}
- (void)_beginVectorDraw
{
// turn on converts masks to PDF's
// as PDF context and layer masks dont work
void (^block)(CALayer* layer, BOOL isMask) = ^void(CALayer* layer, BOOL isMask) {
((IJSVGLayer*)layer).convertMasksToPaths = YES;
};
[IJSVGLayer recursivelyWalkLayer:self.layer withBlock:block];
}
- (void)_endVectorDraw
{
// turn of convert masks to paths as not
// needed for generic rendering
void (^block)(CALayer* layer, BOOL isMask) = ^void(CALayer* layer, BOOL isMask) {
((IJSVGLayer*)layer).convertMasksToPaths = NO;
};
[IJSVGLayer recursivelyWalkLayer:self.layer withBlock:block];
}
//- (void)_beginVectorDraw
//{
// // turn on converts masks to PDF's
// // as PDF context and layer masks dont work
// void (^block)(CALayer* layer, BOOL isMask, BOOL* stop) =
// ^void(CALayer* layer, BOOL isMask, BOOL* stop) {
// ((IJSVGLayer*)layer).convertMasksToPaths = YES;
// };
// [IJSVGLayer recursivelyWalkLayer:self.layer
// withBlock:block];
//}
//
//- (void)_endVectorDraw
//{
// // turn of convert masks to paths as not
// // needed for generic rendering
// void (^block)(CALayer* layer, BOOL isMask, BOOL* stop) =
// ^void(CALayer* layer, BOOL isMask, BOOL* stop) {
// ((IJSVGLayer*)layer).convertMasksToPaths = NO;
// };
// [IJSVGLayer recursivelyWalkLayer:self.layer
// withBlock:block];
//}
- (void)prepForDrawingInView:(NSView*)view
{
@@ -505,7 +589,7 @@
}
// construct the layer before drawing
[self layer];
[self rootLayer];
// set the scale
__block NSView* weakView = view;
@@ -540,31 +624,39 @@
- (BOOL)drawInRect:(NSRect)rect
error:(NSError**)error
{
CGContextRef currentCGContext;
if (@available(macOS 10.10, *)) {
currentCGContext = NSGraphicsContext.currentContext.CGContext;
} else {
currentCGContext = NSGraphicsContext.currentContext.graphicsPort;
}
return [self _drawInRect:rect
context:[[NSGraphicsContext currentContext] CGContext]
context:currentCGContext
error:error];
}
- (CGFloat)computeBackingScale:(CGFloat)actualScale
{
_backingScale = actualScale;
return (CGFloat)(_scale + actualScale);
}
- (NSRect)computeRectDrawingInRect:(NSRect)rect
isValid:(BOOL*)valid
{
// we also need to calculate the viewport so we can clip
// the drawing if needed
NSRect viewPort = NSZeroRect;
viewPort.origin.x = round((rect.size.width / 2 - (_proposedViewSize.width / 2) * _clipScale) + rect.origin.x);
NSSize propSize = [self computeSVGSizeWithRenderSize:rect.size];
viewPort.origin.x = round((rect.size.width / 2 - (propSize.width / 2) * _clipScale) + rect.origin.x);
viewPort.origin.y = round(
(rect.size.height / 2 - (_proposedViewSize.height / 2) * _clipScale) + rect.origin.y);
viewPort.size.width = _proposedViewSize.width * _clipScale;
viewPort.size.height = _proposedViewSize.height * _clipScale;
(rect.size.height / 2 - (propSize.height / 2) * _clipScale) + rect.origin.y);
viewPort.size.width = propSize.width * _clipScale;
viewPort.size.height = propSize.height * _clipScale;
// check the viewport
if (NSEqualRects(_viewBox, NSZeroRect) || _viewBox.size.width <= 0 || _viewBox.size.height <= 0 || NSEqualRects(NSZeroRect, viewPort) || CGRectIsEmpty(viewPort) || CGRectIsNull(viewPort) || viewPort.size.width <= 0 || viewPort.size.height <= 0) {
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;
return NSZeroRect;
}
@@ -579,147 +671,126 @@
[self _drawInRect:rect context:context error:nil];
}
//- (BOOL)_drawInRect:(NSRect)rect
// context:(CGContextRef)ref
// error:(NSError**)error
//{
// // prep for draw...
// CGContextSaveGState(ref);
// @try {
// [self _beginDraw:rect];
//
// // we also need to calculate the viewport so we can clip
// // the drawing if needed
// BOOL canDraw = NO;
// NSRect viewPort = [self computeRectDrawingInRect:rect isValid:&canDraw];
// // check the viewport
// if (canDraw == NO) {
// if (error != NULL) {
// *error = [[[NSError alloc] initWithDomain:IJSVGErrorDomain
// code:IJSVGErrorDrawing
// userInfo:nil] autorelease];
// }
// } else {
// // clip to mask
// if (self.clipToViewport == YES) {
// CGContextClipToRect(ref, viewPort);
// }
//
// // add the origin back onto the viewport
// viewPort.origin.x -= (_viewBox.origin.x) * _scale;
// viewPort.origin.y -= (_viewBox.origin.y) * _scale;
//
// // transforms
// CGContextTranslateCTM(ref, viewPort.origin.x, viewPort.origin.y);
// CGContextScaleCTM(ref, _scale, _scale);
//
// // do we need to update the backing scales on the
// // layers?
// [self backingScaleFactor:nil];
//
// CGInterpolationQuality quality;
// switch (self.renderQuality) {
// case kIJSVGRenderQualityLow: {
// quality = kCGInterpolationLow;
// break;
// }
// case kIJSVGRenderQualityOptimized: {
// quality = kCGInterpolationMedium;
// break;
// }
// default: {
// quality = kCGInterpolationHigh;
// }
// }
// CGContextSetInterpolationQuality(ref, quality);
// BOOL hasTransaction = IJSVGBeginTransaction();
// [self.layer renderInContext:ref];
// if (hasTransaction == YES) {
// IJSVGEndTransaction();
// }
// }
// } @catch (NSException* exception) {
// // just catch and give back a drawing error to the caller
// if (error != NULL) {
// *error = [[[NSError alloc] initWithDomain:IJSVGErrorDomain
// code:IJSVGErrorDrawing
// userInfo:nil] autorelease];
// }
// }
// CGContextRestoreGState(ref);
// return (error == nil);
//}
- (BOOL)_drawInRect:(NSRect)rect
context:(CGContextRef)ref
error:(NSError**)error
{
// prep for draw...
BOOL transaction = IJSVGBeginTransaction();
CGContextSaveGState(ref);
@try {
[self _beginDraw:rect];
// we also need to calculate the viewport so we can clip
// the drawing if needed
BOOL canDraw = NO;
NSRect viewPort = [self computeRectDrawingInRect:rect isValid:&canDraw];
// check the viewport
if (canDraw == NO) {
if (error != NULL) {
*error = [[[NSError alloc] initWithDomain:IJSVGErrorDomain
code:IJSVGErrorDrawing
userInfo:nil] autorelease];
}
} else {
// clip to mask
if (self.clipToViewport == YES) {
CGContextClipToRect(ref, viewPort);
}
// add the origin back onto the viewport
viewPort.origin.x -= (_viewBox.origin.x) * _scale;
viewPort.origin.y -= (_viewBox.origin.y) * _scale;
// transforms
CGContextTranslateCTM(ref, viewPort.origin.x, viewPort.origin.y);
CGContextScaleCTM(ref, _scale, _scale);
// do we need to update the backing scales on the
// layers?
if (self.renderingBackingScaleHelper != nil) {
[self backindScaleFactor:nil];
}
CGInterpolationQuality quality;
switch (self.renderQuality) {
case kIJSVGRenderQualityLow: {
quality = kCGInterpolationLow;
break;
}
case kIJSVGRenderQualityOptimized: {
quality = kCGInterpolationMedium;
break;
}
default: {
quality = kCGInterpolationHigh;
}
}
CGContextSetInterpolationQuality(ref, quality);
[self.layer renderInContext:ref];
}
} @catch (NSException* exception) {
// just catch and give back a drawing error to the caller
if (error != NULL) {
*error = [[[NSError alloc] initWithDomain:IJSVGErrorDomain
code:IJSVGErrorDrawing
userInfo:nil] autorelease];
}
}
// make sure we setup a transaction
CGFloat backingScale = [self backingScaleFactor];
[self.rootLayer renderInContext:ref
viewPort:rect
backingScale:backingScale
quality:_renderQuality];
CGContextRestoreGState(ref);
return (error == nil);
if(transaction) {
IJSVGEndTransaction();
}
return YES;
}
- (CGFloat)backindScaleFactor:(CGFloat* _Nullable)proposedBackingScale
- (IJSVGLayerTree*)layerTree
{
if(_layerTree == nil) {
_layerTree = [[IJSVGLayerTree alloc] init];
}
return _layerTree;
}
- (IJSVGRootLayer*)rootLayer
{
if(_rootLayer == nil) {
_rootLayer = [self.layerTree rootLayerForRootNode:_rootNode].retain;
}
return _rootLayer;
}
- (CGFloat)backingScaleFactor
{
__block CGFloat scale = 1.f;
scale = (self.renderingBackingScaleHelper)();
if (scale < 1.f) {
scale = 1.f;
}
_backingScaleFactor = scale;
// make sure we multiple the scale by the scale of the rendered clip
// or it will be blurry for gradients and other bitmap drawing
scale = (_scale * scale);
// dont do anything, nothing has changed, no point of iterating over
// every layer for no reason!
if (scale == _lastProposedBackingScale && renderQuality == _lastProposedRenderQuality) {
return _backingScaleFactor;
}
IJSVGRenderQuality quality = self.renderQuality;
_lastProposedBackingScale = scale;
_lastProposedRenderQuality = quality;
if (proposedBackingScale != nil && proposedBackingScale != NULL) {
*proposedBackingScale = scale;
}
// walk the tree
void (^block)(CALayer* layer, BOOL isMask) = ^void(CALayer* layer, BOOL isMask) {
IJSVGLayer* propLayer = ((IJSVGLayer*)layer);
propLayer.renderQuality = quality;
if (propLayer.requiresBackingScaleHelp == YES) {
propLayer.backingScaleFactor = scale;
}
};
// gogogo
[IJSVGLayer recursivelyWalkLayer:self.layer withBlock:block];
return _backingScaleFactor;
return _backingScale = scale;
}
- (IJSVGLayer*)layerWithTree:(IJSVGLayerTree*)tree
- (void)setRenderingStyle:(IJSVGRenderingStyle*)style
{
// clear memory
if (_layerTree != nil) {
(void)([_layerTree release]), _layerTree = nil;
}
// force rebuild of the tree
_layerTree = [[tree layerForNode:_group] retain];
return _layerTree;
}
- (IJSVGLayer*)layer
{
if (_layerTree != nil) {
return _layerTree;
}
// create the renderer and assign default values
// from this SVG object
IJSVGLayerTree* renderer = [[[IJSVGLayerTree alloc] init] autorelease];
renderer.viewBox = self.viewBox;
renderer.style = self.style;
// return the rendered layer
return [self layerWithTree:renderer];
}
- (void)setStyle:(IJSVGRenderingStyle*)style
{
(void)([_style release]), _style = nil;
_style = style.retain;
(void)([_renderingStyle release]), _renderingStyle = nil;
_renderingStyle = style.retain;
}
- (void)observeValueForKeyPath:(NSString*)keyPath
@@ -728,7 +799,7 @@
context:(void*)context
{
// invalidate the tree if a style is set
if (object == _style) {
if (object == _renderingStyle) {
[self invalidateLayerTree];
}
}
@@ -743,53 +814,72 @@
(void)([_layerTree release]), _layerTree = nil;
}
- (IJSVGColorList*)computedColorList:(BOOL*)hasPatternFills
- (IJSVGColorList*)colorList
{
IJSVGColorList* sheet = [[[IJSVGColorList alloc] init] autorelease];
void (^block)(CALayer* layer, BOOL isMask) = ^void(CALayer* layer,
BOOL isMask) {
if ([layer isKindOfClass:[IJSVGShapeLayer class]] && isMask == NO && layer.isHidden == NO) {
IJSVGShapeLayer* sLayer = (IJSVGShapeLayer*)layer;
NSColor* color = nil;
if (sLayer.fillColor != nil) {
color = [NSColor colorWithCGColor:sLayer.fillColor];
if (color.alphaComponent != 0.f) {
[sheet addColor:color];
}
}
if (sLayer.strokeColor != nil) {
color = [NSColor colorWithCGColor:sLayer.strokeColor];
color = [IJSVGColor computeColorSpace:color];
if (color.alphaComponent != 0.f) {
[sheet addColor:color];
}
}
void (^block)(CALayer* layer, BOOL* stop) =
^void(CALayer* layer, BOOL* stop) {
// dont do anything
if(([layer isKindOfClass:IJSVGShapeLayer.class] &&
layer.isHidden == NO) == NO) {
return;
}
// compute
IJSVGShapeLayer* sLayer = (IJSVGShapeLayer*)layer;
NSColor* color = nil;
// check for any patterns
if (sLayer.patternFillLayer != nil || sLayer.gradientFillLayer != nil || sLayer.gradientStrokeLayer != nil || sLayer.patternStrokeLayer != nil) {
if (hasPatternFills != nil && *hasPatternFills != YES) {
*hasPatternFills = YES;
}
// 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];
}
}
// 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];
}
// stroke color
if (sLayer.strokeColor != nil) {
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];
}
// gradient stroke layers
if ((gradStrokeLayer = sLayer.gradientStrokeLayer) != nil) {
IJSVGColorList* gradSheet = gradStrokeLayer.gradient.colorList;
[sheet addColorsFromList:gradSheet];
}
}
};
// walk
[IJSVGLayer recursivelyWalkLayer:self.layer withBlock:block];
// return the colours!
// gogogo!
[IJSVGLayer recursivelyWalkLayer:self.rootLayer
withBlock:block];
return sheet;
}
@@ -800,12 +890,13 @@
// to transform the paths into our viewbox
NSSize dest = rect.size;
NSSize source = _viewBox.size;
_clipScale = MIN(dest.width / _proposedViewSize.width,
dest.height / _proposedViewSize.height);
NSSize propSize = [self computeSVGSizeWithRenderSize:rect.size];
_clipScale = MIN(dest.width / propSize.width,
dest.height / propSize.height);
// work out the actual scale based on the clip scale
CGFloat w = _proposedViewSize.width * _clipScale;
CGFloat h = _proposedViewSize.height * _clipScale;
CGFloat w = propSize.width * _clipScale;
CGFloat h = propSize.height * _clipScale;
_scale = MIN(w / source.width, h / source.height);
}
@@ -837,25 +928,43 @@
}
}
- (BOOL)svgParser:(IJSVGParser*)parser
shouldHandleForeignObject:(IJSVGForeignObject*)foreignObject
{
if (_delegate != nil && _respondsTo.shouldHandleForeignObject == 1) {
return [_delegate svg:self
shouldHandleForeignObject:foreignObject];
}
return NO;
}
#pragma mark matching
- (void)svgParser:(IJSVGParser*)parser
handleForeignObject:(IJSVGForeignObject*)foreignObject
document:(NSXMLDocument*)document
- (BOOL)matchesPropertiesWithMask:(IJSVGMatchPropertiesMask)mask
{
if (_delegate != nil && _respondsTo.handleForeignObject == 1) {
[_delegate svg:self
handleForeignObject:foreignObject
document:document];
}
__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 matchesTraits:IJSVGNodeTraitStroked] == 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:_rootNode
handler:handler];
return matchedMask == mask;
}
@end
@@ -6,7 +6,7 @@
// Copyright (c) 2015 Curtis Hard. All rights reserved.
//
#import "IJSVG.h"
#import <IJSVG/IJSVG.h>
#import <Foundation/Foundation.h>
typedef void (^IJSVGFontConverterEnumerateBlock)(NSString* unicode, IJSVG* svg);
@@ -6,7 +6,6 @@
// Copyright (c) 2015 Curtis Hard. All rights reserved.
//
#import "IJSVGBezierPathAdditions.h"
#import "IJSVGFontConverter.h"
#import "IJSVGShapeLayer.h"
@@ -104,16 +103,15 @@
+ (IJSVG*)convertIJSVGPathToSVG:(IJSVGPath*)path
{
CGPathRef cgPath = [path.path newCGPathRef:NO];
CGPathRef flippedPath = [IJSVGUtils newFlippedCGPath:cgPath];
CGPathRef flippedPath = [IJSVGUtils newFlippedCGPath:path.path];
IJSVG* svg = [self convertPathToSVG:flippedPath];
CGPathRelease(flippedPath);
CGPathRelease(cgPath);
return svg;
}
+ (IJSVG*)convertPathToSVG:(CGPathRef)path
{
IJSVGBeginTransaction();
__block IJSVG* svg = nil;
IJSVGGroupLayer* layer = [[[IJSVGGroupLayer alloc] init] autorelease];
IJSVGShapeLayer* shape = [[[IJSVGShapeLayer alloc] init] autorelease];
@@ -122,6 +120,7 @@
CGRect box = CGPathGetPathBoundingBox(path);
svg = [[IJSVG alloc] initWithSVGLayer:layer
viewBox:box];
IJSVGEndTransaction();
return [svg autorelease];
}
@@ -17,6 +17,9 @@
IJSVG* _svg;
}
- (instancetype)initWithData:(NSData*)data;
@property (nonatomic, readonly) CGRect viewBox;
@property (nonatomic, readonly) IJSVG* SVG;
@end
@@ -11,8 +11,6 @@
@implementation IJSVGImageRep
@synthesize viewBox = _viewBox;
+ (void)load
{
[NSBitmapImageRep registerImageRepClass:self];
@@ -25,12 +23,20 @@
+ (NSArray<NSString*>*)imageTypes
{
return @[ (NSString*)kUTTypeScalableVectorGraphics, @"svg" ];
if (@available(macOS 10.10, *)) {
return @[ (NSString*)kUTTypeScalableVectorGraphics, @"svg" ];
} else {
return @[ @"public.svg-image", @"svg" ];
}
}
+ (NSArray<NSString*>*)imageUnfilteredTypes
{
return @[ (NSString*)kUTTypeScalableVectorGraphics, @"svg" ];
if (@available(macOS 10.10, *)) {
return @[ (NSString*)kUTTypeScalableVectorGraphics, @"svg" ];
} else {
return @[ @"public.svg-image", @"svg" ];
}
}
+ (NSArray<NSImageRep*>*)imageRepsWithData:(NSData*)data
@@ -101,4 +107,9 @@
return _svg.viewBox;
}
- (IJSVG*)SVG
{
return _svg;
}
@end
@@ -6,7 +6,7 @@
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import "IJSVG.h"
#import <IJSVG/IJSVG.h>
#import <Cocoa/Cocoa.h>
IB_DESIGNABLE
@@ -42,7 +42,7 @@
if (imageName != nil) {
IJSVG* anSVG = [IJSVG svgNamed:imageName];
if (tintColor != nil) {
anSVG.style.fillColor = tintColor;
anSVG.renderingStyle.fillColor = tintColor;
}
self.SVG = anSVG;
}
@@ -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 <IJSVG/IJSVGUtils.h>
#import <IJSVG/IJSVGColorType.h>
@class IJSVG;
@class IJSVGExporter;
@class IJSVGLayer;
@class IJSVGNode;
NS_ASSUME_NONNULL_BEGIN
typedef void (^IJSVGCGPathHandler)(const CGPathElement* pathElement);
typedef void (^IJSVGPathElementEnumerationBlock)(const CGPathElement* pathElement, CGPoint currentPoint);
@@ -33,12 +40,43 @@ typedef NS_OPTIONS(NSInteger, IJSVGExporterOptions) {
IJSVGExporterOptionColorAllowRRGGBBAA = 1 << 14,
IJSVGExporterOptionRemoveComments = 1 << 15,
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);
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 {
@@ -50,15 +88,31 @@ const NSArray* IJSVGShortCharacterArray(void);
NSXMLElement* _defElement;
NSInteger _idCount;
NSInteger _shortIdCount;
BOOL _appliedXLink;
struct {
unsigned int identifierForElement: 1;
unsigned int stringForColor: 1;
} _respondsTo;
}
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* description;
@property (nonatomic, assign) id<IJSVGExporterDelegate> delegate;
@property (nonatomic, assign) IJSVGFloatingPointOptions floatingPointOptions;
@property (nonatomic, copy, nullable) NSString* title;
@property (nonatomic, copy, nullable) NSString* desc;
- (id)initWithSVG:(IJSVG*)svg
size:(CGSize)size
options:(IJSVGExporterOptions)options;
- (id)initWithSVG:(IJSVG*)svg
size:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (NSString*)SVGString;
- (NSData*)SVGData;
- (IJSVG*)SVG:(NSError**)error;
@end
NS_ASSUME_NONNULL_END
File diff suppressed because it is too large Load Diff
@@ -15,30 +15,53 @@ typedef struct {
NSArray<NSString*>* params;
} IJSVGExporterPathInstructionCommand;
typedef struct {
CGPoint center;
CGFloat radius;
} IJSVGExporterPathInstructionCircle;
@interface IJSVGExporterPathInstruction : NSObject {
@private
NSInteger _dataCount;
char _instruction;
CGFloat* _data;
CGFloat* _base;
CGFloat* _coords;
}
@property (nonatomic, assign) char instruction;
void IJSVGExporterPathInstructionRoundData(CGFloat* data, NSInteger length, IJSVGFloatingPointOptions options);
CGFloat IJSVGExporterPathFloatToFixed(CGFloat number, int precision);
IJSVGExporterPathInstructionCommand* IJSVGExporterPathInstructionCommandCopy(IJSVGExporterPathInstructionCommand command);
void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand* _Nullable command);
+ (NSArray<IJSVGExporterPathInstruction*>*)instructionsFromPath:(CGPathRef)path;
+ (NSArray<IJSVGExporterPathInstruction*>*)instructionsFromPath:(CGPathRef)path
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (id)initWithInstruction:(char)instruction
dataCount:(NSInteger)floatCount;
- (void)setInstruction:(char)newInstruction;
- (char)instruction;
- (CGFloat*)data;
- (NSInteger)dataLength;
+ (void)convertInstructionsToRelativeCoordinates:(NSArray<IJSVGExporterPathInstruction*>*)instructions;
+ (NSString*)pathStringFromInstructions:(NSArray<IJSVGExporterPathInstruction*>*)instructions;
+ (NSString*)pathStringWithInstructionSet:(NSArray<NSValue*>*)instructionSets;
+ (NSArray<IJSVGExporterPathInstruction*>*)convertInstructionsCurves:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (void)convertInstructionsToMixedAbsoluteRelative:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (void)convertInstructionsDataToRounded:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (void)convertInstructionsToRelativeCoordinates:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (NSString*)pathStringFromInstructions:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (NSString*)pathStringWithInstructionSet:(NSArray<NSValue*>*)instructionSets
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
@end
NS_ASSUME_NONNULL_END
static NSInteger const kIJSVGExporterPathInstructionFloatPrecision = 3;
static CGFloat const kIJSVGExporterPathInstructionErrorThreshold = 1e-2;
#define IJ_SVG_EXPORT_ROUND(value) IJSVGExporterPathFloatToFixed(value, kIJSVGExporterPathInstructionFloatPrecision)
@@ -9,13 +9,22 @@
#import "IJSVGExporter.h"
#import "IJSVGExporterPathInstruction.h"
#import "IJSVGUtils.h"
#import <math.h>
@implementation IJSVGExporterPathInstruction
@synthesize instruction = _instruction;
- (void)dealloc
{
if (_data != NULL) {
free(_data);
(void)free(_data), _data = NULL;
}
if (_base != NULL) {
(void)free(_base), _base = NULL;
}
if (_coords != NULL) {
(void)free(_coords), _coords = NULL;
}
[super dealloc];
}
@@ -28,8 +37,13 @@
// only allocate if not zero
if (floatCount != 0) {
_dataCount = floatCount;
_data = (CGFloat*)calloc(sizeof(CGFloat), floatCount);
}
// setup base and coords
_base = (CGFloat*)malloc(sizeof(CGFloat) * 2);
_coords = (CGFloat*)malloc(sizeof(CGFloat) * 2);
}
return self;
}
@@ -39,21 +53,21 @@
return _dataCount;
}
- (void)setInstruction:(char)newInstruction
{
_instruction = newInstruction;
}
- (char)instruction
{
return _instruction;
}
- (CGFloat*)data
{
return _data;
}
- (CGFloat*)base
{
return _base;
}
- (CGFloat*)coords
{
return _coords;
}
IJSVGExporterPathInstructionCommand* IJSVGExporterPathInstructionCommandCopy(IJSVGExporterPathInstructionCommand command)
{
IJSVGExporterPathInstructionCommand* copy = NULL;
@@ -71,10 +85,10 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
}
+ (NSString*)pathStringWithInstructionSet:(NSArray<NSValue*>*)instructionSets
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
IJSVGExporterPathInstructionCommand* lastCommand = NULL;
NSMutableString* string = [[[NSMutableString alloc] init] autorelease];
char* lastCommandChars = NULL;
for (NSValue* value in instructionSets) {
// read back the bytes
IJSVGExporterPathInstructionCommand command;
@@ -89,31 +103,9 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
[string appendString:@" "];
}
NSInteger index = 0;
for (NSString* dataString in command.params) {
const char* chars = dataString.UTF8String;
// work out if the command is signed and or decimal
BOOL isSigned = chars[0] == '-';
BOOL isDecimal = (isSigned == NO && chars[0] == '.') || (isSigned == YES && chars[1] == '.');
// we also need to know if the previous command was a decimal or not
BOOL lastWasDecimal = NO;
if (lastCommandChars != NULL) {
lastWasDecimal = strchr(lastCommandChars, '.') != NULL;
}
// we only need a space if the current command is not signed
// a decimal and the previous command was decimal too
if (index++ == 0 || isSigned || (isDecimal == YES && lastWasDecimal == YES)) {
[string appendString:dataString];
} else {
[string appendFormat:@" %@", dataString];
}
// store last command chars
lastCommandChars = (char*)chars;
}
// compresses the floats
NSString* compressedFloats = IJSVGCompressFloatParameterArray(command.params);
[string appendString:compressedFloats];
// store last command
IJSVGExporterPathInstructionCommandFree(lastCommand);
@@ -125,6 +117,7 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
}
+ (NSString*)pathStringFromInstructions:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
NSMutableArray* pathInstructions = [[[NSMutableArray alloc] init] autorelease];
for (IJSVGExporterPathInstruction* instruction in instructions) {
@@ -132,11 +125,12 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
const char lowerInstruction = tolower(instruction.instruction);
NSArray<NSString*>* set = nil;
switch (lowerInstruction) {
case 't':
case 'm':
case 'l': {
set = @[
IJSVGShortFloatString(data[0]),
IJSVGShortFloatString(data[1])
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions)
];
break;
}
@@ -144,29 +138,43 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
case 'v':
case 'h': {
set = @[
IJSVGShortFloatString(data[0])
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions)
];
break;
}
case 'c': {
set = @[
IJSVGShortFloatString(data[0]),
IJSVGShortFloatString(data[1]),
IJSVGShortFloatString(data[2]),
IJSVGShortFloatString(data[3]),
IJSVGShortFloatString(data[4]),
IJSVGShortFloatString(data[5])
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[2], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[3], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[4], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[5], floatingPointOptions)
];
break;
}
case 's':
case 'q': {
set = @[
IJSVGShortFloatString(data[0]),
IJSVGShortFloatString(data[1]),
IJSVGShortFloatString(data[2]),
IJSVGShortFloatString(data[3])
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[2], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[3], floatingPointOptions)
];
break;
}
case 'a': {
set = @[
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[2], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[3], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[4], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[5], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[6], floatingPointOptions),
];
break;
}
@@ -187,13 +195,201 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
objCType:@encode(IJSVGExporterPathInstructionCommand)];
[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
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
CGFloat point[2] = { 0, 0 };
CGFloat subpathPoint[2] = { 0, 0 };
IJSVGExporterPathInstruction* baseInstruction = nil;
IJSVGExporterPathInstruction* prevInstruction = nil;
NSInteger index = 0;
for (IJSVGExporterPathInstruction* anInstruction in instructions) {
@@ -212,6 +408,8 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
if (instruction == 'm') {
subpathPoint[0] = point[0];
subpathPoint[1] = point[1];
baseInstruction = anInstruction;
}
} else if (instruction == 'h') {
@@ -232,8 +430,8 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
subpathPoint[0] = point[0] += data[0];
subpathPoint[1] = point[1] += data[1];
baseInstruction = anInstruction;
} else if (instruction == 'L' || instruction == 'T') {
instruction = tolower(instruction);
data[0] -= point[0];
@@ -241,9 +439,7 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
point[0] += data[0];
point[1] += data[1];
} else if (instruction == 'C') {
instruction = 'c';
data[0] -= point[0];
@@ -255,9 +451,7 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
point[0] += data[4];
point[1] += data[5];
} else if (instruction == 'S' || instruction == 'Q') {
instruction = tolower(instruction);
data[0] -= point[0];
@@ -267,9 +461,7 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
point[0] += data[2];
point[1] += data[3];
} else if (instruction == 'A') {
instruction = 'a';
data[5] -= point[0];
@@ -277,38 +469,51 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
point[0] += data[5];
point[1] += data[6];
} else if (instruction == 'H') {
instruction = 'h';
data[0] -= point[0];
point[0] += data[0];
} else if (instruction == 'V') {
instruction = 'v';
data[0] -= point[1];
point[1] += data[0];
}
// reset the instruction
[anInstruction setInstruction:instruction];
anInstruction.instruction = instruction;
CGFloat* coords = anInstruction.coords;
coords[0] = point[0];
coords[1] = point[1];
} else if (instruction == 'Z' || instruction == 'z') {
if (baseInstruction != nil) {
CGFloat* coords = anInstruction.coords;
coords[0] = baseInstruction.coords[0];
coords[1] = baseInstruction.coords[1];
}
point[0] = subpathPoint[0];
point[1] = subpathPoint[1];
}
CGFloat* base = anInstruction.base;
if (prevInstruction != nil) {
base[0] = prevInstruction.coords[0];
base[1] = prevInstruction.coords[1];
} else {
base[0] = 0.f;
base[1] = 0.f;
}
// increment index
prevInstruction = anInstruction;
index++;
}
}
+ (NSArray<IJSVGExporterPathInstruction*>*)instructionsFromPath:(CGPathRef)path
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
// keep track of the current point
@@ -378,6 +583,7 @@ void IJSVGExporterPathInstructionCommandFree(IJSVGExporterPathInstructionCommand
CGPoint controlPoint1 = pathElement->points[0];
CGPoint controlPoint2 = pathElement->points[1];
CGPoint point = pathElement->points[2];
currentPoint = point;
instruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'C'
dataCount:6] autorelease];
@@ -6,18 +6,16 @@
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import "IJSVGGradient.h"
#import "IJSVGLayer.h"
#import "IJSVGPath.h"
#import <IJSVG/IJSVGGradient.h>
#import <IJSVG/IJSVGLayer.h>
#import <IJSVG/IJSVGPath.h>
#import <QuartzCore/QuartzCore.h>
@interface IJSVGGradientLayer : IJSVGLayer {
}
@property (nonatomic, assign) CGRect viewBox;
@property (nonatomic, retain) IJSVGGradient* gradient;
@property (nonatomic, assign) CGAffineTransform absoluteTransform;
@property (nonatomic, assign) CGRect objectRect;
@property (nonatomic, assign) CGRect viewBox;
@end
@@ -7,42 +7,34 @@
//
#import "IJSVGGradientLayer.h"
#import <IJSVG/IJSVGRootLayer.h>
@implementation IJSVGGradientLayer
@synthesize viewBox;
@synthesize gradient;
@synthesize absoluteTransform;
@synthesize objectRect;
- (void)dealloc
{
(void)([gradient release]), gradient = nil;
(void)([_gradient release]), _gradient = nil;
[super dealloc];
}
- (id)init
- (BOOL)requiresBackingScaleHelp
{
if ((self = [super init]) != nil) {
self.requiresBackingScaleHelp = YES;
self.shouldRasterize = YES;
}
return self;
return YES;
}
- (void)setGradient:(IJSVGGradient*)newGradient
{
if (gradient != nil) {
(void)([gradient release]), gradient = nil;
if (_gradient != nil) {
(void)([_gradient release]), _gradient = nil;
}
gradient = [newGradient retain];
_gradient = [newGradient retain];
// lets check its alpha properties on the colors
BOOL hasAlphaChannel = NO;
NSInteger stops = gradient.gradient.numberOfColorStops;
NSInteger stops = _gradient.gradient.numberOfColorStops;
for (NSInteger i = 0; i < stops; i++) {
NSColor* color = nil;
[gradient.gradient getColor:&color
[_gradient.gradient getColor:&color
location:NULL
atIndex:i];
if (color.alphaComponent != 1.f) {
@@ -79,6 +71,11 @@
[super setBackingScaleFactor:backingScaleFactor];
}
- (CALayer<IJSVGDrawableLayer> *)referencingLayer
{
return [super referencingLayer] ?: self.superlayer;
}
- (void)drawInContext:(CGContextRef)ctx
{
[super drawInContext:ctx];
@@ -89,14 +86,22 @@
}
// draw the gradient
CGAffineTransform trans = CGAffineTransformMakeTranslation(-CGRectGetMinX(objectRect),
-CGRectGetMinY(objectRect));
CGAffineTransform transform = CGAffineTransformConcat(absoluteTransform, trans);
CALayer<IJSVGDrawableLayer>* layer = (CALayer<IJSVGDrawableLayer>*)self.referencingLayer;
CGRect objectRect = layer.boundingBox;
CGRect objectFrame = layer.frame;
CGAffineTransform affine = [IJSVGLayer absoluteTransformForLayer:layer];
affine = CGAffineTransformTranslate(affine,
-CGRectGetMinX(objectFrame),
-CGRectGetMinY(objectFrame));
CGContextSaveGState(ctx);
IJSVGRootLayer* rootNode = (IJSVGRootLayer*)[IJSVGLayer rootLayerForLayer:self];
CGRect bounds = [rootNode.viewBox computeValue:CGSizeZero];
[self.gradient drawInContextRef:ctx
objectRect:objectRect
absoluteTransform:transform
viewPort:self.viewBox];
absoluteTransform:affine
viewPort:bounds];
CGContextRestoreGState(ctx);
}
@@ -6,10 +6,16 @@
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import "IJSVGLayer.h"
#import "IJSVGShapeLayer.h"
#import <IJSVG/IJSVGLayer.h>
#import <IJSVG/IJSVGShapeLayer.h>
#import <QuartzCore/QuartzCore.h>
@interface IJSVGGroupLayer : IJSVGLayer
@interface IJSVGGroupLayer : IJSVGLayer {
}
@property (nonatomic, retain) IJSVGUnitRect* viewBox;
@property (nonatomic, assign) IJSVGViewBoxAlignment viewBoxAlignment;
@property (nonatomic, assign) IJSVGViewBoxMeetOrSlice viewBoxMeetOrSlice;
@end
@@ -7,7 +7,16 @@
//
#import "IJSVGGroupLayer.h"
#import "IJSVGViewBox.h"
#import "IJSVGUnitRect.h"
#import "IJSVGLayer.h"
@implementation IJSVGGroupLayer
- (void)dealloc
{
(void)[_viewBox release], _viewBox = nil;
[super dealloc];
}
@end
@@ -6,14 +6,20 @@
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import "IJSVGLayer.h"
#import <IJSVG/IJSVGLayer.h>
#import <IJSVG/IJSVGImage.h>
#import <IJSVG/IJSVGTransformLayer.h>
#import <AppKit/AppKit.h>
#import <QuartzCore/QuartzCore.h>
@interface IJSVGImageLayer : IJSVGLayer {
@private
IJSVGLayer* _imageLayer;
}
- (id)initWithImage:(NSImage*)image;
- (id)initWithCGImage:(CGImageRef)imageRef;
@property (nonatomic, retain) IJSVGImage* image;
- (id)initWithImage:(IJSVGImage*)image;
@end
@@ -10,46 +10,44 @@
@implementation IJSVGImageLayer
- (id)initWithImage:(NSImage*)image
- (void)dealloc
{
NSRect rect = (NSRect){
.origin = NSZeroPoint,
.size = image.size
};
CGImageRef ref = [image CGImageForProposedRect:&rect
context:nil
hints:nil];
return [self initWithCGImage:ref];
(void)[_image release], _image = nil;
(void)[_imageLayer release], _imageLayer = nil;
[super dealloc];
}
- (id)initWithCGImage:(CGImageRef)imageRef
- (id)initWithImage:(IJSVGImage*)image
{
if ((self = [super init]) != nil) {
// set the contents
self.contents = (id)imageRef;
// make sure we say we need help
self.requiresBackingScaleHelp = YES;
self.shouldRasterize = YES;
// set the frame, simple stuff
self.frame = (CGRect){
.origin = CGPointZero,
.size = CGSizeMake(CGImageGetWidth(imageRef),
CGImageGetHeight(imageRef))
};
if((self = [super init]) != nil) {
_image = image.retain;
}
return self;
}
- (void)setNeedsDisplay
- (BOOL)requiresBackingScaleHelp
{
// swap the content around on call
// because set needs display discards previous
// content - yolo!
id oldContent = self.contents;
[super setNeedsDisplay];
self.contents = oldContent;
return YES;
}
- (void)layoutSublayers
{
[super layoutSublayers];
[self reloadContent];
[self setNeedsDisplay];
}
- (void)reloadContent
{
if(_imageLayer == nil) {
_imageLayer = [IJSVGLayer layer].retain;
_imageLayer.contentsGravity = kCAGravityResize;
_imageLayer.affineTransform = CGAffineTransformMakeScale(1.f, -1.f);
_imageLayer.contents = (id)_image.CGImage;
[self addSublayer:_imageLayer];
}
_imageLayer.frame = self.bounds;
}
@end
@@ -6,8 +6,8 @@
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import "IJSVGRendering.h"
#import "IJSVGTransaction.h"
#import <IJSVG/IJSVGRendering.h>
#import <IJSVG/IJSVGTransaction.h>
#import <QuartzCore/QuartzCore.h>
@class IJSVGShapeLayer;
@@ -15,8 +15,52 @@
@class IJSVGPatternLayer;
@class IJSVGStrokeLayer;
@class IJSVGGroupLayer;
@class IJSVGLayer;
@interface IJSVGLayer : CALayer {
typedef NS_ENUM(NSUInteger, IJSVGLayerFillType) {
IJSVGLayerFillTypeColor,
IJSVGLayerFillTypePattern,
IJSVGLayerFillTypeGradient,
IJSVGLayerFillTypeUnknown
};
typedef NS_OPTIONS(NSUInteger, IJSVGLayerTraits) {
IJSVGLayerTraitGroup
};
@protocol IJSVGPathableLayer <NSObject>
@required
@property (nonatomic, assign) CGPathRef path;
@end
@protocol IJSVGDrawableLayer <NSObject>
@required
@property (nonatomic, assign) CGBlendMode blendingMode;
@property (nonatomic, retain) CALayer<IJSVGDrawableLayer>* clipLayer;
@property (nonatomic, retain) CALayer<IJSVGDrawableLayer>* maskLayer;
@property (nonatomic, copy) CAShapeLayerFillRule clipRule;
@property (nonatomic, copy) CAShapeLayerFillRule fillRule;
@property (nonatomic, readonly) CGPoint absoluteOrigin;
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
@property (nonatomic, assign) CGFloat backingScaleFactor;
@property (nonatomic, readonly) BOOL requiresBackingScaleHelp;
@property (nonatomic, readonly) CALayer<IJSVGDrawableLayer>* rootLayer;
@property (nonatomic, readonly) CGRect absoluteFrame;
@property (nonatomic, assign) CGRect boundingBox;
@property (nonatomic, readonly) CGRect strokeBoundingBox;
@property (nonatomic, readonly) CGRect boundingBoxBounds;
@property (nonatomic, assign) CGRect outerBoundingBox;
@property (nonatomic, assign) CALayer<IJSVGDrawableLayer>* referencingLayer;
@property (nonatomic, readonly) NSArray<CALayer<IJSVGDrawableLayer>*>* debugLayers;
- (void)performRenderInContext:(CGContextRef)ctx;
@end
@interface IJSVGLayer : CALayer <IJSVGDrawableLayer> {
@private
IJSVGLayer* _maskingLayer;
@@ -27,19 +71,65 @@
@property (nonatomic, assign) IJSVGStrokeLayer* strokeLayer;
@property (nonatomic, assign) IJSVGGradientLayer* gradientStrokeLayer;
@property (nonatomic, assign) IJSVGPatternLayer* patternStrokeLayer;
@property (nonatomic, assign) BOOL requiresBackingScaleHelp;
@property (nonatomic, readonly) BOOL requiresBackingScaleHelp;
@property (nonatomic, assign) CGFloat backingScaleFactor;
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
@property (nonatomic, assign) CGBlendMode blendingMode;
@property (nonatomic, assign) CGPoint absoluteOrigin;
@property (nonatomic, assign) BOOL convertMasksToPaths;
@property (nonatomic, retain) CALayer<IJSVGDrawableLayer>* clipLayer;
@property (nonatomic, copy) CAShapeLayerFillRule clipRule;
@property (nonatomic, copy) CAShapeLayerFillRule fillRule;
@property (nonatomic, retain) CALayer<IJSVGDrawableLayer>* maskLayer;
@property (nonatomic, readonly) CALayer<IJSVGDrawableLayer>* rootLayer;
@property (nonatomic, readonly) CGRect absoluteFrame;
@property (nonatomic, assign) CGRect boundingBox;
@property (nonatomic, readonly) CGRect boundingBoxBounds;
@property (nonatomic, readonly) CGRect strokeBoundingBox;
@property (nonatomic, assign) CGRect outerBoundingBox;
@property (nonatomic, readonly) CGRect outerBoundingBoxBounds;
@property (nonatomic, assign) CALayer<IJSVGDrawableLayer>* referencingLayer;
+ (IJSVGLayerFillType)fillTypeForFill:(id)fill;
+ (NSArray*)deepestSublayersOfLayer:(CALayer*)layer;
+ (void)recursivelyWalkLayer:(CALayer*)layer
withBlock:(void (^)(CALayer* layer, BOOL isMask))block;
+ (void)recursivelyWalkLayer:(CALayer<IJSVGDrawableLayer>*)layer
withBlock:(void (^)(CALayer<IJSVGDrawableLayer>* layer, BOOL* stop))block;
- (void)applySublayerMaskToContext:(CGContextRef)context
forSublayer:(IJSVGLayer*)sublayer
withOffset:(CGPoint)offset;
+ (CGImageRef)newMaskImageForLayer:(CALayer<IJSVGDrawableLayer>*)layer
scale:(CGFloat)scale;
+ (CGImageRef)newImageForLayer:(CALayer<IJSVGDrawableLayer>*)layer
colorSpace:(CGColorSpaceRef)colorSpace
bitmapInfo:(uint32_t)bitmapInfo
scale:(CGFloat)scale;
+ (void)renderLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)ctx;
+ (void)applyBlendingMode:(CGBlendMode)blendMode
toContext:(CGContextRef)ctx
drawingBlock:(dispatch_block_t)drawingBlock;
+ (void)clipContextWithClip:(CALayer<IJSVGDrawableLayer>*)clipLayer
toLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)ctx
drawingBlock:(dispatch_block_t)drawingBlock;
+ (void)clipContextWithMask:(CALayer<IJSVGDrawableLayer>*)maskLayer
toLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)context
drawingBlock:(dispatch_block_t)drawingBlock;
+ (CALayer<IJSVGDrawableLayer>*)rootLayerForLayer:(CALayer<IJSVGDrawableLayer>*)layer;
+ (CGAffineTransform)absoluteTransformForLayer:(CALayer*)layer;
+ (CGRect)absoluteFrameForLayer:(CALayer<IJSVGDrawableLayer>*)layer;
+ (CGRect)calculateFrameForSublayers:(NSArray<CALayer<IJSVGDrawableLayer>*>*)layers;
+ (void)logLayer:(CALayer<IJSVGDrawableLayer>*)layer;
@end
+369 -39
View File
@@ -10,26 +10,255 @@
#import "IJSVGGroupLayer.h"
#import "IJSVGLayer.h"
#import "IJSVGShapeLayer.h"
#import "IJSVGTransformLayer.h"
#import "IJSVGRootLayer.h"
@implementation IJSVGLayer
@synthesize gradientFillLayer;
@synthesize patternFillLayer;
@synthesize gradientStrokeLayer;
@synthesize patternStrokeLayer;
@synthesize strokeLayer;
@synthesize requiresBackingScaleHelp;
@synthesize backingScaleFactor;
@synthesize blendingMode;
@synthesize convertMasksToPaths;
- (void)dealloc
{
self.contents = nil;
(void)([_clipLayer release]), _clipLayer = nil;
(void)([_maskLayer release]), _maskLayer = nil;
(void)([_clipRule release]), _clipRule = nil;
(void)([_fillRule release]), _fillRule = nil;
(void)([_maskingLayer release]), _maskingLayer = nil;
[super dealloc];
}
- (instancetype)init
{
if((self = [super init]) != nil) {
_boundingBox = CGRectNull;
_outerBoundingBox = CGRectNull;
}
return self;
}
- (CAShapeLayerFillRule)fillRule
{
return kCAFillRuleNonZero;
}
- (CAShapeLayerFillRule)clipRule
{
if(_clipRule == nil) {
return self.fillRule;
}
return _clipRule;
}
+ (IJSVGLayerFillType)fillTypeForFill:(id)fill
{
if([fill isKindOfClass:IJSVGColorNode.class]) {
return IJSVGLayerFillTypeColor;
}
if([fill isKindOfClass:IJSVGGradient.class]) {
return IJSVGLayerFillTypeGradient;
}
if([fill isKindOfClass:IJSVGPattern.class]) {
return IJSVGLayerFillTypePattern;
}
return IJSVGLayerFillTypeUnknown;
}
+ (CGAffineTransform)absoluteTransformForLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
CGAffineTransform identity = CGAffineTransformIdentity;
CALayer<IJSVGDrawableLayer>* parentLayer = layer;
while((parentLayer = parentLayer.referencingLayer) != nil) {
identity = [self absoluteTransformForLayer:parentLayer];
// only go up until we find a root layer, at that point, we know
// we can stop looking
if([parentLayer isKindOfClass:IJSVGRootLayer.class] == YES) {
break;
}
}
return CGAffineTransformConcat(identity, layer.affineTransform);
}
+ (CALayer<IJSVGDrawableLayer>*)rootLayerForLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
CALayer<IJSVGDrawableLayer>* parentLayer = (CALayer<IJSVGDrawableLayer>*)layer.referencingLayer;
while([parentLayer isKindOfClass:IJSVGRootLayer.class] == NO &&
parentLayer.referencingLayer != nil) {
parentLayer = (CALayer<IJSVGDrawableLayer>*)parentLayer.referencingLayer;
}
return parentLayer;
}
+ (void)renderLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)ctx
{
dispatch_block_t drawingBlock = ^{
[layer performRenderInContext:ctx];
};
[self applyBlendingMode:layer.blendingMode
toContext:ctx
drawingBlock:^{
// we need to clip first
if(layer.clipLayer == nil) {
drawingBlock();
return;
}
[self clipContextWithClip:layer.clipLayer
toLayer:layer
inContext:ctx
drawingBlock:drawingBlock];
}];
}
+ (void)applyBlendingMode:(CGBlendMode)blendMode
toContext:(CGContextRef)ctx
drawingBlock:(dispatch_block_t)drawingBlock
{
if (blendMode != kCGBlendModeNormal) {
CGContextSaveGState(ctx);
CGContextSetBlendMode(ctx, blendMode);
drawingBlock();
CGContextRestoreGState(ctx);
return;
}
drawingBlock();
}
/// Shape layers are the only thing we can clip, as they contain a path, however
/// the layer passed to us from a clip could have groups contained with in it that
/// have transforms on them, so simply recursively iterate over them and concat
/// their transforms down to the path and clip at the end with the path.
+ (void)recursivelyClip:(CALayer<IJSVGDrawableLayer>*)layer
transform:(CGAffineTransform)transform
inContext:(CGContextRef)ctx
{
if([layer isKindOfClass:IJSVGShapeLayer.class]) {
[self clipContextWithShapeLayer:(IJSVGShapeLayer*)layer
transform:transform
inContext:ctx];
return;
}
for(CALayer<IJSVGDrawableLayer>* drawableLayer in layer.sublayers) {
CGAffineTransform drawTransform = CGAffineTransformConcat(transform,
drawableLayer.affineTransform);
// If its not a shape layer, just go down the tree until we find one
if([drawableLayer isKindOfClass:IJSVGShapeLayer.class] == NO) {
[self recursivelyClip:drawableLayer
transform:drawTransform
inContext:ctx];
continue;
}
IJSVGShapeLayer* shapeLayer = (IJSVGShapeLayer*)drawableLayer;
[self clipContextWithShapeLayer:shapeLayer
transform:transform
inContext:ctx];
}
}
+ (void)clipContextWithShapeLayer:(IJSVGShapeLayer*)shapeLayer
transform:(CGAffineTransform)transform
inContext:(CGContextRef)ctx
{
CGAffineTransform drawTransform = CGAffineTransformConcat(transform,
shapeLayer.affineTransform);
// Shape layers paths are reset back to 0,0 origin, so in order to be
// correct path, we need to shift it back to where it belongs.
drawTransform = CGAffineTransformTranslate(transform,
shapeLayer.frame.origin.x,
shapeLayer.frame.origin.y);
CGPathRef path = CGPathCreateCopyByTransformingPath(shapeLayer.path,
&drawTransform);
CGContextAddPath(ctx, path);
CGPathRelease(path);
}
+ (void)clipContextWithClip:(CALayer<IJSVGDrawableLayer>*)clipLayer
toLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)ctx
drawingBlock:(dispatch_block_t)drawingBlock
{
CGContextSaveGState(ctx);
[self recursivelyClip:clipLayer
transform:CGAffineTransformIdentity
inContext:ctx];
if([layer.clipRule isEqualToString:kCAFillRuleEvenOdd]) {
CGContextEOClip(ctx);
} else {
CGContextClip(ctx);
}
drawingBlock();
CGContextRestoreGState(ctx);
}
+ (void)clipContextWithMask:(CALayer<IJSVGDrawableLayer>*)maskLayer
toLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)ctx
drawingBlock:(dispatch_block_t)drawingBlock
{
CGContextSaveGState(ctx);
CGRect bounds = layer.outerBoundingBox;
CGFloat scale = layer.backingScaleFactor;
CGImageRef maskImage = [self newMaskImageForLayer:maskLayer
scale:scale];
CGContextClipToMask(ctx, bounds, maskImage);
drawingBlock();
CGImageRelease(maskImage);
CGContextRestoreGState(ctx);
}
+ (CGImageRef)newMaskImageForLayer:(CALayer<IJSVGDrawableLayer>*)layer
scale:(CGFloat)scale
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGImageRef ref = [self newImageForLayer:layer
colorSpace:colorSpace
bitmapInfo:kCGImageAlphaNone
scale:scale];
CGColorSpaceRelease(colorSpace);
return ref;
}
+ (CGImageRef)newImageForLayer:(CALayer<IJSVGDrawableLayer>*)layer
colorSpace:(CGColorSpaceRef)colorSpace
bitmapInfo:(uint32_t)bitmapInfo
scale:(CGFloat)scale
{
CGRect frame = layer.outerBoundingBox;
CGContextRef offscreenContext = CGBitmapContextCreate(NULL,
ceilf(frame.size.width * scale),
ceilf(frame.size.height * scale),
8, 0, colorSpace, bitmapInfo);
CGContextSaveGState(offscreenContext);
CGContextScaleCTM(offscreenContext, scale, scale);
CGContextTranslateCTM(offscreenContext, -frame.origin.x, -frame.origin.y);
[layer renderInContext:offscreenContext];
CGImageRef image = CGBitmapContextCreateImage(offscreenContext);
CGContextRelease(offscreenContext);
return image;
}
+ (CGRect)absoluteFrameForLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
return (CGRect) {
.origin = [self absoluteOriginForLayer:layer],
.size = layer.frame.size
};
}
+ (CGPoint)absoluteOriginForLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
CGPoint point = CGPointZero;
CALayer<IJSVGDrawableLayer>* pLayer = layer;
while (pLayer != nil) {
point.x += pLayer.frame.origin.x;
point.y += pLayer.frame.origin.y;
pLayer = pLayer.referencingLayer;
}
return point;
}
+ (NSArray*)deepestSublayersOfLayer:(CALayer*)layer
{
NSMutableArray* arr = [[[NSMutableArray alloc] init] autorelease];
@@ -44,44 +273,90 @@
return arr;
}
+ (void)recursivelyWalkLayer:(CALayer*)layer
withBlock:(void (^)(CALayer* layer, BOOL isMask))block
+ (void)recursivelyWalkLayer:(CALayer<IJSVGDrawableLayer>*)layer
withBlock:(void (^)(CALayer<IJSVGDrawableLayer>* layer, BOOL* stop))block
{
// call for layer and mask if there is one
block(layer, NO);
// do the mask too!
if (layer.mask != nil) {
block(layer.mask, YES);
BOOL stop = NO;
block(layer, &stop);
if(stop == YES) {
return;
}
// sublayers!!
for (CALayer* aLayer in layer.sublayers) {
for (CALayer<IJSVGDrawableLayer>* aLayer in layer.sublayers) {
[self recursivelyWalkLayer:aLayer
withBlock:block];
}
}
+ (void)logLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
[self logLayer:layer
depth:0];
}
+ (void)logLayer:(CALayer<IJSVGDrawableLayer>*)layer
depth:(NSUInteger)depth
{
NSLog(@"%@ %@ frame: %@ transform: %@",[@"" stringByPaddingToLength:depth
withString:@"- - "
startingAtIndex:0], layer,
NSStringFromRect(layer.frame),
[IJSVGTransform affineTransformToSVGTransformComponentString:layer.affineTransform]);
for(CALayer<IJSVGDrawableLayer>* sublayer in layer.debugLayers) {
[self logLayer:sublayer
depth:depth+1];
}
}
+ (CGRect)calculateFrameForSublayers:(NSArray<CALayer<IJSVGDrawableLayer>*>*)layers
{
CGRect rect = CGRectNull;
for(CALayer<IJSVGDrawableLayer>* layer in layers) {
CGRect layerFrame = layer.outerBoundingBox;
// if we are a transform layer, we can just apply its transform
// to its sublayers and keep going down the tree
if([layer isKindOfClass:IJSVGTransformLayer.class] == YES) {
CGRect frame = [self calculateFrameForSublayers:layer.sublayers];
frame = CGRectApplyAffineTransform(frame, layer.affineTransform);
layerFrame = frame;
}
if(CGRectIsNull(rect)) {
rect = layerFrame;
continue;
}
rect = CGRectUnion(rect, layerFrame);
}
return rect;
}
- (void)setBackingScaleFactor:(CGFloat)newFactor
{
if (self.backingScaleFactor == newFactor) {
if (_backingScaleFactor == newFactor) {
return;
}
backingScaleFactor = newFactor;
_backingScaleFactor = newFactor;
self.contentsScale = newFactor;
self.rasterizationScale = newFactor;
// make sure its applied to any mask or clipPath
_maskLayer.backingScaleFactor = newFactor;
_clipLayer.backingScaleFactor = newFactor;
[self setNeedsDisplay];
}
- (void)_customRenderInContext:(CGContextRef)ctx
- (void)performRenderInContext:(CGContextRef)ctx
{
if (self.convertMasksToPaths == YES && _maskingLayer != nil) {
CGContextSaveGState(ctx);
[self applySublayerMaskToContext:ctx
forSublayer:(IJSVGLayer*)self
withOffset:CGPointZero];
[super renderInContext:ctx];
CGContextRestoreGState(ctx);
if(_maskLayer != nil) {
[self.class clipContextWithMask:_maskLayer
toLayer:self
inContext:ctx
drawingBlock:^{
[super renderInContext:ctx];
}];
return;
}
[super renderInContext:ctx];
@@ -89,10 +364,10 @@
- (void)setConvertMasksToPaths:(BOOL)flag
{
if (convertMasksToPaths == flag) {
if (_convertMasksToPaths == flag) {
return;
}
convertMasksToPaths = flag;
_convertMasksToPaths = flag;
if (flag == YES) {
if (_maskingLayer != nil) {
(void)([_maskingLayer release]), _maskingLayer = nil;
@@ -145,6 +420,21 @@
CGContextConcatCTM(context, sublayerTransform);
}
- (CALayer<IJSVGDrawableLayer>*)rootLayer
{
return [self.class rootLayerForLayer:self];
}
- (CGAffineTransform)absoluteTransform
{
return [IJSVGLayer absoluteTransformForLayer:self];
}
- (BOOL)requiresBackingScaleHelp
{
return _maskLayer != nil || _clipLayer != nil;
}
- (IJSVGShapeLayer*)maskingLayer
{
return (IJSVGShapeLayer*)_maskingLayer ?: nil;
@@ -152,14 +442,8 @@
- (void)renderInContext:(CGContextRef)ctx
{
if (self.blendingMode != kCGBlendModeNormal) {
CGContextSaveGState(ctx);
CGContextSetBlendMode(ctx, self.blendingMode);
[self _customRenderInContext:ctx];
CGContextRestoreGState(ctx);
return;
}
[self _customRenderInContext:ctx];
[IJSVGLayer renderLayer:self
inContext:ctx];
}
- (id<CAAction>)actionForKey:(NSString*)event
@@ -167,4 +451,50 @@
return nil;
}
- (CGRect)absoluteFrame
{
return [self.class absoluteFrameForLayer:self];
}
- (CGRect)boundingBox
{
return CGRectIsNull(_boundingBox) == NO ? _boundingBox : self.frame;
}
- (CGRect)outerBoundingBox
{
return CGRectIsNull(_outerBoundingBox) == NO ? _outerBoundingBox : self.frame;
}
- (CGRect)outerBoundingBoxBounds
{
return (CGRect) {
.origin = CGPointZero,
.size = self.outerBoundingBox.size
};
}
- (CGRect)boundingBoxBounds
{
return (CGRect) {
.origin = CGPointZero,
.size = self.boundingBox.size
};
}
- (CGRect)strokeBoundingBox
{
return self.boundingBox;
}
- (CALayer<IJSVGDrawableLayer> *)referencingLayer
{
return _referencingLayer ?: self.superlayer;
}
-(NSArray<CALayer<IJSVGDrawableLayer>*>*)debugLayers
{
return self.sublayers;
}
@end
@@ -12,7 +12,7 @@
@interface IJSVGPatternLayer : IJSVGLayer
@property (nonatomic, retain) IJSVGLayer* pattern;
@property (nonatomic, retain) CALayer<IJSVGDrawableLayer>* pattern;
@property (nonatomic, retain) IJSVGPattern* patternNode;
@end
@@ -6,36 +6,61 @@
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import "IJSVGPatternLayer.h"
#import <IJSVG/IJSVGPatternLayer.h>
#import <IJSVG/IJSVGTransform.h>
#import <IJSVG/IJSVGUnitRect.h>
#import <IJSVG/IJSVGViewBox.h>
@interface IJSVGPatternLayer ()
@property (nonatomic, assign, readonly) CGSize cellSize;
@property (nonatomic, assign) CGRect viewBox;
@end
@implementation IJSVGPatternLayer
@synthesize pattern;
@synthesize patternNode;
- (void)dealloc
{
(void)([pattern release]), pattern = nil;
(void)([patternNode release]), patternNode = nil;
(void)([_pattern release]), _pattern = nil;
(void)([_patternNode release]), _patternNode = nil;
[super dealloc];
}
- (id)init
- (BOOL)requiresBackingScaleHelp
{
if ((self = [super init]) != nil) {
self.requiresBackingScaleHelp = YES;
self.shouldRasterize = YES;
}
return self;
return YES;
}
void IJSVGPatternDrawingCallBack(void* info, CGContextRef ctx)
{
// reassign the layer
IJSVGPatternLayer* layer = (IJSVGPatternLayer*)info;
[layer.pattern renderInContext:ctx];
CGSize size = layer.cellSize;
CGContextSaveGState(ctx);
CGRect rect = CGRectMake(0.f, 0.f, size.width, size.height);
CGContextClipToRect(ctx, rect);
IJSVGViewBoxAlignment alignment = layer.patternNode.viewBoxAlignment;
IJSVGViewBoxMeetOrSlice meetOrSlice = layer.patternNode.viewBoxMeetOrSlice;
CGRect viewBox = layer.viewBox;
[IJSVGViewBox drawViewBox:viewBox
inRect:rect
alignment:alignment
meetOrSlice:meetOrSlice
inContext:ctx
drawingBlock:^(CGSize size) {
[layer.pattern renderInContext:ctx];
}];
CGContextSaveGState(ctx);
};
- (CALayer<IJSVGDrawableLayer>*)referencingLayer
{
return [super referencingLayer] ?: self.superlayer;
}
- (void)drawInContext:(CGContextRef)ctx
{
// holder for callback
@@ -45,13 +70,54 @@ void IJSVGPatternDrawingCallBack(void* info, CGContextRef ctx)
CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
CGContextSetFillColorSpace(ctx, patternSpace);
CGColorSpaceRelease(patternSpace);
CGRect rect = self.referencingLayer.boundingBoxBounds;
IJSVGUnitLength* wLength = _patternNode.width;
IJSVGUnitLength* hLength = _patternNode.height;
if(self.patternNode.units == IJSVGUnitObjectBoundingBox ||
self.patternNode.contentUnits == IJSVGUnitObjectBoundingBox) {
wLength = wLength.lengthByMatchingPercentage;
hLength = hLength.lengthByMatchingPercentage;
}
CGFloat width = [wLength computeValue:rect.size.width];
CGFloat height = [hLength computeValue:rect.size.height];
_cellSize = CGSizeMake(width, height);
CALayer<IJSVGDrawableLayer>* layer = (CALayer<IJSVGDrawableLayer>*)self.referencingLayer;
// transform us back into the correct space
CGAffineTransform transform = CGAffineTransformIdentity;
if (self.patternNode.units == IJSVGUnitUserSpaceOnUse) {
CGRect frame = layer.boundingBox;
transform = [IJSVGLayer absoluteTransformForLayer:layer];
transform = CGAffineTransformTranslate(transform,
-CGRectGetMinX(frame),
-CGRectGetMinY(frame));
}
transform = CGAffineTransformConcat(transform, IJSVGConcatTransforms(self.patternNode.transforms));
// transform the X and Y shift
transform = CGAffineTransformTranslate(transform,
[_patternNode.x computeValue:rect.size.width],
[_patternNode.y computeValue:rect.size.height]);
// who knew that patterns have viewBoxes? Not me, but here is an implementation
// of it anyway
if(_patternNode.viewBox != nil && _patternNode.viewBox.isZeroRect == NO) {
if(_patternNode.units == IJSVGUnitObjectBoundingBox) {
IJSVGUnitRect* viewBox = nil;
viewBox = [[_patternNode.viewBox copyByConvertingToUnitsLengthType:IJSVGUnitLengthTypePercentage] autorelease];
_viewBox = [viewBox computeValue:rect.size];
}
}
// create the pattern
CGRect rect = self.bounds;
CGPatternRef ref = CGPatternCreate((void*)self, self.bounds,
CGAffineTransformIdentity,
roundf(rect.size.width * self.patternNode.width.value),
roundf(rect.size.height * self.patternNode.height.value),
CGRect selfBounds = self.boundingBoxBounds;
CGPatternRef ref = CGPatternCreate((void*)self, selfBounds,
transform, width, height,
kCGPatternTilingConstantSpacing,
true, &callbacks);
@@ -61,7 +127,12 @@ void IJSVGPatternDrawingCallBack(void* info, CGContextRef ctx)
CGPatternRelease(ref);
// fill it
CGContextFillRect(ctx, rect);
CGContextFillRect(ctx, selfBounds);
}
- (NSArray<CALayer<IJSVGDrawableLayer>*>*)debugLayers
{
return @[self.pattern];
}
@end
@@ -1,159 +0,0 @@
//
// IJSVGRadialGradient.m
// IJSVGExample
//
// Created by Curtis Hard on 03/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGRadialGradient.h"
@implementation IJSVGRadialGradient
@synthesize cx;
@synthesize cy;
@synthesize fx;
@synthesize fy;
@synthesize radius;
- (void)dealloc
{
(void)([cx release]), cx = nil;
(void)([cy release]), cy = nil;
(void)([fx release]), fx = nil;
(void)([fy release]), fy = nil;
(void)([radius release]), radius = nil;
[super dealloc];
}
- (id)copyWithZone:(NSZone*)zone
{
IJSVGRadialGradient* grad = [super copyWithZone:zone];
grad.fx = self.fx;
grad.fy = self.fy;
grad.cx = self.cx;
grad.cy = self.cy;
grad.radius = self.radius;
return grad;
}
+ (NSGradient*)parseGradient:(NSXMLElement*)element
gradient:(IJSVGRadialGradient*)gradient
{
// cx defaults to 50% if not specified
NSDictionary* kv = @{ @"cx" : @"cx",
@"cy" : @"cy",
@"r" : @"radius" };
for (NSString* key in kv.allKeys) {
NSString* str = [element attributeForName:key].stringValue;
IJSVGUnitLength* unit = nil;
if (str != nil) {
unit = [IJSVGUnitLength unitWithString:str
fromUnitType:gradient.units];
} else {
// spec says to say 50% for missing property default
unit = [IJSVGUnitLength unitWithPercentageFloat:.5f];
}
[gradient setValue:unit
forKey:kv[key]];
}
// fx and fy are the same unless specified otherwise
gradient.fx = gradient.cx;
gradient.fy = gradient.cy;
// needs fixing
NSString* fx = [element attributeForName:@"fx"].stringValue;
if (fx != nil) {
gradient.fx = [IJSVGUnitLength unitWithString:fx
fromUnitType:gradient.units];
}
NSString* fy = [element attributeForName:@"fy"].stringValue;
if (fx != nil) {
gradient.fy = [IJSVGUnitLength unitWithString:fy
fromUnitType:gradient.units];
}
if (gradient.gradient != nil) {
return nil;
}
NSArray* colors = nil;
CGFloat* colorStops = [self.class computeColorStopsFromString:element colors:&colors];
NSGradient* ret = [[[NSGradient alloc] initWithColors:colors
atLocations:colorStops
colorSpace:IJSVGColor.defaultColorSpace] autorelease];
free(colorStops);
return ret;
}
- (void)drawInContextRef:(CGContextRef)ctx
objectRect:(NSRect)objectRect
absoluteTransform:(CGAffineTransform)absoluteTransform
viewPort:(CGRect)viewBox
{
BOOL inUserSpace = self.units == IJSVGUnitUserSpaceOnUse;
CGFloat radius = 0.f;
CGPoint startPoint = CGPointZero;
CGPoint gradientStartPoint = CGPointZero;
CGPoint gradientEndPoint = CGPointZero;
// transforms
CGAffineTransform selfTransform = IJSVGConcatTransforms(self.transforms);
CGRect boundingBox = inUserSpace ? viewBox : objectRect;
// make sure we apply the absolute position to
// transform us back into the correct space
if (inUserSpace == YES) {
CGContextConcatCTM(ctx, absoluteTransform);
}
// compute size based on percentages
CGFloat x = [self.cx computeValue:CGRectGetWidth(boundingBox)];
CGFloat y = [self.cy computeValue:CGRectGetHeight(boundingBox)];
startPoint = CGPointMake(x, y);
CGFloat val = MIN(CGRectGetWidth(boundingBox),
CGRectGetHeight(boundingBox));
radius = [self.radius computeValue:val];
CGFloat ex = [self.fx computeValue:CGRectGetWidth(boundingBox)];
CGFloat ey = [self.fy computeValue:CGRectGetHeight(boundingBox)];
gradientEndPoint = CGPointMake(ex, ey);
gradientStartPoint = startPoint;
// transform if width or height is not equal - this can only
// be done if we are using objectBoundingBox
if (inUserSpace == NO && CGRectGetWidth(boundingBox) != CGRectGetHeight(boundingBox)) {
CGAffineTransform tr = CGAffineTransformMakeTranslation(gradientStartPoint.x,
gradientStartPoint.y);
if (CGRectGetWidth(boundingBox) > CGRectGetHeight(boundingBox)) {
tr = CGAffineTransformScale(tr, CGRectGetWidth(boundingBox) / CGRectGetHeight(boundingBox), 1);
} else {
tr = CGAffineTransformScale(tr, 1.f, CGRectGetHeight(boundingBox) / CGRectGetWidth(boundingBox));
}
tr = CGAffineTransformTranslate(tr, -gradientStartPoint.x, -gradientStartPoint.y);
selfTransform = CGAffineTransformConcat(tr, selfTransform);
}
// transform the context
CGContextConcatCTM(ctx, selfTransform);
// draw the gradient
CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
CGContextDrawRadialGradient(ctx, self.CGGradient,
gradientEndPoint, 0,
gradientStartPoint,
radius, options);
#ifdef IJSVG_DEBUG_GRADIENTS
[self _debugStart:gradientStartPoint
end:gradientEndPoint
context:ctx];
#endif
}
@end
@@ -0,0 +1,23 @@
//
// IJSVGRootLayer.h
// IJSVG
//
// Created by Curtis Hard on 15/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVG.h>
@interface IJSVGRootLayer : IJSVGGroupLayer {
@private
BOOL _disableBackingScalePropagation;
}
- (void)renderInContext:(CGContextRef)ctx
viewPort:(CGRect)viewPort
backingScale:(CGFloat)backingScale
quality:(IJSVGRenderQuality)quality;
@end
@@ -0,0 +1,71 @@
//
// IJSVGRootLayer.m
// IJSVG
//
// Created by Curtis Hard on 15/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import "IJSVGRootLayer.h"
@implementation IJSVGRootLayer
- (void)performRenderInContext:(CGContextRef)ctx
{
if(self.viewBox != nil) {
CGRect viewBox = [self.viewBox computeValue:CGSizeZero];
__block IJSVGRootLayer* weakSelf = self;
IJSVGViewBoxDrawingBlock drawingBlock = ^(CGSize size) {
// we have to make sure we set the backing scale factor once
// we know how scale this will be drawn at
weakSelf.backingScaleFactor *= MAX(size.width, size.height);
[super performRenderInContext:ctx];
};
[IJSVGViewBox drawViewBox:viewBox
inRect:self.boundingBoxBounds
alignment:self.viewBoxAlignment
meetOrSlice:self.viewBoxMeetOrSlice
inContext:ctx
drawingBlock:drawingBlock];
return;
}
[super performRenderInContext:ctx];
}
- (void)setBackingScaleFactor:(CGFloat)backingScaleFactor
{
[super setBackingScaleFactor:backingScaleFactor];
if(_disableBackingScalePropagation == YES) {
return;
}
[self propagateBackingScalePropertiesToSublayers];
}
- (void)propagateBackingScalePropertiesToSublayers
{
__block IJSVGRootLayer* weakSelf = self;
for(CALayer<IJSVGDrawableLayer>* layer in self.sublayers) {
[IJSVGLayer recursivelyWalkLayer:layer
withBlock:^(CALayer<IJSVGDrawableLayer>* propLayer, BOOL *stop) {
propLayer.renderQuality = weakSelf.renderQuality;
propLayer.backingScaleFactor = weakSelf.backingScaleFactor;
}];
}
}
- (void)renderInContext:(CGContextRef)ctx
viewPort:(CGRect)viewPort
backingScale:(CGFloat)backingScale
quality:(IJSVGRenderQuality)quality
{
CGRect frame = viewPort;
self.frame = frame;
_disableBackingScalePropagation = YES;
self.backingScaleFactor = backingScale;
self.renderQuality = quality;
_disableBackingScalePropagation = NO;
[self propagateBackingScalePropertiesToSublayers];
[self renderInContext:ctx];
}
@end
@@ -6,11 +6,11 @@
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import "IJSVGLayer.h"
#import "IJSVGUtils.h"
#import <IJSVG/IJSVGLayer.h>
#import <IJSVG/IJSVGUtils.h>
#import <QuartzCore/QuartzCore.h>
@interface IJSVGShapeLayer : CAShapeLayer {
@interface IJSVGShapeLayer : CAShapeLayer <IJSVGDrawableLayer, IJSVGPathableLayer> {
@private
IJSVGLayer* _maskingLayer;
@@ -21,14 +21,25 @@
@property (nonatomic, assign) IJSVGStrokeLayer* strokeLayer;
@property (nonatomic, assign) IJSVGGradientLayer* gradientStrokeLayer;
@property (nonatomic, assign) IJSVGPatternLayer* patternStrokeLayer;
@property (nonatomic, assign) BOOL requiresBackingScaleHelp;
@property (nonatomic, assign) CGFloat backingScaleFactor;
@property (nonatomic, readonly) BOOL requiresBackingScaleHelp;
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
@property (nonatomic, assign) CGBlendMode blendingMode;
@property (nonatomic, assign) CGPoint absoluteOrigin;
@property (nonatomic, assign) CGPoint originalPathOrigin;
@property (nonatomic, assign) BOOL convertMasksToPaths;
@property (nonatomic, assign) IJSVGPrimitivePathType primitiveType;
@property (nonatomic, readonly) CGRect computedFrame;
@property (nonatomic, retain) CALayer<IJSVGDrawableLayer>* clipLayer;
@property (nonatomic, retain) CALayer<IJSVGDrawableLayer>* maskLayer;
@property (nonatomic, readonly) CALayer<IJSVGDrawableLayer>* rootLayer;
@property (nonatomic, readonly) CGRect absoluteFrame;
@property (nonatomic, assign) CGRect boundingBox;
@property (nonatomic, assign) CGRect outerBoundingBox;
@property (nonatomic, readonly) CGRect boundingBoxBounds;
@property (nonatomic, assign) CALayer<IJSVGDrawableLayer>* referencingLayer;
@property (nonatomic, assign) CGRect strokeBoundingBox;
@property (nonatomic, copy) CAShapeLayerFillRule clipRule;
- (void)applySublayerMaskToContext:(CGContextRef)context
forSublayer:(IJSVGLayer*)sublayer
@@ -11,46 +11,74 @@
@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
{
self.contents = nil;
(void)([_clipLayer release]), _clipLayer = nil;
(void)([_maskLayer release]), _maskLayer = nil;
(void)([_maskingLayer release]), _maskingLayer = nil;
(void)([_clipRule release]), _clipRule = nil;
[super dealloc];
}
- (CALayer<IJSVGDrawableLayer> *)rootLayer
{
return [IJSVGLayer rootLayerForLayer:self];
}
- (CGRect)absoluteFrame
{
return [IJSVGLayer absoluteFrameForLayer:self];
}
- (CGAffineTransform)absoluteTransform
{
return [IJSVGLayer absoluteTransformForLayer:self];
}
- (CALayer<IJSVGDrawableLayer> *)referencingLayer
{
return _referencingLayer ?: self.superlayer;
}
- (CGRect)boundingBoxBounds
{
return (CGRect) {
.origin = CGPointZero,
.size = self.boundingBox.size
};
}
- (BOOL)requiresBackingScaleHelp
{
return _maskLayer != nil || _clipLayer != nil;
}
- (void)setBackingScaleFactor:(CGFloat)newFactor
{
if (self.backingScaleFactor == newFactor) {
if (_backingScaleFactor == newFactor) {
return;
}
backingScaleFactor = newFactor;
_backingScaleFactor = newFactor;
self.contentsScale = newFactor;
self.rasterizationScale = newFactor;
// make sure its applied to any mask or clipPath
_maskLayer.backingScaleFactor = newFactor;
_clipLayer.backingScaleFactor = newFactor;
[self setNeedsDisplay];
}
- (void)_customRenderInContext:(CGContextRef)ctx
- (void)performRenderInContext:(CGContextRef)ctx
{
if (self.convertMasksToPaths == YES && _maskingLayer != nil) {
CGContextSaveGState(ctx);
[self applySublayerMaskToContext:ctx
forSublayer:(IJSVGLayer*)self
withOffset:CGPointZero];
[super renderInContext:ctx];
CGContextRestoreGState(ctx);
if(_maskLayer != nil) {
[IJSVGLayer clipContextWithMask:_maskLayer
toLayer:self
inContext:ctx
drawingBlock:^{
[super renderInContext:ctx];
}];
return;
}
[super renderInContext:ctx];
@@ -58,10 +86,10 @@
- (void)setConvertMasksToPaths:(BOOL)flag
{
if (convertMasksToPaths == flag) {
if (_convertMasksToPaths == flag) {
return;
}
convertMasksToPaths = flag;
_convertMasksToPaths = flag;
if (flag == YES) {
if (_maskingLayer != nil) {
(void)([_maskingLayer release]), _maskingLayer = nil;
@@ -121,26 +149,8 @@
- (void)renderInContext:(CGContextRef)ctx
{
if (self.blendingMode != kCGBlendModeNormal) {
CGContextSaveGState(ctx);
CGContextSetBlendMode(ctx, self.blendingMode);
[self _customRenderInContext:ctx];
CGContextRestoreGState(ctx);
return;
}
[self _customRenderInContext:ctx];
}
- (CGPoint)absoluteOrigin
{
CGPoint point = CGPointZero;
CALayer* pLayer = self;
while (pLayer != nil) {
point.x += pLayer.frame.origin.x;
point.y += pLayer.frame.origin.y;
pLayer = pLayer.superlayer;
}
return point;
[IJSVGLayer renderLayer:(IJSVGLayer*)self
inContext:ctx];
}
- (id<CAAction>)actionForKey:(NSString*)event
@@ -148,4 +158,9 @@
return nil;
}
-(NSArray<CALayer<IJSVGDrawableLayer>*>*)debugLayers
{
return self.sublayers;
}
@end
@@ -10,4 +10,11 @@
@implementation IJSVGStrokeLayer
- (CGRect)outerBoundingBox
{
return CGRectMake(-self.lineWidth / 2.f, -self.lineWidth / 2.f,
self.boundingBox.size.width + self.lineWidth,
self.boundingBox.size.height + self.lineWidth);
}
@end
@@ -0,0 +1,21 @@
//
// IJSVGTransformLayer.h
// IJSVG
//
// Created by Curtis Hard on 31/03/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
#import <IJSVG/IJSVGLayer.h>
@interface IJSVGTransformLayer : CATransformLayer <IJSVGDrawableLayer>
@property (nonatomic, assign) CGBlendMode blendingMode;
@property (nonatomic, retain) CALayer<IJSVGDrawableLayer>* clipLayer;
@property (nonatomic, readonly) CGPoint absoluteOrigin;
@property (nonatomic, readonly) CGRect computedFrame;
@property (nonatomic, readonly) CALayer<IJSVGDrawableLayer>* referencedLayer;
@property (nonatomic, assign) CALayer<IJSVGDrawableLayer>* referencingLayer;
@end
@@ -0,0 +1,69 @@
//
// IJSVGTransformLayer.m
// IJSVG
//
// Created by Curtis Hard on 31/03/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import "IJSVGTransformLayer.h"
@implementation IJSVGTransformLayer
@synthesize backingScaleFactor;
@synthesize renderQuality;
@synthesize requiresBackingScaleHelp;
@synthesize maskLayer = _maskLayer;
@synthesize fillRule = _fillRule;
@synthesize clipRule = _clipRule;
@synthesize absoluteFrame;
@synthesize boundingBox;
@synthesize boundingBoxBounds;
@synthesize strokeBoundingBox;
@synthesize outerBoundingBox;
- (void)dealloc
{
(void)[_maskLayer release], _maskLayer = nil;
(void)[_fillRule release], _fillRule = nil;
(void)[_clipRule release], _clipRule = nil;
(void)[_clipLayer release], _clipLayer = nil;
[super dealloc];
}
- (CALayer<IJSVGDrawableLayer> *)referencedLayer
{
return self.sublayers.firstObject;
}
- (CALayer<IJSVGDrawableLayer> *)referencingLayer {
return _referencingLayer ?: self.superlayer;
}
- (CALayer<IJSVGDrawableLayer>*)rootLayer
{
return [IJSVGLayer rootLayerForLayer:self];
}
- (BOOL)requiresBackingScaleHelp
{
return YES;
}
- (void)renderInContext:(CGContextRef)ctx
{
[IJSVGLayer renderLayer:self
inContext:ctx];
}
- (void)performRenderInContext:(CGContextRef)ctx
{
[super renderInContext:ctx];
}
- (NSArray<CALayer<IJSVGDrawableLayer>*>*)debugLayers
{
return self.sublayers;
}
@end
@@ -0,0 +1,21 @@
//
// IJSVGColorNode.h
// IJSVG
//
// Created by Curtis Hard on 29/03/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGNode.h>
@interface IJSVGColorNode : IJSVGNode {
}
@property (nonatomic, retain) NSColor* color;
+ (IJSVGNode*)colorNodeWithColor:(NSColor*)color;
- (id)initWithColor:(NSColor*)color;
@end
@@ -0,0 +1,39 @@
//
// IJSVGColorNode.m
// IJSVG
//
// Created by Curtis Hard on 29/03/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import "IJSVGColorNode.h"
@implementation IJSVGColorNode
- (void)dealloc
{
(void)[_color release], _color = nil;
[super dealloc];
}
+ (IJSVGNode*)colorNodeWithColor:(NSColor *)color
{
return [[[self alloc] initWithColor:color] autorelease];
}
- (id)initWithColor:(NSColor*)color {
if((self = [super init]) != nil) {
[self addTraits:IJSVGNodeTraitPaintable];
self.color = color;
}
return self;
}
- (void)applyPropertiesFromNode:(IJSVGNode*)node
{
if([node isKindOfClass:self.class]) {
self.color = ((IJSVGColorNode*)node).color;
}
}
@end
@@ -6,7 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGNode.h"
#import <IJSVG/IJSVGNode.h>
#import <Foundation/Foundation.h>
@interface IJSVGDef : NSObject {
@@ -7,14 +7,11 @@
//
#import <Foundation/Foundation.h>
#import "IJSVGNode.h"
#import <IJSVG/IJSVGNode.h>
@interface IJSVGForeignObject : IJSVGNode {
NSString * requiredExtension;
}
@property ( nonatomic, copy ) NSString * requiredExtension;
@property (nonatomic, copy) NSString* requiredExtension;
@end
@@ -10,11 +10,9 @@
@implementation IJSVGForeignObject
@synthesize requiredExtension;
- (void)dealloc
{
(void)([requiredExtension release]), requiredExtension = nil;
(void)([_requiredExtension release]), _requiredExtension = nil;
[super dealloc];
}
@@ -6,12 +6,13 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGColorList.h"
#import "IJSVGDef.h"
#import "IJSVGTransform.h"
#import <Foundation/Foundation.h>
#import <IJSVG/IJSVGColorList.h>
#import <IJSVG/IJSVGDef.h>
#import <IJSVG/IJSVGTransform.h>
#import <IJSVG/IJSVGGroup.h>
@interface IJSVGGradient : IJSVGNode
@interface IJSVGGradient : IJSVGGroup
@property (nonatomic, retain) NSGradient* gradient;
@property (nonatomic, assign) CGGradientRef CGGradient;
@@ -21,8 +22,9 @@
@property (nonatomic, retain) IJSVGUnitLength* y2;
@property (nonatomic, retain) IJSVGColorList* colorList;
+ (CGFloat*)computeColorStopsFromString:(NSXMLElement*)element
colors:(NSArray**)someColors;
+ (CGFloat*)computeColorStops:(IJSVGGradient*)gradient
colors:(NSArray**)someColors;
- (CGGradientRef)CGGradient;
- (void)drawInContextRef:(CGContextRef)ctx
objectRect:(NSRect)objectRect
@@ -33,6 +35,7 @@
end:(CGPoint)endPoint
context:(CGContextRef)ctx;
- (IJSVGColorList*)colorList;
- (IJSVGColorList*)computedColorList;
@end
@@ -7,23 +7,22 @@
//
#import "IJSVGGradient.h"
#import "IJSVGParser.h"
@implementation IJSVGGradient
@synthesize gradient, CGGradient;
@synthesize x1, x2, y1, y2;
@synthesize colorList = _colorList;
@synthesize colorList = _privateColorList;
- (void)dealloc
{
(void)([x1 release]), x1 = nil;
(void)([x2 release]), x2 = nil;
(void)([y1 release]), y1 = nil;
(void)([y2 release]), y2 = nil;
(void)([gradient release]), gradient = nil;
(void)([_colorList release]), _colorList = nil;
if (CGGradient != nil) {
CGGradientRelease(CGGradient);
(void)([_x1 release]), _x1 = nil;
(void)([_x2 release]), _x2 = nil;
(void)([_y1 release]), _y1 = nil;
(void)([_y2 release]), _y2 = nil;
(void)([_gradient release]), _gradient = nil;
(void)([_privateColorList release]), _privateColorList = nil;
if (_CGGradient != nil) {
CGGradientRelease(_CGGradient);
}
[super dealloc];
}
@@ -37,84 +36,41 @@
- (void)setColorList:(IJSVGColorList*)list
{
(void)([_colorList release]), _colorList = nil;
_colorList = list.retain;
if (CGGradient != nil) {
CGGradientRelease(CGGradient);
(void)([_privateColorList release]), _privateColorList = nil;
_privateColorList = list.retain;
if (_CGGradient != nil) {
CGGradientRelease(_CGGradient);
_CGGradient = nil;
}
}
+ (CGFloat*)computeColorStopsFromString:(NSXMLElement*)element
colors:(NSArray**)someColors
+ (CGFloat *)computeColorStops:(IJSVGGradient*)gradient
colors:(NSArray**)someColors
{
// find each stop element
NSArray* stops = [element children];
NSArray<IJSVGNode*>* stops = gradient.children;
NSMutableArray* colors = [[[NSMutableArray alloc] initWithCapacity:stops.count] autorelease];
CGFloat* stopsParams = (CGFloat*)malloc(stops.count * sizeof(CGFloat));
NSInteger i = 0;
for (NSXMLElement* stop in stops) {
// find the offset
CGFloat offset = [stop attributeForName:@"offset"].stringValue.floatValue;
if (offset > 1.f) {
offset /= 100.f;
}
for(IJSVGNode* stopNode in stops) {
NSColor* color = ((IJSVGColorNode*)(stopNode.fill)).color;
CGFloat opacity = stopNode.fillOpacity.value;
CGFloat offset = stopNode.offset.value;
stopsParams[i++] = offset;
// find the stop opacity
CGFloat stopOpacity = 1.f;
NSXMLNode* stopOpacityAttribute = [stop attributeForName:@"stop-opacity"];
if (stopOpacityAttribute != nil) {
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;
if(color == nil) {
color = [IJSVGColor colorFromHEXInteger:0x000000];
if(opacity != 1.f) {
color = [IJSVGColor changeAlphaOnColor:color
to:opacity];
}
}
// 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];
[colors addObject:color];
}
*someColors = colors;
*someColors = (NSArray*)colors;
return stopsParams;
}
- (IJSVGColorList*)computedColorList
- (IJSVGColorList*)colorList
{
IJSVGColorList* sheet = [[[IJSVGColorList alloc] init] autorelease];
NSInteger num = self.gradient.numberOfColorStops;
@@ -123,21 +79,28 @@
[self.gradient getColor:&color
location:nil
atIndex:i];
[sheet addColor:color];
IJSVGColorType* type = [IJSVGColorType typeWithColor:color
flags:IJSVGColorTypeFlagStop];
[sheet addColor:type];
}
return sheet;
}
- (IJSVGColorList*)computedColorList
{
return _privateColorList;
}
- (CGGradientRef)CGGradient
{
// store it in the cache
if (CGGradient != nil) {
return CGGradient;
if (_CGGradient != nil) {
return _CGGradient;
}
// actually create the gradient
NSInteger num = self.gradient.numberOfColorStops;
CGFloat* locations = malloc(sizeof(CGFloat) * num);
CGFloat* locations = (CGFloat*)malloc(sizeof(CGFloat) * num);
CFMutableArrayRef colors = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)num,
&kCFTypeArrayCallBacks);
for (NSInteger i = 0; i < num; i++) {
@@ -145,16 +108,16 @@
[self.gradient getColor:&color
location:&locations[i]
atIndex:i];
if (_colorList != nil) {
color = [_colorList proposedColorForColor:color];
if (_privateColorList != nil) {
color = [_privateColorList proposedColorForColor:color];
}
CFArrayAppendValue(colors, color.CGColor);
}
CGGradientRef result = CGGradientCreateWithColors(self.gradient.colorSpace.CGColorSpace,
CGGradientRef result = CGGradientCreateWithColors(_gradient.colorSpace.CGColorSpace,
colors, locations);
CFRelease(colors);
free(locations);
return CGGradient = result;
return _CGGradient = result;
}
- (void)drawInContextRef:(CGContextRef)ctx
@@ -6,17 +6,18 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGNode.h"
#import "IJSVGPath.h"
#import <IJSVG/IJSVGNode.h>
#import <IJSVG/IJSVGPath.h>
#import <Foundation/Foundation.h>
@interface IJSVGGroup : IJSVGNode {
NSMutableArray* children;
@private
NSMutableArray<IJSVGNode*>* _children;
}
- (void)addChild:(id)child;
- (NSArray*)children;
- (void)purgeChildren;
@property (nonatomic, readonly) NSArray<IJSVGNode*>* children;
- (void)addChild:(IJSVGNode*)child;
@end
+31 -12
View File
@@ -12,21 +12,24 @@
- (void)dealloc
{
(void)([children release]), children = nil;
(void)([_children release]), _children = nil;
[super dealloc];
}
- (id)init
{
if ((self = [super init]) != nil) {
children = [[NSMutableArray alloc] init];
_children = [[NSMutableArray alloc] init];
}
return self;
}
- (void)prepareFromCopy
{
children = [[NSMutableArray alloc] init];
if(_children != nil) {
(void)[_children release], _children = nil;
}
_children = [[NSMutableArray alloc] init];
}
- (id)copyWithZone:(NSZone*)zone
@@ -34,7 +37,7 @@
IJSVGGroup* node = [super copyWithZone:zone];
[node prepareFromCopy];
for (IJSVGNode* childNode in self.children) {
for (IJSVGNode* childNode in _children) {
childNode = [[childNode copy] autorelease];
childNode.parentNode = node;
[node addChild:childNode];
@@ -42,25 +45,41 @@
return node;
}
- (void)purgeChildren
- (void)addChild:(IJSVGNode*)child
{
[children removeAllObjects];
if(child == nil || (child.parentNode == self && [_children containsObject:child])) {
return;
}
child.parentNode = self;
[_children addObject:child];
}
- (void)addChild:(id)child
- (void)removeChild:(IJSVGNode*)child
{
if (child != nil)
[children addObject:child];
if(child.parentNode == self) {
[child detach];
}
[_children removeObject:child];
}
- (NSArray*)children
- (CGRect)bounds
{
return children;
CGRect rect = CGRectZero;
for(IJSVGNode* node in self.children) {
rect = CGRectUnion(rect, node.bounds);
}
return rect;
}
- (NSArray<IJSVGNode*>*)children
{
return _children;
}
- (NSString*)description
{
return [NSString stringWithFormat:@"%@ - %@", [super description], self.children];
return [NSString stringWithFormat:@"%@ - %@",
[super description], self.children];
}
@end
@@ -6,21 +6,22 @@
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import "IJSVGNode.h"
#import <IJSVG/IJSVGNode.h>
#import <Foundation/Foundation.h>
@class IJSVGPath;
@interface IJSVGImage : IJSVGNode {
NSImage* image;
CGImageRef CGImage;
IJSVGPath* imagePath;
}
@property (nonatomic, readonly) CGSize intrinsicSize;
@property (nonatomic, readonly) CGRect intrinsicBounds;
@property (nonatomic, readonly) CGAffineTransform intrinsicTransform;
@property (nonatomic, retain) NSImage* image;
- (CGImageRef)CGImage;
- (void)drawInContextRef:(CGContextRef)context
path:(IJSVGPath*)path;
- (void)loadFromBase64EncodedString:(NSString*)encodedString;
- (void)loadFromString:(NSString*)encodedString;
- (void)loadFromURL:(NSURL*)aURL;
@end
+36 -47
View File
@@ -15,12 +15,11 @@
- (void)dealloc
{
(void)(CGImageRelease(CGImage)), CGImage = nil;
(void)([imagePath release]), imagePath = nil;
(void)([image release]), image = nil;
(void)([_image release]), _image = nil;
[super dealloc];
}
- (void)loadFromBase64EncodedString:(NSString*)encodedString
- (void)loadFromString:(NSString*)encodedString
{
if ([encodedString hasPrefix:@"data:"]) {
encodedString = [encodedString stringByReplacingOccurrencesOfString:@"\\s+"
@@ -28,8 +27,15 @@
options:NSRegularExpressionSearch
range:NSMakeRange(0, encodedString.length)];
}
NSURL* URL = [NSURL URLWithString:encodedString];
NSData* data = [NSData dataWithContentsOfURL:URL];
NSURL* url = [NSURL URLWithString:encodedString];
if(url != nil) {
[self loadFromURL:url];
}
}
- (void)loadFromURL:(NSURL*)aURL
{
NSData* data = [NSData dataWithContentsOfURL:aURL];
// no data, just ignore...invalid probably
if (data == nil) {
@@ -41,35 +47,24 @@
[self setImage:anImage];
}
- (IJSVGPath*)path
{
if (imagePath == nil) {
// lazy load the path as it might not be needed
imagePath = [[IJSVGPath alloc] init];
[imagePath.path appendBezierPathWithRect:NSMakeRect(0.f, 0.f, self.width.value, self.height.value)];
[imagePath close];
}
return imagePath;
}
- (void)setImage:(NSImage*)anImage
{
if (image != nil) {
(void)([image release]), image = nil;
if (_image != nil) {
(void)([_image release]), _image = nil;
}
image = [anImage retain];
_image = [anImage retain];
_intrinsicSize = (CGSize)_image.size;
if (CGImage != nil) {
CGImageRelease(CGImage);
CGImage = nil;
}
NSRect rect = NSMakeRect(0.f, 0.f, self.width.value, self.height.value);
CGImage = [image CGImageForProposedRect:&rect
context:nil
hints:nil];
NSRect rect = NSMakeRect(0.f, 0.f, _intrinsicSize.width, _intrinsicSize.height);
CGImage = [_image CGImageForProposedRect:&rect
context:nil
hints:nil];
// be sure to retain (some reason this is required in Xcode 8 beta 5?)
CGImageRetain(CGImage);
}
@@ -78,32 +73,26 @@
return CGImage;
}
- (void)drawInContextRef:(CGContextRef)context
path:(IJSVGPath*)path
- (CGRect)intrinsicBounds
{
// run the transforms
// draw the image
if (self.width.value == 0.f || self.height.value == 0.f) {
return;
}
CGRect rect = CGRectZero;
rect.size.width = _intrinsicSize.width;
rect.size.height = _intrinsicSize.height;
return rect;
}
// make sure path is set
if (path == nil) {
path = [self path];
}
- (CGAffineTransform)intrinsicTransform
{
CGFloat widthRatio = self.width.value / _intrinsicSize.width;
CGFloat heightRatio = self.height.value / _intrinsicSize.height;
return CGAffineTransformMakeScale(widthRatio, heightRatio);
}
CGRect rect = path.path.bounds;
CGRect bounds = CGRectMake(0.f, 0.f, rect.size.width, rect.size.height);
// save the state of the context
CGContextSaveGState(context);
{
// flip the coordinates
CGContextTranslateCTM(context, rect.origin.x, (rect.origin.y) + rect.size.height);
CGContextScaleCTM(context, 1.f, -1.f);
CGContextDrawImage(context, bounds, CGImage);
}
CGContextRestoreGState(context);
- (CGRect)bounds
{
return CGRectMake(0.f, 0.f,
self.width.value,
self.height.value);
}
@end
@@ -6,7 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGGradient.h"
#import <IJSVG/IJSVGGradient.h>
#import <Foundation/Foundation.h>
@interface IJSVGLinearGradient : IJSVGGradient
@@ -8,6 +8,7 @@
#import "IJSVGLinearGradient.h"
#import "IJSVGUtils.h"
#import "IJSVGParser.h"
@implementation IJSVGLinearGradient
@@ -15,20 +16,20 @@
gradient:(IJSVGLinearGradient*)aGradient
{
// just ask unit for the value
NSString* x1 = ([element attributeForName:@"x1"].stringValue ?: @"0");
NSString* x2 = ([element attributeForName:@"x2"].stringValue ?: @"100%");
NSString* y1 = ([element attributeForName:@"y1"].stringValue ?: @"0");
NSString* y2 = ([element attributeForName:@"y2"].stringValue ?: @"0");
NSString* x1 = ([element attributeForName:IJSVGAttributeX1].stringValue ?: @"0");
NSString* x2 = ([element attributeForName:IJSVGAttributeX2].stringValue ?: @"100%");
NSString* y1 = ([element attributeForName:IJSVGAttributeY1].stringValue ?: @"0");
NSString* y2 = ([element attributeForName:IJSVGAttributeY2].stringValue ?: @"0");
aGradient.x1 = [IJSVGGradientUnitLength unitWithString:x1 fromUnitType:aGradient.units];
aGradient.x2 = [IJSVGGradientUnitLength unitWithString:x2 fromUnitType:aGradient.units];
aGradient.y1 = [IJSVGGradientUnitLength unitWithString:y1 fromUnitType:aGradient.units];
aGradient.y2 = [IJSVGGradientUnitLength unitWithString:y2 fromUnitType:aGradient.units];
// compute the color stops and colours
NSArray* colors = nil;
CGFloat* stopsParams = [self.class computeColorStopsFromString:element
colors:&colors];
CGFloat* stopsParams = [self.class computeColorStops:aGradient
colors:&colors];
// create the gradient with the colours
NSGradient* grad = [[NSGradient alloc] initWithColors:colors
atLocations:stopsParams
@@ -50,7 +51,7 @@
CGAffineTransform selfTransform = IJSVGConcatTransforms(self.transforms);
CGRect boundingBox = inUserSpace ? viewBox : objectRect;
// make sure we apply the absolute position to
// transform us back into the correct space
if (inUserSpace == YES) {
@@ -64,7 +65,7 @@
gradientEndPoint = CGPointMake([self.x2 computeValue:width],
[self.y2 computeValue:height]);
// transform the context
CGContextConcatCTM(ctx, selfTransform);
+50 -15
View File
@@ -6,18 +6,29 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGStyle.h"
#import "IJSVGUnitLength.h"
#import <IJSVG/IJSVGStyle.h>
#import <IJSVG/IJSVGUnitLength.h>
#import <IJSVG/IJSVGViewBox.h>
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
@class IJSVGNode;
@class IJSVG;
@class IJSVGGroup;
@class IJSVGDef;
@class IJSVGGradient;
@class IJSVGGroup;
@class IJSVGPattern;
@class IJSVGTransform;
@class IJSVGRootNode;
@class IJSVGUnitRect;
typedef void (^IJSVGNodeWalkHandler)(IJSVGNode* node, BOOL* allowChildNodes, BOOL* stop);
typedef NS_OPTIONS(NSInteger, IJSVGNodeTraits) {
IJSVGNodeTraitNone = 0,
IJSVGNodeTraitStroked = 1 << 0,
IJSVGNodeTraitPaintable = 1 << 1
};
typedef NS_ENUM(NSInteger, IJSVGNodeType) {
IJSVGNodeTypeGroup,
@@ -43,7 +54,11 @@ typedef NS_ENUM(NSInteger, IJSVGNodeType) {
IJSVGNodeTypeTextSpan,
IJSVGNodeTypeStyle,
IJSVGNodeTypeSwitch,
IJSVGNodeTypeTitle,
IJSVGNodeTypeDesc,
IJSVGNodeTypeStop,
IJSVGNodeTypeNotFound,
IJSVGNodeTypeUnknown,
};
typedef NS_ENUM(NSInteger, IJSVGWindingRule) {
@@ -93,17 +108,30 @@ typedef NS_ENUM(NSInteger, IJSVGBlendMode) {
IJSVGBlendModeLuminosity = kCGBlendModeLuminosity
};
typedef NS_ENUM(NSInteger, IJSVGOverflowVisibility) {
IJSVGOverflowVisibilityHidden,
IJSVGOverflowVisibilityVisible
};
static CGFloat IJSVGInheritedFloatValue = -99.9999991;
@interface IJSVGNode : NSObject <NSCopying>
void IJSVGAssertPaintableObject(id object);
@property (nonatomic, assign) IJSVGNodeTraits traits;
@property (nonatomic, assign, readonly) CGRect bounds;
@property (nonatomic, retain) IJSVGUnitRect* viewBox;
@property (nonatomic, assign) IJSVGViewBoxAlignment viewBoxAlignment;
@property (nonatomic, assign) IJSVGViewBoxMeetOrSlice viewBoxMeetOrSlice;
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* desc;
@property (nonatomic, assign) IJSVGNodeType type;
@property (nonatomic, copy) NSString* name;
@property (nonatomic, copy) NSString* className;
@property (nonatomic, retain) NSArray* classNameList;
@property (nonatomic, copy) NSString* unicode;
@property (nonatomic, assign) BOOL shouldRender;
@property (nonatomic, assign) BOOL usesDefaultFillColor;
@property (nonatomic, retain) IJSVGUnitLength* x;
@property (nonatomic, retain) IJSVGUnitLength* y;
@property (nonatomic, retain) IJSVGUnitLength* width;
@@ -112,36 +140,43 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991;
@property (nonatomic, retain) IJSVGUnitLength* fillOpacity;
@property (nonatomic, retain) IJSVGUnitLength* strokeOpacity;
@property (nonatomic, retain) IJSVGUnitLength* strokeWidth;
@property (nonatomic, retain) NSColor* fillColor;
@property (nonatomic, retain) NSColor* strokeColor;
@property (nonatomic, retain) IJSVGUnitLength* offset;
@property (nonatomic, retain) IJSVGNode* fill;
@property (nonatomic, retain) IJSVGNode* stroke;
@property (nonatomic, copy) NSString* identifier;
@property (nonatomic, assign) IJSVGNode* parentNode;
@property (nonatomic, assign) IJSVGNode* intermediateParentNode;
@property (nonatomic, retain) IJSVGGroup* clipPath;
@property (nonatomic, retain) IJSVGGroup* mask;
@property (nonatomic, assign) IJSVGWindingRule windingRule;
@property (nonatomic, assign) IJSVGLineCapStyle lineCapStyle;
@property (nonatomic, assign) IJSVGLineJoinStyle lineJoinStyle;
@property (nonatomic, retain) NSArray<IJSVGTransform*>* transforms;
@property (nonatomic, retain) IJSVGDef* def;
@property (nonatomic, retain) IJSVGGradient* fillGradient;
@property (nonatomic, retain) IJSVGPattern* fillPattern;
@property (nonatomic, retain) IJSVGGradient* strokeGradient;
@property (nonatomic, retain) IJSVGPattern* strokePattern;
@property (nonatomic, assign) CGFloat* strokeDashArray;
@property (nonatomic, assign) NSInteger strokeDashArrayCount;
@property (nonatomic, readonly) NSArray<NSNumber*>* lineDashPattern;
@property (nonatomic, retain) IJSVGUnitLength* strokeDashOffset;
@property (nonatomic, retain) IJSVG* svg;
@property (nonatomic, assign) IJSVGUnitType contentUnits;
@property (nonatomic, assign) IJSVGUnitType units;
@property (nonatomic, assign) IJSVGBlendMode blendMode;
@property (nonatomic, assign) IJSVGOverflowVisibility overflowVisibility;
+ (void)walkNodeTree:(IJSVGNode*)node
handler:(IJSVGNodeWalkHandler)handler;
+ (IJSVGNodeType)typeForString:(NSString*)string
kind:(NSXMLNodeKind)kind;
- (void)applyPropertiesFromNode:(IJSVGNode*)node;
- (id)initWithDef:(BOOL)flag;
- (void)addDef:(IJSVGNode*)aDef;
- (IJSVGDef*)defForID:(NSString*)anID;
- (IJSVGUnitType)contentUnitsWithReferencingNodeBounds:(CGRect*)bounds;
- (IJSVGUnitType)contentUnitsWithReferencingNode:(IJSVGNode**)referencingNode;
- (instancetype)detach;
- (void)addTraits:(IJSVGNodeTraits)traits;
- (void)removeTraits:(IJSVGNodeTraits)traits;
- (BOOL)matchesTraits:(IJSVGNodeTraits)traits;
- (void)computeTraits;
@end
+298 -236
View File
@@ -6,212 +6,187 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGDef.h"
#import "IJSVGNode.h"
#import "IJSVGUtils.h"
#import <IJSVG/IJSVGDef.h>
#import <IJSVG/IJSVGNode.h>
#import <IJSVG/IJSVGGroup.h>
#import <IJSVG/IJSVGUtils.h>
@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;
@synthesize fill = _fill;
@synthesize stroke = _stroke;
- (void)dealloc
{
free(strokeDashArray);
(void)([x release]), x = nil;
(void)([y release]), y = nil;
(void)([width release]), width = nil;
(void)([height release]), height = nil;
(void)([opacity release]), opacity = nil;
(void)([fillOpacity release]), fillOpacity = nil;
(void)([strokeOpacity release]), strokeOpacity = nil;
(void)([strokeWidth release]), strokeWidth = nil;
(void)([strokeDashOffset release]), strokeDashOffset = nil;
(void)([unicode release]), unicode = nil;
(void)([fillGradient release]), fillGradient = nil;
(void)([strokeGradient release]), strokeGradient = nil;
(void)([strokePattern release]), strokePattern = nil;
(void)([transforms release]), transforms = nil;
(void)([fillColor release]), fillColor = nil;
(void)([strokeColor release]), strokeColor = nil;
(void)([identifier release]), identifier = nil;
(void)([def release]), def = nil;
(void)([name release]), name = nil;
(void)([className release]), className = nil;
(void)([classNameList release]), classNameList = nil;
(void)([fillPattern release]), fillPattern = nil;
(void)([clipPath release]), clipPath = nil;
(void)([svg release]), svg = nil;
(void)([mask release]), mask = nil;
(void)free(_strokeDashArray), _strokeDashArray = NULL;
(void)([_x release]), _x = nil;
(void)([_y release]), _y = nil;
(void)([_width release]), _width = nil;
(void)([_height release]), _height = nil;
(void)([_opacity release]), _opacity = nil;
(void)([_offset release]), _offset = nil;
(void)([_fillOpacity release]), _fillOpacity = nil;
(void)([_strokeOpacity release]), _strokeOpacity = nil;
(void)([_strokeWidth release]), _strokeWidth = nil;
(void)([_strokeDashOffset release]), _strokeDashOffset = nil;
(void)([_unicode release]), _unicode = nil;
(void)([_fill release]), _fill = nil;
(void)([_stroke release]), _stroke = nil;
(void)([_transforms release]), _transforms = nil;
(void)([_identifier release]), _identifier = nil;
(void)([_name release]), _name = nil;
(void)([_title release]), _title = nil;
(void)([_desc release]), _desc = nil;
(void)([_className release]), _className = nil;
(void)([_classNameList release]), _classNameList = nil;
(void)([_clipPath release]), _clipPath = nil;
(void)([_svg release]), _svg = nil;
(void)([_mask release]), _mask = nil;
(void)([_viewBox release]), _viewBox = nil;
[super dealloc];
}
+ (IJSVGNodeType)typeForString:(NSString*)string
kind:(NSXMLNodeKind)kind
{
string = [string lowercaseString];
if ([string isEqualToString:@"style"])
// possible fix for older os's that complain
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;
if ([string isEqualToString:@"switch"])
}
if (strcmp(name, "switch") == 0) {
return IJSVGNodeTypeSwitch;
if ([string isEqualToString:@"defs"])
}
if (strcmp(name, "defs") == 0) {
return IJSVGNodeTypeDef;
if ([string isEqualToString:@"g"])
}
if (strcmp(name, "g") == 0) {
return IJSVGNodeTypeGroup;
if ([string isEqualToString:@"path"])
}
if (strcmp(name, "path") == 0) {
return IJSVGNodeTypePath;
if ([string isEqualToString:@"polygon"])
}
if (strcmp(name, "polygon") == 0) {
return IJSVGNodeTypePolygon;
if ([string isEqualToString:@"polyline"])
}
if (strcmp(name, "polyline") == 0) {
return IJSVGNodeTypePolyline;
if ([string isEqualToString:@"rect"])
}
if (strcmp(name, "rect") == 0) {
return IJSVGNodeTypeRect;
if ([string isEqualToString:@"line"])
}
if (strcmp(name, "line") == 0) {
return IJSVGNodeTypeLine;
if ([string isEqualToString:@"circle"])
}
if (strcmp(name, "circle") == 0) {
return IJSVGNodeTypeCircle;
if ([string isEqualToString:@"ellipse"])
}
if (strcmp(name, "ellipse") == 0) {
return IJSVGNodeTypeEllipse;
if ([string isEqualToString:@"use"])
}
if (strcmp(name, "use") == 0) {
return IJSVGNodeTypeUse;
if ([string isEqualToString:@"lineargradient"])
}
if (strcmp(name, "lineargradient") == 0) {
return IJSVGNodeTypeLinearGradient;
if ([string isEqualToString:@"radialgradient"])
}
if (strcmp(name, "radialgradient") == 0) {
return IJSVGNodeTypeRadialGradient;
if ([string isEqualToString:@"glyph"])
}
if(strcmp(name, "stop") == 0) {
return IJSVGNodeTypeStop;
}
if (strcmp(name, "glyph") == 0) {
return IJSVGNodeTypeGlyph;
if ([string isEqualToString:@"font"])
}
if (strcmp(name, "font") == 0) {
return IJSVGNodeTypeFont;
if ([string isEqualToString:@"clippath"])
}
if (strcmp(name, "clippath") == 0) {
return IJSVGNodeTypeClipPath;
if ([string isEqualToString:@"mask"])
}
if (strcmp(name, "mask") == 0) {
return IJSVGNodeTypeMask;
if ([string isEqualToString:@"image"])
}
if (strcmp(name, "image") == 0) {
return IJSVGNodeTypeImage;
if ([string isEqualToString:@"pattern"])
}
if (strcmp(name, "pattern") == 0) {
return IJSVGNodeTypePattern;
if ([string isEqualToString:@"svg"])
}
if (strcmp(name, "svg") == 0) {
return IJSVGNodeTypeSVG;
if ([string isEqualToString:@"text"])
}
if (strcmp(name, "text") == 0) {
return IJSVGNodeTypeText;
if ([string isEqualToString:@"tspan"] || kind == NSXMLTextKind) {
}
if (strcmp(name, "tspan") == 0 || kind == NSXMLTextKind) {
return IJSVGNodeTypeTextSpan;
}
// are we commong HTML? - if so just treat as a group
if (IJSVGIsCommonHTMLElementName(string) == YES) {
return IJSVGNodeTypeGroup;
if(strcmp(name, "title") == 0) {
return IJSVGNodeTypeTitle;
}
if(strcmp(name, "desc") == 0) {
return IJSVGNodeTypeDesc;
}
return IJSVGNodeTypeUnknown;
}
return IJSVGNodeTypeNotFound;
+ (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.children) {
[self _walkNodeTree:childNode
handler:handler
allowChildNodes:allowChildNodes
stop:stop];
if(*stop == YES) {
return;
}
}
}
- (id)init
{
if ((self = [self initWithDef:YES]) != nil) {
self.opacity = [IJSVGUnitLength unitWithFloat:1];
}
return self;
}
- (void)applyPropertiesFromNode:(IJSVGNode*)node
{
self.name = node.name;
self.type = node.type;
self.unicode = node.unicode;
self.className = node.className;
self.classNameList = node.classNameList;
self.x = node.x;
self.y = node.y;
self.width = node.width;
self.height = node.height;
self.fillGradient = node.fillGradient;
self.fillPattern = node.fillPattern;
self.strokeGradient = node.strokeGradient;
self.strokePattern = node.strokePattern;
self.fillColor = node.fillColor;
self.strokeColor = node.strokeColor;
self.clipPath = node.clipPath;
self.units = node.units;
self.contentUnits = node.contentUnits;
self.opacity = node.opacity;
self.strokeWidth = node.strokeWidth;
self.fillOpacity = node.fillOpacity;
self.strokeOpacity = node.strokeOpacity;
self.identifier = node.identifier;
self.usesDefaultFillColor = node.usesDefaultFillColor;
self.transforms = node.transforms;
self.def = node.def;
self.windingRule = node.windingRule;
self.lineCapStyle = node.lineCapStyle;
self.lineJoinStyle = node.lineJoinStyle;
self.parentNode = node.parentNode;
self.shouldRender = node.shouldRender;
self.blendMode = node.blendMode;
// dash array needs physical memory copied
CGFloat* nStrokeDashArray = (CGFloat*)malloc(node.strokeDashArrayCount * sizeof(CGFloat));
memcpy(self.strokeDashArray, nStrokeDashArray, node.strokeDashArrayCount * sizeof(CGFloat));
self.strokeDashArray = nStrokeDashArray;
self.strokeDashArrayCount = node.strokeDashArrayCount;
self.strokeDashOffset = node.strokeDashOffset;
}
- (id)copyWithZone:(NSZone*)zone
{
IJSVGNode* node = [self.class allocWithZone:zone];
[node applyPropertiesFromNode:self];
return node;
}
- (id)initWithDef:(BOOL)flag
{
if ((self = [super init]) != nil) {
self.opacity = [IJSVGUnitLength unitWithFloat:0.f];
self.opacity = [IJSVGUnitLength unitWithFloat:1.f];
self.fillOpacity = [IJSVGUnitLength unitWithFloat:1.f];
self.fillOpacity.inherit = YES;
@@ -228,159 +203,246 @@
self.lineCapStyle = IJSVGLineCapStyleInherit;
self.lineJoinStyle = IJSVGLineJoinStyleInherit;
self.units = IJSVGUnitInherit;
self.contentUnits = IJSVGUnitInherit;
self.blendMode = IJSVGBlendModeNormal;
if (flag) {
def = [[IJSVGDef alloc] init];
}
self.overflowVisibility = IJSVGOverflowVisibilityVisible;
}
return self;
}
- (IJSVGDef*)defForID:(NSString*)anID
- (void)applyPropertiesFromNode:(IJSVGNode*)node
{
IJSVGDef* aDef = nil;
if ((aDef = [def defForID:anID]) != nil) {
return aDef;
}
if (parentNode != nil) {
return [parentNode defForID:anID];
}
return nil;
self.title = node.title;
self.desc = node.desc;
self.name = node.name;
self.type = node.type;
self.unicode = node.unicode;
self.className = node.className;
self.classNameList = node.classNameList;
self.viewBox = node.viewBox;
self.viewBoxAlignment = node.viewBoxAlignment;
self.viewBoxMeetOrSlice = node.viewBoxMeetOrSlice;
self.x = node.x;
self.y = node.y;
self.width = node.width;
self.height = node.height;
self.fill = node.fill;
self.stroke = node.stroke;
self.clipPath = node.clipPath;
self.units = node.units;
self.contentUnits = node.contentUnits;
self.opacity = node.opacity;
self.strokeWidth = node.strokeWidth;
self.fillOpacity = node.fillOpacity;
self.strokeOpacity = node.strokeOpacity;
self.identifier = node.identifier;
self.transforms = node.transforms;
self.windingRule = node.windingRule;
self.lineCapStyle = node.lineCapStyle;
self.lineJoinStyle = node.lineJoinStyle;
self.parentNode = node.parentNode;
self.shouldRender = node.shouldRender;
self.blendMode = node.blendMode;
self.overflowVisibility = node.overflowVisibility;
// dash array needs physical memory copied
CGFloat* nStrokeDashArray = (CGFloat*)malloc(node.strokeDashArrayCount * sizeof(CGFloat));
memcpy(self.strokeDashArray, nStrokeDashArray, node.strokeDashArrayCount * sizeof(CGFloat));
self.strokeDashArray = nStrokeDashArray;
self.strokeDashArrayCount = node.strokeDashArrayCount;
self.strokeDashOffset = node.strokeDashOffset;
}
- (void)addDef:(IJSVGNode*)aDef
- (id)copyWithZone:(NSZone*)zone
{
[def addDef:aDef];
IJSVGNode* node = [self.class allocWithZone:zone];
[node applyPropertiesFromNode:self];
return node;
}
- (void)setFill:(IJSVGNode*)fill
{
NSAssert([fill matchesTraits:IJSVGNodeTraitPaintable] || fill == nil, @"Fill must a paintable node.");
(void)[_fill release], _fill = nil;
_fill = [fill retain];
}
- (void)setStroke:(IJSVGNode*)stroke
{
NSAssert([stroke matchesTraits:IJSVGNodeTraitPaintable]|| stroke == nil, @"Stroke must be a paintable node.");
(void)[_stroke release], _stroke = nil;
_stroke = [stroke retain];
}
// winding rule can inherit..
- (IJSVGWindingRule)windingRule
{
if (windingRule == IJSVGWindingRuleInherit && parentNode != nil) {
return parentNode.windingRule;
if (_windingRule == IJSVGWindingRuleInherit && _parentNode != nil) {
return _parentNode.windingRule;
}
return windingRule;
return _windingRule;
}
- (IJSVGLineCapStyle)lineCapStyle
{
if (lineCapStyle == IJSVGLineCapStyleInherit) {
if (parentNode != nil) {
return parentNode.lineCapStyle;
if (_lineCapStyle == IJSVGLineCapStyleInherit) {
if (_parentNode != nil) {
return _parentNode.lineCapStyle;
}
}
return lineCapStyle;
return _lineCapStyle;
}
- (IJSVGLineJoinStyle)lineJoinStyle
{
if (lineJoinStyle == IJSVGLineJoinStyleInherit) {
if (parentNode != nil) {
return parentNode.lineJoinStyle;
if (_lineJoinStyle == IJSVGLineJoinStyleInherit) {
if (_parentNode != nil) {
return _parentNode.lineJoinStyle;
}
}
return lineJoinStyle;
return _lineJoinStyle;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGUnitLength*)opacity
{
if (opacity.inherit && parentNode != nil) {
return parentNode.opacity;
if (_opacity.inherit && _parentNode != nil) {
return _parentNode.opacity;
}
return opacity;
return _opacity;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGUnitLength*)fillOpacity
{
if (fillOpacity.inherit && parentNode != nil) {
return parentNode.fillOpacity;
if (_fillOpacity.inherit && _parentNode != nil) {
return _parentNode.fillOpacity;
}
return fillOpacity;
return _fillOpacity;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGUnitLength*)strokeWidth
{
if (strokeWidth.inherit && parentNode != nil) {
return parentNode.strokeWidth;
if (_strokeWidth.inherit && _parentNode != nil) {
return _parentNode.strokeWidth;
}
return strokeWidth;
return _strokeWidth;
}
- (NSArray<NSNumber*>*)lineDashPattern
{
NSMutableArray* arr = [[[NSMutableArray alloc] initWithCapacity:self.strokeDashArrayCount] autorelease];
for (NSInteger i = 0; i < self.strokeDashArrayCount; i++) {
[arr addObject:@((CGFloat)self.strokeDashArray[i])];
}
return arr;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (NSColor*)strokeColor
- (IJSVGNode*)stroke
{
if (strokeColor != nil)
return strokeColor;
if (strokeColor == nil && parentNode != nil)
return parentNode.strokeColor;
return nil;
if (_stroke == nil && _parentNode != nil) {
return _parentNode.stroke;
}
return _stroke;
}
- (IJSVGUnitLength*)strokeOpacity
{
if (strokeOpacity.inherit && parentNode != nil) {
return parentNode.strokeOpacity;
if (_strokeOpacity.inherit && _parentNode != nil) {
return _parentNode.strokeOpacity;
}
return strokeOpacity;
return _strokeOpacity;
}
// even though the spec explicity states fill color
// must be on the path, it can also be on the
- (NSColor*)fillColor
- (IJSVGNode*)fill
{
if (fillColor == nil && parentNode != nil) {
return parentNode.fillColor;
if (_fill == nil && _parentNode != nil) {
return _parentNode.fill;
}
return fillColor;
return _fill;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGGradient*)fillGradient
- (IJSVGUnitType)units
{
if (fillGradient == nil && parentNode != nil) {
return parentNode.fillGradient;
if(_units == IJSVGUnitInherit && _parentNode != nil) {
return _parentNode.units;
}
return fillGradient;
return _units;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGPattern*)fillPattern
- (IJSVGUnitType)contentUnits
{
if (fillPattern == nil && parentNode != nil) {
return parentNode.fillPattern;
if(_contentUnits == IJSVGUnitInherit && _parentNode != nil) {
return _parentNode.contentUnits;
}
return fillPattern;
return _contentUnits;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGGradient*)strokeGradient
- (IJSVGUnitType)contentUnitsWithReferencingNodeBounds:(CGRect*)bounds
{
if (strokeGradient == nil && parentNode != nil) {
return parentNode.strokeGradient;
}
return strokeGradient;
IJSVGNode* node = nil;
IJSVGUnitType type = [self contentUnitsWithReferencingNode:&node];
*bounds = node.parentNode.bounds;
return type;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGPattern*)strokePattern
- (IJSVGUnitType)contentUnitsWithReferencingNode:(IJSVGNode**)referencingNode
{
if (strokePattern == nil && parentNode != nil) {
return parentNode.strokePattern;
if(_contentUnits == IJSVGUnitInherit && _parentNode != nil) {
return [_parentNode contentUnitsWithReferencingNode:referencingNode];
}
return strokePattern;
*referencingNode = self;
return _contentUnits;
}
- (void)addTraits:(IJSVGNodeTraits)traits
{
_traits |= traits;
}
- (void)removeTraits:(IJSVGNodeTraits)traits
{
_traits = _traits & ~traits;
}
- (BOOL)matchesTraits:(IJSVGNodeTraits)traits
{
return (_traits & traits) == traits;
}
- (void)computeTraits
{
// by default this does nothing
}
- (instancetype)detach
{
self.parentNode = nil;
return self;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ %@ %@",self.name,self.classNameList,self.identifier];
}
@end
@@ -6,8 +6,8 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGBezierPathAdditions.h"
#import "IJSVGNode.h"
#import <IJSVG/IJSVGNode.h>
#import <IJSVG/IJSVGColorNode.h>
#import <Foundation/Foundation.h>
@class IJSVGGroup;
@@ -23,15 +23,13 @@ typedef NS_ENUM(NSInteger, IJSVGPrimitivePathType) {
};
@interface IJSVGPath : IJSVGNode {
NSBezierPath* path;
CGPoint lastControlPoint;
}
@property (nonatomic, assign) IJSVGPrimitivePathType primitiveType;
@property (nonatomic, retain) NSBezierPath* path;
@property (nonatomic, assign) CGMutablePathRef path;
@property (nonatomic, assign) CGPoint lastControlPoint;
@property (nonatomic, readonly) CGPathRef CGPath;
@property (nonatomic, readonly) CGRect controlPointBoundingBox;
@property (nonatomic, readonly) CGRect pathBoundingBox;
- (void)close;
- (NSPoint)currentPoint;
+46 -26
View File
@@ -6,23 +6,15 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGGroup.h"
#import "IJSVGPath.h"
#import <IJSVG/IJSVGPath.h>
@implementation IJSVGPath
@synthesize path = _path;
@synthesize lastControlPoint;
@synthesize CGPath = _CGPath;
@synthesize primitiveType = _primitiveType;
- (void)dealloc
{
if (_CGPath != nil) {
CGPathRelease(_CGPath);
_CGPath = nil;
if(_path != NULL) {
(void)CGPathRelease(_path), _path = NULL;
}
((void)[_path release]), _path = nil;
[super dealloc];
}
@@ -30,42 +22,70 @@
{
if ((self = [super init]) != nil) {
_primitiveType = kIJSVGPrimitivePathTypePath;
_path = NSBezierPath.bezierPath.retain;
_path = CGPathCreateMutable();
}
return self;
}
- (CGRect)bounds
{
return self.pathBoundingBox;
}
- (id)copyWithZone:(NSZone*)zone
{
IJSVGPath* node = [super copyWithZone:zone];
node.path = [self.path.copy autorelease];
node.path = _path;
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
{
return _path.currentPoint;
return CGPathGetCurrentPoint(_path);
}
- (void)close
{
[_path closePath];
CGPathCloseSubpath(_path);
}
- (void)invlidateCGPath
{
if (_CGPath != nil) {
CGPathRelease(_CGPath);
}
_CGPath = nil;
}
#pragma mark Traits
- (CGPathRef)CGPath
- (void)computeTraits
{
if (_CGPath == nil) {
_CGPath = [_path newCGPathRef:NO];
if(self.stroke != nil) {
// by default we can just add this on
[self addTraits:IJSVGNodeTraitStroked];
// if we detect the stroke was a color, we need to check its alpha
// component to then remove the trait if its 0.f
if([self.stroke isKindOfClass:IJSVGColorNode.class] == YES) {
IJSVGColorNode* strokeColor = (IJSVGColorNode*)self.stroke;
if(strokeColor.color.alphaComponent == 0.f) {
[self removeTraits:IJSVGNodeTraitStroked];
}
}
}
return _CGPath;
}
@end
@@ -6,11 +6,12 @@
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import "IJSVGGroup.h"
#import "IJSVGImage.h"
#import <IJSVG/IJSVGImage.h>
#import <IJSVG/IJSVGGroup.h>
#import <Foundation/Foundation.h>
@interface IJSVGPattern : IJSVGGroup {
}
@end
@@ -7,7 +7,19 @@
//
#import "IJSVGPattern.h"
#import "IJSVGUnitRect.h"
@implementation IJSVGPattern
- (instancetype)init
{
if((self = [super init]) != nil) {
self.viewBox = nil;
self.viewBoxAlignment = IJSVGViewBoxAlignmentXMidYMid;
self.viewBoxMeetOrSlice = IJSVGViewBoxMeetOrSliceMeet;
}
return self;
}
@end
@@ -6,7 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGGradient.h"
#import <IJSVG/IJSVGGradient.h>
#import <Foundation/Foundation.h>
@interface IJSVGRadialGradient : IJSVGGradient
@@ -15,7 +15,8 @@
@property (nonatomic, retain) IJSVGUnitLength* cy;
@property (nonatomic, retain) IJSVGUnitLength* fx;
@property (nonatomic, retain) IJSVGUnitLength* fy;
@property (nonatomic, retain) IJSVGUnitLength* radius;
@property (nonatomic, retain) IJSVGUnitLength* fr;
@property (nonatomic, retain) IJSVGUnitLength* r;
+ (NSGradient*)parseGradient:(NSXMLElement*)element
gradient:(IJSVGRadialGradient*)gradient;
@@ -0,0 +1,185 @@
//
// IJSVGRadialGradient.m
// IJSVGExample
//
// Created by Curtis Hard on 03/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGRadialGradient.h>
#import <IJSVG/IJSVGParser.h>
@implementation IJSVGRadialGradient
- (void)dealloc
{
(void)([_cx release]), _cx = nil;
(void)([_cy release]), _cy = nil;
(void)([_fx release]), _fx = nil;
(void)([_fy release]), _fy = nil;
(void)([_fr release]), _fr = nil;
(void)([_r release]), _r = nil;
[super dealloc];
}
- (id)copyWithZone:(NSZone*)zone
{
IJSVGRadialGradient* grad = [super copyWithZone:zone];
grad.fx = _fx;
grad.fy = _fy;
grad.fr = _fr;
grad.cx = _cx;
grad.cy = _cy;
grad.r = _r;
return grad;
}
+ (NSGradient*)parseGradient:(NSXMLElement*)element
gradient:(IJSVGRadialGradient*)gradient
{
// cx defaults to 50% if not specified
NSDictionary* kv = @{
IJSVGAttributeCX : @"cx",
IJSVGAttributeCY : @"cy",
IJSVGAttributeR : @"r" };
for (NSString* key in kv.allKeys) {
NSString* str = [element attributeForName:key].stringValue;
IJSVGUnitLength* unit = nil;
if (str != nil) {
unit = [IJSVGUnitLength unitWithString:str
fromUnitType:gradient.units];
} else {
// spec says to say 50% for missing property default
unit = [IJSVGUnitLength unitWithPercentageFloat:.5f];
}
[gradient setValue:unit
forKey:kv[key]];
}
// fr
NSString* fr = [element attributeForName:IJSVGAttributeFR].stringValue;
if(fr != nil) {
gradient.fr = [IJSVGUnitLength unitWithString:fr
fromUnitType:gradient.units];
} else {
gradient.fr = [IJSVGUnitLength unitWithPercentageFloat:0.f];
}
// fx and fy are the same unless specified otherwise
gradient.fx = gradient.cx;
gradient.fy = gradient.cy;
// needs fixing
NSString* fx = [element attributeForName:IJSVGAttributeFX].stringValue;
if (fx != nil) {
gradient.fx = [IJSVGUnitLength unitWithString:fx
fromUnitType:gradient.units];
}
NSString* fy = [element attributeForName:IJSVGAttributeFY].stringValue;
if (fy != nil) {
gradient.fy = [IJSVGUnitLength unitWithString:fy
fromUnitType:gradient.units];
}
if (gradient.gradient != nil) {
return nil;
}
NSArray* colors = nil;
CGFloat* colorStops = [self.class computeColorStops:gradient
colors:&colors];
NSGradient* ret = [[[NSGradient alloc] initWithColors:colors
atLocations:colorStops
colorSpace:IJSVGColor.defaultColorSpace] autorelease];
free(colorStops);
return ret;
}
- (void)drawInContextRef:(CGContextRef)ctx
objectRect:(NSRect)objectRect
absoluteTransform:(CGAffineTransform)absoluteTransform
viewPort:(CGRect)viewBox
{
CGContextSaveGState(ctx);
BOOL inUserSpace = self.units == IJSVGUnitUserSpaceOnUse;
CGFloat radius = 0.f;
CGPoint startPoint = CGPointZero;
CGPoint gradientStartPoint = CGPointZero;
CGPoint gradientEndPoint = CGPointZero;
// transforms
CGAffineTransform selfTransform = IJSVGConcatTransforms(self.transforms);
CGRect boundingBox = inUserSpace ? viewBox : objectRect;
// compute size based on percentages
CGFloat width = CGRectGetWidth(boundingBox);
CGFloat height = CGRectGetHeight(boundingBox);
CGFloat cx = [_cx computeValue:width];
CGFloat cy = [_cy computeValue:height];
startPoint = CGPointMake(cx, cy);
CGFloat val = MIN(width, height);
radius = [_r computeValue:val];
CGFloat focalRadius = [_fr computeValue:val];
CGFloat fx = [_fx computeValue:width];
CGFloat fy = [_fy computeValue:height];
gradientEndPoint = CGPointMake(fx, fy);
gradientStartPoint = startPoint;
// transform if width or height is not equal - this can only
// be done if we are using objectBoundingBox
if(inUserSpace == YES) {
CGFloat rad = 2.f * radius;
CGRect rect = CGRectMake(startPoint.x, startPoint.y, rad, rad);
rect = CGRectApplyAffineTransform(rect, selfTransform);
rect = CGRectApplyAffineTransform(rect, absoluteTransform);
radius = CGRectGetHeight(rect) / 2.f;
CGContextConcatCTM(ctx, absoluteTransform);
} else if(width != height) {
CGAffineTransform transform = CGAffineTransformIdentity;
CGAffineTransform invert = CGAffineTransformIdentity;
CGPoint invPoint = CGPointZero;
CGFloat* radiusScale;
if(width > height) {
transform = CGAffineTransformMakeScale(1.f, height / width);
radiusScale = &invPoint.y;
} else {
transform = CGAffineTransformMakeScale(width / height, 1.f);
radiusScale = &invPoint.x;
}
invert = CGAffineTransformInvert(transform);
invPoint.x = invert.a;
invPoint.y = invert.d;
gradientStartPoint.x *= invPoint.x;
gradientStartPoint.y *= invPoint.y;
gradientEndPoint.x *= invPoint.x;
gradientEndPoint.y *= invPoint.y;
radius *= *radiusScale;
focalRadius *= *radiusScale;
selfTransform = CGAffineTransformConcat(transform, selfTransform);
}
// transform the context
CGContextConcatCTM(ctx, selfTransform);
// draw the gradient
CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
CGContextDrawRadialGradient(ctx, self.CGGradient,
gradientEndPoint, focalRadius,
gradientStartPoint,
radius, options);
CGContextRestoreGState(ctx);
//#ifdef IJSVG_DEBUG_GRADIENTS
// [self _debugStart:gradientStartPoint
// end:gradientEndPoint
// context:ctx];
//#endif
}
@end
@@ -0,0 +1,18 @@
//
// IJSVGRootNode.h
// IJSVG
//
// Created by Curtis Hard on 28/03/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVG.h>
#import <IJSVG/IJSVGGroup.h>
#import <IJSVG/IJSVGUnitSize.h>
@interface IJSVGRootNode : IJSVGGroup
@property (nonatomic, retain) IJSVGUnitSize* intrinsicSize;
@property (nonatomic, readonly) CGRect bounds;
@end
@@ -0,0 +1,33 @@
//
// IJSVGRootNode.m
// IJSVG
//
// Created by Curtis Hard on 28/03/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import "IJSVGRootNode.h"
@implementation IJSVGRootNode
- (void)dealloc
{
(void)[_intrinsicSize release], _intrinsicSize = nil;
[super dealloc];
}
- (instancetype)init
{
if((self = [super init]) != nil) {
self.viewBoxAlignment = IJSVGViewBoxAlignmentXMidYMid;
self.viewBoxMeetOrSlice = IJSVGViewBoxMeetOrSliceMeet;
}
return self;
}
- (CGRect)bounds
{
return [self.viewBox computeValue:CGSizeZero];
}
@end
@@ -6,11 +6,9 @@
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import "IJSVGGroup.h"
#import <IJSVG/IJSVGGroup.h>
@interface IJSVGText : IJSVGGroup {
NSString* text;
}
@property (nonatomic, copy) NSString* text;
@@ -10,18 +10,16 @@
@implementation IJSVGText
@synthesize text;
- (void)dealloc
{
(void)([text release]), text = nil;
(void)([_text release]), _text = nil;
[super dealloc];
}
- (IJSVGText*)copyWithZone:(NSZone*)zone
{
IJSVGText* node = [super copyWithZone:zone];
node.text = self.text;
node.text = _text;
return node;
}
@@ -6,108 +6,106 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGColor.h"
#import "IJSVGCommand.h"
#import "IJSVGDef.h"
#import "IJSVGError.h"
#import "IJSVGForeignObject.h"
#import "IJSVGGroup.h"
#import "IJSVGImage.h"
#import "IJSVGLinearGradient.h"
#import "IJSVGPath.h"
#import "IJSVGPattern.h"
#import "IJSVGRadialGradient.h"
#import "IJSVGStyleSheet.h"
#import "IJSVGText.h"
#import "IJSVGTransform.h"
#import "IJSVGUtils.h"
#import <IJSVG/IJSVGColor.h>
#import <IJSVG/IJSVGCommand.h>
#import <IJSVG/IJSVGDef.h>
#import <IJSVG/IJSVGError.h>
#import <IJSVG/IJSVGForeignObject.h>
#import <IJSVG/IJSVGGroup.h>
#import <IJSVG/IJSVGColorNode.h>
#import <IJSVG/IJSVGImage.h>
#import <IJSVG/IJSVGLinearGradient.h>
#import <IJSVG/IJSVGPath.h>
#import <IJSVG/IJSVGPattern.h>
#import <IJSVG/IJSVGRadialGradient.h>
#import <IJSVG/IJSVGStyleSheet.h>
#import <IJSVG/IJSVGText.h>
#import <IJSVG/IJSVGTransform.h>
#import <IJSVG/IJSVGUnitRect.h>
#import <IJSVG/IJSVGUtils.h>
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
static NSString const* IJSVGAttributeViewBox = @"viewBox";
static NSString const* IJSVGAttributeID = @"id";
static NSString const* IJSVGAttributeClass = @"class";
static NSString const* IJSVGAttributeX = @"x";
static NSString const* IJSVGAttributeY = @"y";
static NSString const* IJSVGAttributeWidth = @"width";
static NSString const* IJSVGAttributeHeight = @"height";
static NSString const* IJSVGAttributeOpacity = @"opacity";
static NSString const* IJSVGAttributeStrokeOpacity = @"stroke-opacity";
static NSString const* IJSVGAttributeStrokeWidth = @"stroke-width";
static NSString const* IJSVGAttributeStrokeDashOffset = @"stroke-dashoffset";
static NSString const* IJSVGAttributeFillOpacity = @"fill-opacity";
static NSString const* IJSVGAttributeClipPath = @"clip-path";
static NSString const* IJSVGAttributeMask = @"mask";
static NSString const* IJSVGAttributeGradientUnits = @"gradientUnits";
static NSString const* IJSVGAttributeMaskUnits = @"maskUnits";
static NSString const* IJSVGAttributeMaskContentUnits = @"maskContentUnits";
static NSString const* IJSVGAttributeTransform = @"transform";
static NSString const* IJSVGAttributeGradientTransform = @"gradientTransform";
static NSString const* IJSVGAttributeUnicode = @"unicode";
static NSString const* IJSVGAttributeStrokeLineCap = @"stroke-linecap";
static NSString const* IJSVGAttributeLineJoin = @"stroke-linejoin";
static NSString const* IJSVGAttributeStroke = @"stroke";
static NSString const* IJSVGAttributeStrokeDashArray = @"stroke-dasharray";
static NSString const* IJSVGAttributeFill = @"fill";
static NSString const* IJSVGAttributeFillRule = @"fill-rule";
static NSString const* IJSVGAttributeBlendMode = @"mix-blend-mode";
static NSString const* IJSVGAttributeDisplay = @"display";
static NSString const* IJSVGAttributeStyle = @"style";
static NSString const* IJSVGAttributeD = @"d";
static NSString const* IJSVGAttributeXLink = @"xlink:href";
static NSString const* IJSVGAttributeX1 = @"x1";
static NSString const* IJSVGAttributeX2 = @"x2";
static NSString const* IJSVGAttributeY1 = @"y1";
static NSString const* IJSVGAttributeY2 = @"y2";
static NSString const* IJSVGAttributeRX = @"rx";
static NSString const* IJSVGAttributeRY = @"ry";
static NSString const* IJSVGAttributeCX = @"cx";
static NSString const* IJSVGAttributeCY = @"cy";
static NSString const* IJSVGAttributeR = @"r";
static NSString const* IJSVGAttributePoints = @"points";
extern NSString* const IJSVGAttributeViewBox;
extern NSString* const IJSVGAttributePreserveAspectRatio;
extern NSString* const IJSVGAttributeID;
extern NSString* const IJSVGAttributeClass;
extern NSString* const IJSVGAttributeX;
extern NSString* const IJSVGAttributeY;
extern NSString* const IJSVGAttributeWidth;
extern NSString* const IJSVGAttributeHeight;
extern NSString* const IJSVGAttributeOpacity;
extern NSString* const IJSVGAttributeStrokeOpacity;
extern NSString* const IJSVGAttributeStrokeWidth;
extern NSString* const IJSVGAttributeStrokeDashOffset;
extern NSString* const IJSVGAttributeFillOpacity;
extern NSString* const IJSVGAttributeClipPath;
extern NSString* const IJSVGAttributeMask;
extern NSString* const IJSVGAttributeGradientUnits;
extern NSString* const IJSVGAttributePatternContentUnits;
extern NSString* const IJSVGAttributeMaskUnits;
extern NSString* const IJSVGAttributeMaskContentUnits;
extern NSString* const IJSVGAttributeTransform;
extern NSString* const IJSVGAttributeGradientTransform;
extern NSString* const IJSVGAttributeUnicode;
extern NSString* const IJSVGAttributeStrokeLineCap;
extern NSString* const IJSVGAttributeLineJoin;
extern NSString* const IJSVGAttributeStroke;
extern NSString* const IJSVGAttributeStrokeDashArray;
extern NSString* const IJSVGAttributeFill;
extern NSString* const IJSVGAttributeFillRule;
extern NSString* const IJSVGAttributeBlendMode;
extern NSString* const IJSVGAttributeDisplay;
extern NSString* const IJSVGAttributeStyle;
extern NSString* const IJSVGAttributeD;
extern NSString* const IJSVGAttributeXLink;
extern NSString* const IJSVGAttributeX1;
extern NSString* const IJSVGAttributeX2;
extern NSString* const IJSVGAttributeY1;
extern NSString* const IJSVGAttributeY2;
extern NSString* const IJSVGAttributeRX;
extern NSString* const IJSVGAttributeRY;
extern NSString* const IJSVGAttributeCX;
extern NSString* const IJSVGAttributeCY;
extern NSString* const IJSVGAttributeR;
extern NSString* const IJSVGAttributeFR;
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;
@protocol IJSVGParserDelegate <NSObject>
@optional
- (BOOL)svgParser:(IJSVGParser*)svg
shouldHandleForeignObject:(IJSVGForeignObject*)foreignObject;
- (void)svgParser:(IJSVGParser*)svg
handleForeignObject:(IJSVGForeignObject*)foreignObject
document:(NSXMLDocument*)document;
- (void)svgParser:(IJSVGParser*)svg
foundSubSVG:(IJSVG*)subSVG
withSVGString:(NSString*)string;
@end
@interface IJSVGParser : IJSVGGroup {
NSRect viewBox;
NSSize proposedViewSize;
@interface IJSVGParser : NSObject {
@private
id<IJSVGParserDelegate> _delegate;
NSXMLDocument* _document;
NSMutableArray* _glyphs;
IJSVGStyleSheet* _styleSheet;
NSMutableArray* _parsedNodes;
NSMutableDictionary* _defNodes;
NSMutableDictionary* _baseDefNodes;
NSMutableArray<IJSVG*>* _svgs;
struct {
unsigned int shouldHandleForeignObject : 1;
unsigned int handleForeignObject : 1;
unsigned int handleSubSVG : 1;
} _respondsTo;
IJSVGPathDataStream* _commandDataStream;
IJSVGStyleSheet* _styleSheet;
NSMapTable<IJSVGNode*, NSMutableDictionary<NSString*, NSXMLElement*>*>* _detachedElements;
}
@property (nonatomic, readonly) NSRect viewBox;
@property (nonatomic, readonly) NSSize proposedViewSize;
@property (nonatomic, retain, readonly) IJSVGRootNode* rootNode;
+ (BOOL)isDataSVG:(NSData*)data;
@@ -124,9 +122,5 @@ static NSString const* IJSVGAttributePoints = @"points";
+ (IJSVGParser*)groupForFileURL:(NSURL*)aURL
error:(NSError**)error
delegate:(id<IJSVGParserDelegate>)delegate;
- (NSSize)size;
- (BOOL)isFont;
- (NSArray*)glyphs;
- (NSArray<IJSVG*>*)subSVGs:(BOOL)recursive;
@end
File diff suppressed because it is too large Load Diff
@@ -6,18 +6,25 @@
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import "IJSVGNode.h"
#import "IJSVGRenderingStyle.h"
#import <IJSVG/IJSVGNode.h>
#import <IJSVG/IJSVGRenderingStyle.h>
#import <QuartzCore/QuartzCore.h>
@class IJSVGLayer;
@class IJSVGRootLayer;
@class IJSVGRootNode;
@interface IJSVGLayerTree : NSObject {
@private
NSMutableArray<NSValue*>* _viewPortStack;
}
@property (nonatomic, assign) CGRect viewBox;
@property (nonatomic, assign) CGFloat backingScale;
@property (nonatomic, retain) IJSVGRenderingStyle* style;
- (IJSVGLayer*)layerForNode:(IJSVGNode*)node;
- (id)initWithViewPortRect:(CGRect)viewPort
backingScale:(CGFloat)scale;
- (IJSVGRootLayer*)rootLayerForRootNode:(IJSVGRootNode*)rootNode;
@end
File diff suppressed because it is too large Load Diff
@@ -6,8 +6,8 @@
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import "IJSVGColorList.h"
#import "IJSVGNode.h"
#import <IJSVG/IJSVGColorList.h>
#import <IJSVG/IJSVGNode.h>
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@@ -10,13 +10,6 @@
@implementation IJSVGRenderingStyle
@synthesize colorList = _colorList;
@synthesize lineCapStyle = _lineCapStyle;
@synthesize lineJoinStyle = _lineJoinStyle;
@synthesize lineWidth = _lineWidth;
@synthesize fillColor = _fillColor;
@synthesize strokeColor = _strokeColor;
- (void)dealloc
{
(void)([_fillColor release]), _fillColor = nil;
@@ -6,7 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGColor.h"
#import <IJSVG/IJSVGColor.h>
#import <Foundation/Foundation.h>
@interface IJSVGStyle : NSObject {
@@ -91,7 +91,7 @@
+ (NSArray*)allowedColourKeys
{
return @[ @"fill", @"stroke-colour", @"stop-color", @"stroke" ];
return @[ @"fill", @"stroke-color", @"stop-color", @"stroke" ];
}
- (void)setProperties:(NSDictionary*)properties
@@ -6,8 +6,8 @@
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import "IJSVGStyleSheetRule.h"
#import "IJSVGStyleSheetSelector.h"
#import <IJSVG/IJSVGStyleSheetRule.h>
#import <IJSVG/IJSVGStyleSheetSelector.h>
#import <Foundation/Foundation.h>
@class IJSVGNode;
@@ -11,9 +11,6 @@
#import "IJSVGStyleSheet.h"
@interface IJSVGStyleSheetSelectorListItem : NSObject {
IJSVGStyleSheetSelector* selector;
IJSVGStyleSheetRule* rule;
}
@property (nonatomic, retain) IJSVGStyleSheetRule* rule;
@@ -23,12 +20,11 @@
@implementation IJSVGStyleSheetSelectorListItem
@synthesize rule, selector;
- (void)dealloc
{
(void)([rule release]), rule = nil;
(void)([selector release]), selector = nil;
(void)([_rule release]), _rule = nil;
(void)([_selector release]), _selector = nil;
[super dealloc];
}
@@ -6,16 +6,13 @@
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import "IJSVGStyle.h"
#import "IJSVGStyleSheetSelector.h"
#import <IJSVG/IJSVGStyle.h>
#import <IJSVG/IJSVGStyleSheetSelector.h>
#import <Foundation/Foundation.h>
@class IJSVGNode;
@interface IJSVGStyleSheetRule : NSObject {
NSArray* selectors;
IJSVGStyle* style;
}
@property (nonatomic, retain) NSArray* selectors;
@@ -10,12 +10,10 @@
@implementation IJSVGStyleSheetRule
@synthesize selectors, style;
- (void)dealloc
{
(void)([selectors release]), selectors = nil;
(void)([style release]), style = nil;
(void)([_selectors release]), _selectors = nil;
(void)([_style release]), _style = nil;
[super dealloc];
}
@@ -24,7 +22,7 @@
{
// interate over each select and work out if
// it allows us to be applied
for (IJSVGStyleSheetSelector* selector in selectors) {
for (IJSVGStyleSheetSelector* selector in _selectors) {
if ([selector matchesNode:node]) {
*matchedSelector = selector;
return YES;
@@ -6,7 +6,7 @@
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import "IJSVGStyleSheetSelectorRaw.h"
#import <IJSVG/IJSVGStyleSheetSelectorRaw.h>
#import <Foundation/Foundation.h>
@class IJSVGNode;
@@ -14,7 +14,7 @@
@interface IJSVGStyleSheetSelector : NSObject {
NSString* selector;
NSUInteger specificity;
@private
NSMutableArray* _rawSelectors;
}

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