Compare commits

...

43 Commits

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

This also changes how unit lengths works (now does correct parsing and using cstrings instead of nsstring where applicable)
2021-01-30 18:40:29 +00:00
Curtis Hard 972812b3f6 Fixes arc commands 2021-01-26 08:56:04 +00:00
Curtis Hard ad8f440c72 Merge branch 'feature/cm-mm-units' 2021-01-25 19:32:47 +00:00
92 changed files with 2040 additions and 1781 deletions
+18 -18
View File
@@ -11,6 +11,8 @@
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 */; };
@@ -30,7 +32,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 */; };
@@ -57,12 +58,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 */; };
@@ -113,7 +112,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 */; };
@@ -132,6 +130,8 @@
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 */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -139,6 +139,8 @@
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; };
@@ -160,7 +162,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>"; };
@@ -187,12 +188,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>"; };
@@ -243,7 +242,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>"; };
@@ -262,6 +260,8 @@
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>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -332,6 +332,8 @@
59EB758523905F6C00F5AE63 /* IJSVGColor.m */,
59EB75A923905F6F00F5AE63 /* IJSVGColorList.h */,
59EB757123905F6C00F5AE63 /* IJSVGColorList.m */,
59F36506262F1ABB00BCE3FD /* IJSVGColorType.h */,
59F36507262F1ABB00BCE3FD /* IJSVGColorType.m */,
);
path = Colors;
sourceTree = "<group>";
@@ -349,8 +351,6 @@
59EB757B23905F6C00F5AE63 /* IJSVGLayer.m */,
59EB75BF23905F7000F5AE63 /* IJSVGPatternLayer.h */,
59EB758C23905F6D00F5AE63 /* IJSVGPatternLayer.m */,
59EB757223905F6C00F5AE63 /* IJSVGRadialGradient.h */,
59EB75A623905F6F00F5AE63 /* IJSVGRadialGradient.m */,
59EB759723905F6D00F5AE63 /* IJSVGShapeLayer.h */,
59EB757A23905F6C00F5AE63 /* IJSVGShapeLayer.m */,
59EB75A423905F6E00F5AE63 /* IJSVGStrokeLayer.h */,
@@ -379,6 +379,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 */,
@@ -418,6 +420,8 @@
5919E65A23F480330051873A /* IJSVGUnitPoint.m */,
59A24EBA23F480EA0090C374 /* IJSVGUnitSize.h */,
59A24EBB23F480EA0090C374 /* IJSVGUnitSize.m */,
591A3E4B25CC91F800AD45B7 /* IJSVGParsing.h */,
591A3E4C25CC91F800AD45B7 /* IJSVGParsing.m */,
);
path = Utils;
sourceTree = "<group>";
@@ -449,8 +453,6 @@
592ABBEB2397A11800F44380 /* Additions */ = {
isa = PBXGroup;
children = (
59EB75C323905F7100F5AE63 /* IJSVGBezierPathAdditions.h */,
59EB758E23905F6D00F5AE63 /* IJSVGBezierPathAdditions.m */,
59EB75B823905F7000F5AE63 /* IJSVGStringAdditions.h */,
59EB757823905F6C00F5AE63 /* IJSVGStringAdditions.m */,
594A10D8248D7C90001A3181 /* NSImage+IJSVGAdditions.h */,
@@ -508,8 +510,6 @@
59EB757D23905F6C00F5AE63 /* IJSVGImageRep.m */,
59EB75CD23905F7200F5AE63 /* IJSVGView.h */,
59EB75C123905F7100F5AE63 /* IJSVGView.m */,
59EB756E23905F6C00F5AE63 /* IJSVGWriter.h */,
59EB758923905F6D00F5AE63 /* IJSVGWriter.m */,
);
path = Core;
sourceTree = "<group>";
@@ -541,7 +541,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 */,
@@ -586,14 +585,15 @@
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 */,
@@ -678,6 +678,7 @@
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 */,
@@ -700,17 +701,16 @@
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 */,
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 */,
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1220"
LastUpgradeVersion = "1250"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -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
@@ -7,43 +7,30 @@
//
#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
@@ -78,17 +65,7 @@
- (BOOL)ijsvg_isHexString
{
const char* chars = self.UTF8String;
char c;
while((c = *chars++)) {
BOOL flag = ((c == '#') ||
(c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'));
if(flag == NO) {
return NO;
}
}
return YES;
return IJSVGCharBufferIsHEX((char*)chars);
}
@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);
@@ -9,11 +9,15 @@
#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);
@@ -242,37 +246,51 @@ CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightnes
+ (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]
@@ -280,10 +298,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];
@@ -291,21 +309,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 [self computeColorSpace:NSColor.clearColor];
}
// could return nil
(void)free(str), str = NULL;
return [self.class colorFromPredefinedColorName:string];
}
+ (NSColor*)colorFromPredefinedColorName:(NSString*)name
@@ -754,28 +781,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;
@@ -18,16 +18,8 @@ typedef NS_ENUM(NSInteger, IJSVGCommandType) {
};
@interface IJSVGCommand : NSObject {
NSString* commandString;
char command;
CGFloat* parameters;
NSInteger parameterCount;
NSArray<IJSVGCommand*>* subCommands;
NSInteger requiredParameters;
IJSVGCommandType type;
IJSVGCommand* previousCommand;
@private
NSInteger _currentIndex;
BOOL isSubCommand;
}
@property (nonatomic, copy) NSString* commandString;
@@ -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;
@@ -103,10 +94,10 @@
- (void)dealloc
{
(void)([commandString release]), commandString = nil;
(void)([subCommands release]), subCommands = nil;
if (parameters) {
(void)(free(parameters)), parameters = nil;
(void)([_commandString release]), _commandString = nil;
(void)([_subCommands release]), _subCommands = nil;
if (_parameters) {
(void)(free(_parameters)), _parameters = nil;
}
[super dealloc];
}
@@ -117,12 +108,12 @@
if ((self = [super init]) != nil) {
// work out the basics
_currentIndex = 0;
command = str[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, strlen(str),
_parameters = IJSVGParsePathDataStreamSequence(str, strlen(str),
dataStream, sequence, [self.class requiredParameterCount], &sets);
if (sets <= 1) {
@@ -130,7 +121,7 @@
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,7 +145,7 @@
}
// store the retained value
subCommands = subCommandArray.copy;
_subCommands = subCommandArray.copy;
}
}
return self;
@@ -188,15 +179,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 +202,16 @@
_currentIndex = 0;
}
- (NSString *)description
{
NSMutableString* str = [[[NSMutableString alloc] init] autorelease];
[str appendFormat:@"%c ",_command];
NSMutableArray* args = [[[NSMutableArray alloc] initWithCapacity:_parameterCount] autorelease];
for(int i = 0; i < _parameterCount; i++) {
[args addObject:[NSString stringWithFormat:@"%f",_parameters[i]]];
}
[str appendString:[args componentsJoinedByString:@", "]];
return str;
}
@end
@@ -23,14 +23,16 @@
path:(IJSVGPath*)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.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 = path.currentPoint;
CGPathAddCurveToPoint(path.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,10 +30,11 @@ 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
@@ -42,75 +44,96 @@ static IJSVGPathDataSequence* _sequence;
{
CGPoint radii = CGPointZero;
CGPoint arcEndPoint = CGPointZero;
CGPoint arcStartPoint = path.currentPoint;
CGFloat xAxisRotation = 0;
CGPoint pathCurrentPoint = path.currentPoint;
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.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];
[trans rotateByRadians:-xAxisRotation];
[trans scaleXBy:(1 / scale.x)
yBy:(1 / scale.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);
[path.path appendBezierPathWithArcWithCenter:NSZeroPoint
radius:radius
startAngle:radians_to_degrees(startAngle)
endAngle:radians_to_degrees(startAngle + angleDelta)
clockwise:!sweepFlag];
trans = NSAffineTransform.transform;
[trans scaleXBy:scale.x
yBy:scale.y];
[trans rotateByRadians:xAxisRotation];
[trans translateXBy:centerPoint.x
yBy:centerPoint.y];
[path.path transformUsingAffineTransform:trans];
CGPathAddRelativeArc(path.path, &transform, 0.f, 0.f, 1.f,
startAngle, angleDelta);
}
@end
@@ -23,10 +23,12 @@
path:(IJSVGPath*)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[path.path lineToPoint:NSMakePoint(params[0], path.currentPoint.y)];
CGPathAddLineToPoint(path.path, NULL, params[0], path.currentPoint.y);
return;
}
[path.path relativeLineToPoint:NSMakePoint(params[0], 0.f)];
CGPoint currentPoint = path.currentPoint;
CGPathAddLineToPoint(path.path, NULL, currentPoint.x + params[0],
path.currentPoint.y);
}
@end
@@ -23,12 +23,12 @@
path:(IJSVGPath*)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[path.path lineToPoint:NSMakePoint(params[0], params[1])];
CGPathAddLineToPoint(path.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 = path.currentPoint;
CGPathAddLineToPoint(path.path, NULL, currentPoint.x + params[0],
currentPoint.y + params[1]);
}
@end
@@ -41,10 +41,14 @@
// there is no current point. Asking for current point on an empty
// path will result in an exception being thrown
if (type == kIJSVGCommandTypeAbsolute || command == nil) {
[path.path moveToPoint:NSMakePoint(params[0], params[1])];
CGPathMoveToPoint(path.path, NULL,
params[0], params[1]);
return;
}
[path.path relativeMoveToPoint:NSMakePoint(params[0], params[1])];
CGPoint currentPoint = path.currentPoint;
CGPathMoveToPoint(path.path, NULL,
currentPoint.x + params[0],
currentPoint.y + params[1]);
}
@end
@@ -24,12 +24,14 @@
path:(IJSVGPath*)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[path.path addQuadCurveToPoint:NSMakePoint(params[2], params[3])
controlPoint:NSMakePoint(params[0], params[1])];
CGPathAddQuadCurveToPoint(path.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 = path.currentPoint;
CGPathAddQuadCurveToPoint(path.path, NULL,
currentPoint.x + params[0], currentPoint.y + params[1],
currentPoint.x + params[2], currentPoint.y + params[3]);
}
@end
@@ -24,41 +24,41 @@
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
{
NSPoint firstControl = NSMakePoint(path.currentPoint.x, path.currentPoint.y);
CGPoint currentPoint = path.currentPoint;
CGPoint firstControl = CGPointMake(currentPoint.x, currentPoint.y);
if (command != nil) {
if (command.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.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.path, NULL, firstControl.x, firstControl.y,
currentPoint.x + params[0], currentPoint.y + params[1],
currentPoint.x + params[2], currentPoint.y + params[3]);
}
@end
@@ -24,33 +24,34 @@
type:(IJSVGCommandType)type
path:(IJSVGPath*)path
{
NSPoint commandPoint = NSMakePoint(path.currentPoint.x, path.currentPoint.y);
CGPoint currentPoint = path.currentPoint;
CGPoint commandPoint = CGPointMake(currentPoint.x, currentPoint.y);
if (command != nil) {
if (command.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 * (path.lastControlPoint.x) + 2 * (currentPoint.x),
-1 * (path.lastControlPoint.y) + 2 * currentPoint.y);
}
}
path.lastControlPoint = commandPoint;
if (type == kIJSVGCommandTypeAbsolute) {
[path.path addQuadCurveToPoint:NSMakePoint(params[0], params[1])
controlPoint:commandPoint];
CGPathAddQuadCurveToPoint(path.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.path, NULL, commandPoint.x, commandPoint.y,
currentPoint.x + params[0], currentPoint.y + params[1]);
}
@end
@@ -23,10 +23,11 @@
path:(IJSVGPath*)path
{
if (type == kIJSVGCommandTypeAbsolute) {
[path.path lineToPoint:NSMakePoint(path.currentPoint.x, params[0])];
CGPathAddLineToPoint(path.path, NULL, path.currentPoint.x, params[0]);
return;
}
[path.path relativeLineToPoint:NSMakePoint(0.f, params[0])];
CGPoint currentPoint = path.currentPoint;
CGPathAddLineToPoint(path.path, NULL, currentPoint.x, currentPoint.y + params[0]);
}
@end
+27 -12
View File
@@ -6,21 +6,26 @@
// 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/IJSVGExporter.h>
#import <IJSVG/IJSVGGradientLayer.h>
#import <IJSVG/IJSVGGroupLayer.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
@@ -125,6 +130,13 @@
- (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;
@@ -164,6 +176,9 @@
- (void)setNeedsDisplay;
// colors
- (IJSVGColorList*)computedColorList:(BOOL*)hasPatternFills;
- (IJSVGColorList*)colorList;
- (void)performBlock:(dispatch_block_t)block;
// matching
- (BOOL)matchesPropertiesWithMask:(IJSVGMatchPropertiesMask)mask;
@end
+145 -57
View File
@@ -12,26 +12,21 @@
@implementation IJSVG
// these are explicitly implemented
@synthesize title = _title;
@synthesize desc = _desc;
@synthesize renderingBackingScaleHelper;
@synthesize clipToViewport;
@synthesize renderQuality;
@synthesize renderingStyle = _renderingStyle;
@synthesize intrinsicSize = _intrinsicSize;
- (void)dealloc
{
// this can all be called on the background thread to be released
BOOL hasTransaction = IJSVGBeginTransaction();
(void)([renderingBackingScaleHelper release]),
renderingBackingScaleHelper = nil;
(void)([_renderingBackingScaleHelper release]), _renderingBackingScaleHelper = nil;
(void)([_replacementColors release]), _replacementColors = nil;
(void)([_renderingStyle release]), _renderingStyle = nil;
(void)([_group release]), _group = nil;
(void)([_intrinsicSize release]), _intrinsicSize = nil;
(void)([_desc release]), _desc = nil;
(void)([_title release]), _title = nil;
(void)([_desc release]), _desc = nil;
// kill any memory that has been around
(void)([_layerTree release]), _layerTree = nil;
@@ -74,12 +69,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;
}
@@ -554,20 +572,24 @@
{
// 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) {
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];
[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) {
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];
[IJSVGLayer recursivelyWalkLayer:self.layer
withBlock:block];
}
- (void)prepForDrawingInView:(NSView*)view
@@ -645,7 +667,14 @@
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;
}
@@ -746,7 +775,7 @@
// dont do anything, nothing has changed, no point of iterating over
// every layer for no reason!
if (scale == _lastProposedBackingScale && renderQuality == _lastProposedRenderQuality) {
if (scale == _lastProposedBackingScale && _renderQuality == _lastProposedRenderQuality) {
return _backingScaleFactor;
}
@@ -758,7 +787,8 @@
}
// walk the tree
void (^block)(CALayer* layer, BOOL isMask) = ^void(CALayer* layer, BOOL isMask) {
void (^block)(CALayer* layer, BOOL isMask, BOOL* stop) =
^void(CALayer* layer, BOOL isMask, BOOL* stop) {
IJSVGLayer* propLayer = ((IJSVGLayer*)layer);
propLayer.renderQuality = quality;
if (propLayer.requiresBackingScaleHelp == YES) {
@@ -834,53 +864,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 isMask, BOOL* stop) =
^void(CALayer* layer, BOOL isMask, BOOL* stop) {
// dont do anything
if(([layer isKindOfClass:IJSVGShapeLayer.class] && isMask == NO &&
layer.isHidden == NO) == false) {
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.layer
withBlock:block];
return sheet;
}
@@ -950,4 +999,43 @@
}
}
#pragma mark matching
- (BOOL)matchesPropertiesWithMask:(IJSVGMatchPropertiesMask)mask
{
__block IJSVGMatchPropertiesMask matchedMask = IJSVGMatchPropertyNone;
IJSVGNodeWalkHandler handler = ^(IJSVGNode* node, BOOL* allowChildNodes,
BOOL* stop) {
// dont compute nodes that are not designed
// to be rendered
if(node.shouldRender == NO) {
*allowChildNodes = NO;
return;
}
// check for stroke
IJSVGPath* path = (IJSVGPath*)node;
if((mask & IJSVGMatchPropertyContainsStrokedElement) != 0 &&
[node isKindOfClass:IJSVGPath.class] == YES &&
path.isStroked == YES) {
matchedMask |= IJSVGMatchPropertyContainsStrokedElement;
}
// check for mask
if((mask & IJSVGMatchPropertyContainsMaskedElement) != 0 &&
node.mask != nil) {
matchedMask |= IJSVGMatchPropertyContainsMaskedElement;
}
// simply check if masks equal, if they are, stop this loop
// and return the evaluation
if(matchedMask == mask) {
*stop = YES;
}
};
[IJSVGNode walkNodeTree:_group
handler:handler];
return matchedMask == mask;
}
@end
@@ -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];
}
@@ -6,7 +6,7 @@
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import "IJSVG.h"
#import <IJSVG/IJSVG.h>
#import <Cocoa/Cocoa.h>
IB_DESIGNABLE
@@ -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,9 +7,15 @@
//
#import <Foundation/Foundation.h>
#import "IJSVGUtils.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);
@@ -39,7 +45,16 @@ typedef NS_OPTIONS(NSInteger, IJSVGExporterOptions) {
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
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);
@@ -47,6 +62,22 @@ void IJSVGEnumerateCGPathElements(CGPathRef path, IJSVGPathElementEnumerationBlo
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 {
@private
@@ -58,22 +89,30 @@ const NSDictionary<NSString*, NSString*>* IJSVGDefaultAttributes(void);
NSInteger _idCount;
NSInteger _shortIdCount;
BOOL _appliedXLink;
struct {
unsigned int identifierForElement: 1;
unsigned int stringForColor: 1;
} _respondsTo;
}
@property (nonatomic, assign) id<IJSVGExporterDelegate> delegate;
@property (nonatomic, assign) IJSVGFloatingPointOptions floatingPointOptions;
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* description;
@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;
size:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (NSString*)SVGString;
- (NSData*)SVGData;
- (IJSVG*)SVG:(NSError**)error;
@end
NS_ASSUME_NONNULL_END
@@ -28,9 +28,6 @@
#define XML_DOC_CHARSET @"UTF-8"
#define XML_DOC_GENERATOR @"Generated by IJSVG (https://github.com/iconjar/IJSVG)"
@synthesize title;
@synthesize description;
BOOL IJSVGExporterHasOption(IJSVGExporterOptions options, NSInteger option)
{
return (options & option) != 0;
@@ -49,7 +46,7 @@ const NSArray<NSString*>* IJSVGShortCharacterArray(void)
return _array;
}
const NSDictionary<NSString*, NSString*>* IJSVGDefaultAttributes()
const NSDictionary<NSString*, NSString*>* IJSVGDefaultAttributes(void)
{
static NSDictionary* _defaults;
static dispatch_once_t onceToken;
@@ -111,7 +108,7 @@ const NSDictionary<NSString*, NSString*>* IJSVGDefaultAttributes()
return _defaults;
}
const NSArray* IJSVGInheritableAttributes()
const NSArray* IJSVGInheritableAttributes(void)
{
static NSArray* _attributes;
static dispatch_once_t onceToken;
@@ -195,8 +192,8 @@ NSString* IJSVGHash(NSString* key)
(void)([_svg release]), _svg = nil;
(void)([_dom release]), _dom = nil;
(void)([_defElement release]), _defElement = nil;
(void)([title release]), title = nil;
(void)([description release]), description = nil;
(void)([_title release]), _title = nil;
(void)([_desc release]), _desc = nil;
[super dealloc];
}
@@ -204,15 +201,16 @@ NSString* IJSVGHash(NSString* key)
size:(CGSize)size
options:(IJSVGExporterOptions)options
{
IJSVGFloatingPointOptions fpo = IJSVGFloatingPointOptionsDefault();
return [self initWithSVG:svg
size:size
options:options
floatingPointOptions:IJSVGFloatingPointOptionsDefault()];
floatingPointOptions:fpo];
}
- (id)initWithSVG:(IJSVG*)svg
size:(CGSize)size
options:(IJSVGExporterOptions)options
size:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
if ((self = [super init]) != nil) {
@@ -222,15 +220,17 @@ NSString* IJSVGHash(NSString* key)
// defaults for floating point rounding, if any
_floatingPointOptions = floatingPointOptions;
// clear memory as soon as possible
@autoreleasepool {
[self _prepare];
}
}
return self;
}
- (void)setDelegate:(id<IJSVGExporterDelegate>)delegate
{
_delegate = delegate;
_respondsTo.identifierForElement = [delegate respondsToSelector:@selector(svgExporter:identifierForElement:type:defaultID:)];
_respondsTo.stringForColor = [delegate respondsToSelector:@selector(svgExporter:stringForColor:flags:options:)];
}
- (NSXMLElement*)defElement
{
if (_defElement != nil) {
@@ -378,8 +378,15 @@ NSString* IJSVGHash(NSString* key)
return [NSString stringWithFormat:@"%@%ld", chars[(_idCount++ % chars.count)], _shortIdCount];
}
- (void)_prepare
- (void)_generateDOMDocument
{
_idCount = 0;
_shortIdCount = 0;
_appliedXLink = NO;
(void)[_dom release], _dom = nil;
(void)[_defElement release], _defElement = nil;
// create the stand alone DOM
NSXMLElement* nestedRoot = nil;
NSXMLElement* rootNode = [self rootNode:&nestedRoot];
@@ -592,7 +599,7 @@ NSString* IJSVGHash(NSString* key)
if ([self compareElementChildren:gradientA toElement:gradientB] == YES) {
NSString* idString = [gradientB attributeForName:@"id"].stringValue;
if (idString == nil || idString.length == 0) {
idString = [self generateID];
idString = [self identifierForElement:gradientA];
IJSVGApplyAttributesToElement(@{ @"id" : idString }, gradientB);
}
NSDictionary* atts = @{ @"xlink:href" : IJSVGHash(idString) };
@@ -923,7 +930,7 @@ NSString* IJSVGHash(NSString* key)
element.name = @"path";
NSDictionary* atts = @{ @"d" : data,
@"id" : [self generateID] };
@"id" : [self identifierForElement:element] };
IJSVGApplyAttributesToElement(atts, element);
// store it against the def
@@ -1127,7 +1134,7 @@ NSString* IJSVGHash(NSString* key)
patternElement.name = @"pattern";
NSMutableDictionary* dict = [[[NSMutableDictionary alloc] init] autorelease];
dict[@"id"] = [self generateID];
dict[@"id"] = [self identifierForElement:patternElement];
dict[@"width"] = IJSVGShortFloatStringWithOptions(layer.patternNode.width.value, _floatingPointOptions);
dict[@"height"] = IJSVGShortFloatStringWithOptions(layer.patternNode.height.value, _floatingPointOptions);
@@ -1175,7 +1182,6 @@ NSString* IJSVGHash(NSString* key)
toElement:(NSXMLElement*)element
{
IJSVGGradient* gradient = layer.gradient;
NSString* gradKey = [self generateID];
NSXMLElement* gradientElement = [[[NSXMLElement alloc] init] autorelease];
// work out linear gradient
@@ -1183,7 +1189,7 @@ NSString* IJSVGHash(NSString* key)
IJSVGLinearGradient* lGradient = (IJSVGLinearGradient*)gradient;
gradientElement.name = @"linearGradient";
NSDictionary* dict = @{ @"id" : gradKey,
NSDictionary* dict = @{
@"x1" : [lGradient.x1 stringValueWithFloatingPointOptions:_floatingPointOptions],
@"y1" : [lGradient.y1 stringValueWithFloatingPointOptions:_floatingPointOptions],
@"x2" : [lGradient.x2 stringValueWithFloatingPointOptions:_floatingPointOptions],
@@ -1196,7 +1202,7 @@ NSString* IJSVGHash(NSString* key)
// assume radial
IJSVGRadialGradient* rGradient = (IJSVGRadialGradient*)gradient;
gradientElement.name = @"radialGradient";
NSDictionary* dict = @{ @"id" : gradKey,
NSDictionary* dict = @{
@"cx" : [rGradient.cx stringValueWithFloatingPointOptions:_floatingPointOptions],
@"cy" : [rGradient.cy stringValueWithFloatingPointOptions:_floatingPointOptions],
@"fx" : [rGradient.fx stringValueWithFloatingPointOptions:_floatingPointOptions],
@@ -1206,6 +1212,10 @@ NSString* IJSVGHash(NSString* key)
// give it the attributes
IJSVGApplyAttributesToElement(dict, gradientElement);
}
// apply the identifier
NSString* gradKey = [self identifierForElement:gradientElement];
IJSVGApplyAttributesToElement(@{@"id": gradKey}, gradientElement);
// apply the units
if (layer.gradient.units == IJSVGUnitUserSpaceOnUse) {
@@ -1215,7 +1225,7 @@ NSString* IJSVGHash(NSString* key)
// add the stops
NSGradient* grad = layer.gradient.gradient;
IJSVGColorList* sheet = layer.gradient.colorList;
IJSVGColorList* sheet = layer.gradient.computedColorList;
NSInteger noStops = grad.numberOfColorStops;
for (NSInteger i = 0; i < noStops; i++) {
@@ -1239,9 +1249,9 @@ NSString* IJSVGHash(NSString* key)
// add the color
IJSVGColorStringOptions options = IJSVGColorStringOptionForceHEX | IJSVGColorStringOptionAllowShortHand;
NSString* stopColor = [IJSVGColor colorStringFromColor:aColor
options:options];
NSString* stopColor = [self colorStringForColor:aColor
flag:IJSVGColorTypeFlagStop
options:options];
// dont bother adding default
if ([stopColor isEqualToString:@"#000"] == NO) {
atts[@"stop-color"] = stopColor;
@@ -1300,7 +1310,7 @@ NSString* IJSVGHash(NSString* key)
imageElement.name = @"image";
NSMutableDictionary* dict = [[[NSMutableDictionary alloc] init] autorelease];
dict[@"id"] = [self generateID];
dict[@"id"] = [self identifierForElement:imageElement];
dict[@"width"] = IJSVGShortFloatStringWithOptions(layer.frame.size.width, _floatingPointOptions);
dict[@"height"] = IJSVGShortFloatStringWithOptions(layer.frame.size.height, _floatingPointOptions);
dict[@"xlink:href"] = base64String;
@@ -1365,7 +1375,7 @@ NSString* IJSVGHash(NSString* key)
BOOL cleanupPaths = IJSVGExporterHasOption(_options, IJSVGExporterOptionCleanupPaths);
BOOL convertArcs = IJSVGExporterHasOption(_options, IJSVGExporterOptionConvertArcs);
BOOL convertShapesToPaths = IJSVGExporterHasOption(_options, IJSVGExporterOptionConvertShapesToPaths);
// path
switch (layer.primitiveType) {
case kIJSVGPrimitivePathTypeRect: {
@@ -1516,7 +1526,10 @@ NSString* IJSVGHash(NSString* key)
}
});
if (layer.primitiveType == kIJSVGPrimitivePathTypePolygon) {
[instructions removeLastObject];
// remove last one if it was M
if(instructions.lastObject.instruction == 'M') {
[instructions removeLastObject];
}
IJSVGExporterPathInstruction* instruction = nil;
instruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'Z'
dataCount:0] autorelease];
@@ -1525,8 +1538,10 @@ NSString* IJSVGHash(NSString* key)
dict[@"d"] = [self pathFromInstructions:instructions];
} else {
NSMutableArray<NSString*>* points = [[[NSMutableArray alloc] init] autorelease];
__block CGPathElementType type;
IJSVGEnumerateCGPathElements(transformPath, ^(const CGPathElement* pathElement, CGPoint currentPoint) {
switch (pathElement->type) {
type = pathElement->type;
switch (type) {
case kCGPathElementMoveToPoint: {
pathElement->points[0].x = pathElement->points[0].x;
pathElement->points[0].y = pathElement->points[0].y;
@@ -1544,7 +1559,8 @@ NSString* IJSVGHash(NSString* key)
}
});
// polygon does not need the move to command
if (layer.primitiveType == kIJSVGPrimitivePathTypePolygon) {
if (layer.primitiveType == kIJSVGPrimitivePathTypePolygon &&
type == kCGPathElementMoveToPoint) {
[points removeLastObject];
}
dict[@"points"] = [points componentsJoinedByString:@" "];
@@ -1679,8 +1695,9 @@ NSString* IJSVGHash(NSString* key)
// fill color
if (layer.fillColor != nil) {
NSColor* fillColor = [NSColor colorWithCGColor:layer.fillColor];
NSString* colorString = [IJSVGColor colorStringFromColor:fillColor
options:[self colorOptions]];
NSString* colorString = [self colorStringForColor:fillColor
flag:IJSVGColorTypeFlagFill
options:[self colorOptions]];
// could be none
if (colorString != nil) {
@@ -1731,8 +1748,9 @@ NSString* IJSVGHash(NSString* key)
} else if (strokeLayer.strokeColor != nil) {
NSColor* strokeColor = [NSColor colorWithCGColor:strokeLayer.strokeColor];
NSString* strokeColorString = [IJSVGColor colorStringFromColor:strokeColor
options:[self colorOptions]];
NSString* strokeColorString = [self colorStringForColor:strokeColor
flag:IJSVGColorTypeFlagStroke
options:[self colorOptions]];
// could be none
if (strokeColorString != nil) {
@@ -1855,7 +1873,7 @@ NSString* IJSVGHash(NSString* key)
mask.name = @"mask";
// create the key
NSString* maskKey = [self generateID];
NSString* maskKey = [self identifierForElement:mask];
NSMutableDictionary* dict = [[[NSMutableDictionary alloc] init] autorelease];
dict[@"id"] = maskKey;
@@ -1881,6 +1899,16 @@ NSString* IJSVGHash(NSString* key)
[[self defElement] addChild:mask];
}
- (NSXMLDocument*)_dom
{
if(_dom == nil) {
@autoreleasepool {
[self _generateDOMDocument];
}
}
return _dom;
}
- (NSString*)SVGString
{
NSXMLNodeOptions options = NSXMLNodePrettyPrint;
@@ -1888,7 +1916,7 @@ NSString* IJSVGHash(NSString* key)
options = NSXMLNodeOptionsNone;
}
options |= NSXMLNodeCompactEmptyElement;
NSString* output = [_dom XMLStringWithOptions:options];
NSString* output = [[self _dom] XMLStringWithOptions:options];
if (IJSVGExporterHasOption(_options, IJSVGExporterOptionRemoveXMLDeclaration) == YES) {
return [output substringFromIndex:38];
}
@@ -2036,4 +2064,46 @@ void IJSVGEnumerateCGPathElements(CGPathRef path, IJSVGPathElementEnumerationBlo
}
}
#pragma mark Delegate calling methods
- (NSString*)identifierForElement:(NSXMLElement* _Nullable)element
{
NSString* identifier = nil;
if(_respondsTo.identifierForElement == 1) {
__weak id weakSelf = self;
NSString* (^block)(void) = ^NSString*(void) {
return [weakSelf generateID];
};
IJSVGNodeType type = [IJSVGNode typeForString:element.localName
kind:element.kind];
identifier = [_delegate svgExporter:self
identifierForElement:element
type:type
defaultID:block];
if(identifier != nil) {
return identifier;
}
}
return [self generateID];
}
- (NSString*)colorStringForColor:(NSColor*)color
flag:(IJSVGColorTypeFlags)flag
options:(IJSVGColorStringOptions)options
{
NSString* colorString = nil;
if(_respondsTo.stringForColor == 1) {
color = [IJSVGColor computeColorSpace:color];
colorString = [_delegate svgExporter:self
stringForColor:color
flags:flag
options:options];
if(colorString != nil) {
return colorString;
}
}
return [IJSVGColor colorStringFromColor:color
options:options];
}
@end
@@ -364,16 +364,16 @@ void IJSVGExporterPathInstructionRoundData(CGFloat* data, NSInteger length,
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[[IJSVGExporterPathInstruction alloc] initWithInstruction:'t'
dataCount:2] autorelease];
nInstruction.data[0] = instruction.data[3];
nInstruction.data[1] = instruction.data[4];
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[3];
nInstruction.data[1] = instruction.data[4];
nInstruction.data[0] = instruction.data[2];
nInstruction.data[1] = instruction.data[3];
[nInstructions addObject:nInstruction];
continue;
}
@@ -6,9 +6,9 @@
// 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 {
@@ -10,14 +10,9 @@
@implementation IJSVGGradientLayer
@synthesize viewBox;
@synthesize gradient;
@synthesize absoluteTransform;
@synthesize objectRect;
- (void)dealloc
{
(void)([gradient release]), gradient = nil;
(void)([_gradient release]), _gradient = nil;
[super dealloc];
}
@@ -32,17 +27,17 @@
- (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) {
@@ -89,12 +84,12 @@
}
// draw the gradient
CGAffineTransform trans = CGAffineTransformMakeTranslation(-CGRectGetMinX(objectRect),
-CGRectGetMinY(objectRect));
CGAffineTransform transform = CGAffineTransformConcat(absoluteTransform, trans);
CGAffineTransform trans = CGAffineTransformMakeTranslation(-CGRectGetMinX(_objectRect),
-CGRectGetMinY(_objectRect));
CGAffineTransform transform = CGAffineTransformConcat(_absoluteTransform, trans);
CGContextSaveGState(ctx);
[self.gradient drawInContextRef:ctx
objectRect:objectRect
objectRect:_objectRect
absoluteTransform:transform
viewPort:self.viewBox];
CGContextRestoreGState(ctx);
@@ -6,8 +6,8 @@
// 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
@@ -6,7 +6,7 @@
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import "IJSVGLayer.h"
#import <IJSVG/IJSVGLayer.h>
#import <AppKit/AppKit.h>
#import <QuartzCore/QuartzCore.h>
@@ -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;
@@ -36,7 +36,7 @@
+ (NSArray*)deepestSublayersOfLayer:(CALayer*)layer;
+ (void)recursivelyWalkLayer:(CALayer*)layer
withBlock:(void (^)(CALayer* layer, BOOL isMask))block;
withBlock:(void (^)(CALayer* layer, BOOL isMask, BOOL* stop))block;
- (void)applySublayerMaskToContext:(CGContextRef)context
forSublayer:(IJSVGLayer*)sublayer
@@ -13,16 +13,6 @@
@implementation IJSVGLayer
@synthesize gradientFillLayer;
@synthesize patternFillLayer;
@synthesize gradientStrokeLayer;
@synthesize patternStrokeLayer;
@synthesize strokeLayer;
@synthesize requiresBackingScaleHelp;
@synthesize backingScaleFactor;
@synthesize blendingMode;
@synthesize convertMasksToPaths;
- (void)dealloc
{
(void)([_maskingLayer release]), _maskingLayer = nil;
@@ -44,14 +34,21 @@
}
+ (void)recursivelyWalkLayer:(CALayer*)layer
withBlock:(void (^)(CALayer* layer, BOOL isMask))block
withBlock:(void (^)(CALayer* layer, BOOL isMask, BOOL* stop))block
{
// call for layer and mask if there is one
block(layer, NO);
BOOL stop = NO;
block(layer, NO, &stop);
if(stop == YES) {
return;
}
// do the mask too!
if (layer.mask != nil) {
block(layer.mask, YES);
block(layer.mask, YES, &stop);
if(stop == YES) {
return;
}
}
// sublayers!!
@@ -63,10 +60,10 @@
- (void)setBackingScaleFactor:(CGFloat)newFactor
{
if (self.backingScaleFactor == newFactor) {
if (_backingScaleFactor == newFactor) {
return;
}
backingScaleFactor = newFactor;
_backingScaleFactor = newFactor;
self.contentsScale = newFactor;
self.rasterizationScale = newFactor;
[self setNeedsDisplay];
@@ -74,7 +71,7 @@
- (void)_customRenderInContext:(CGContextRef)ctx
{
if (self.convertMasksToPaths == YES && _maskingLayer != nil) {
if (_convertMasksToPaths == YES && _maskingLayer != nil) {
CGContextSaveGState(ctx);
[self applySublayerMaskToContext:ctx
forSublayer:(IJSVGLayer*)self
@@ -88,10 +85,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;
@@ -151,9 +148,9 @@
- (void)renderInContext:(CGContextRef)ctx
{
if (self.blendingMode != kCGBlendModeNormal) {
if (_blendingMode != kCGBlendModeNormal) {
CGContextSaveGState(ctx);
CGContextSetBlendMode(ctx, self.blendingMode);
CGContextSetBlendMode(ctx, _blendingMode);
[self _customRenderInContext:ctx];
CGContextRestoreGState(ctx);
return;
@@ -10,13 +10,10 @@
@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];
}
@@ -50,8 +47,8 @@ void IJSVGPatternDrawingCallBack(void* info, CGContextRef ctx)
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),
roundf(rect.size.width * _patternNode.width.value),
roundf(rect.size.height * _patternNode.height.value),
kCGPatternTilingConstantSpacing,
true, &callbacks);
@@ -6,8 +6,8 @@
// 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 {
@@ -11,19 +11,6 @@
@implementation IJSVGShapeLayer
@synthesize gradientFillLayer;
@synthesize patternFillLayer;
@synthesize gradientStrokeLayer;
@synthesize patternStrokeLayer;
@synthesize strokeLayer;
@synthesize requiresBackingScaleHelp;
@synthesize backingScaleFactor;
@synthesize blendingMode;
@synthesize convertMasksToPaths;
@synthesize originalPathOrigin;
@synthesize renderQuality;
@synthesize primitiveType;
- (void)dealloc
{
(void)([_maskingLayer release]), _maskingLayer = nil;
@@ -35,7 +22,7 @@
if (self.backingScaleFactor == newFactor) {
return;
}
backingScaleFactor = newFactor;
_backingScaleFactor = newFactor;
self.contentsScale = newFactor;
self.rasterizationScale = newFactor;
[self setNeedsDisplay];
@@ -57,10 +44,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;
@@ -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 <IJSVG/IJSVGColorList.h>
#import <IJSVG/IJSVGDef.h>
#import <IJSVG/IJSVGTransform.h>
#import <IJSVG/IJSVGGroup.h>
#import <Foundation/Foundation.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.childNodes;
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 = stopNode.fillColor;
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,16 +79,23 @@
[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
@@ -145,8 +108,8 @@
[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);
}
@@ -154,7 +117,7 @@
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*>* _childNodes;
}
- (void)addChild:(id)child;
- (NSArray*)children;
- (void)addChild:(IJSVGNode*)child;
- (NSArray<IJSVGNode*>*)childNodes;
- (void)purgeChildren;
@end
+16 -11
View File
@@ -12,21 +12,24 @@
- (void)dealloc
{
(void)([children release]), children = nil;
(void)([_childNodes release]), _childNodes = nil;
[super dealloc];
}
- (id)init
{
if ((self = [super init]) != nil) {
children = [[NSMutableArray alloc] init];
_childNodes = [[NSMutableArray alloc] init];
}
return self;
}
- (void)prepareFromCopy
{
children = [[NSMutableArray alloc] init];
if(_childNodes != nil) {
(void)[_childNodes release], _childNodes = nil;
}
_childNodes = [[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 _childNodes) {
childNode = [[childNode copy] autorelease];
childNode.parentNode = node;
[node addChild:childNode];
@@ -44,23 +47,25 @@
- (void)purgeChildren
{
[children removeAllObjects];
[_childNodes removeAllObjects];
}
- (void)addChild:(id)child
- (void)addChild:(IJSVGNode*)child
{
if (child != nil)
[children addObject:child];
if (child != nil) {
[_childNodes addObject:child];
}
}
- (NSArray*)children
- (NSArray<IJSVGNode*>*)childNodes
{
return children;
return _childNodes;
}
- (NSString*)description
{
return [NSString stringWithFormat:@"%@ - %@", [super description], self.children];
return [NSString stringWithFormat:@"%@ - %@",
[super description], self.childNodes];
}
@end
@@ -6,7 +6,7 @@
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import "IJSVGNode.h"
#import <IJSVG/IJSVGNode.h>
#import <Foundation/Foundation.h>
@class IJSVGPath;
@@ -21,6 +21,7 @@
- (CGImageRef)CGImage;
- (void)drawInContextRef:(CGContextRef)context
path:(IJSVGPath*)path;
- (void)loadFromBase64EncodedString:(NSString*)encodedString;
- (void)loadFromString:(NSString*)encodedString;
- (void)loadFromURL:(NSURL*)aURL;
@end
@@ -20,7 +20,7 @@
[super dealloc];
}
- (void)loadFromBase64EncodedString:(NSString*)encodedString
- (void)loadFromString:(NSString*)encodedString
{
if ([encodedString hasPrefix:@"data:"]) {
encodedString = [encodedString stringByReplacingOccurrencesOfString:@"\\s+"
@@ -28,8 +28,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) {
@@ -46,7 +53,8 @@
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)];
CGRect rect = CGRectMake(0.f, 0.f, self.width.value, self.height.value);
CGPathAddRect(imagePath.path, NULL, rect);
[imagePath close];
}
return imagePath;
@@ -92,7 +100,7 @@
path = [self path];
}
CGRect rect = path.path.bounds;
CGRect rect = path.pathBoundingBox;
CGRect bounds = CGRectMake(0.f, 0.f, rect.size.width, rect.size.height);
// save the state of the context
@@ -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,10 +16,10 @@
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];
@@ -26,8 +27,8 @@
// 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
+16 -2
View File
@@ -6,11 +6,12 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGStyle.h"
#import "IJSVGUnitLength.h"
#import <IJSVG/IJSVGStyle.h>
#import <IJSVG/IJSVGUnitLength.h>
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
@class IJSVGNode;
@class IJSVG;
@class IJSVGGroup;
@class IJSVGDef;
@@ -19,6 +20,8 @@
@class IJSVGPattern;
@class IJSVGTransform;
typedef void (^IJSVGNodeWalkHandler)(IJSVGNode* node, BOOL* allowChildNodes, BOOL* stop);
typedef NS_ENUM(NSInteger, IJSVGNodeType) {
IJSVGNodeTypeGroup,
IJSVGNodeTypePath,
@@ -45,6 +48,7 @@ typedef NS_ENUM(NSInteger, IJSVGNodeType) {
IJSVGNodeTypeSwitch,
IJSVGNodeTypeTitle,
IJSVGNodeTypeDesc,
IJSVGNodeTypeStop,
IJSVGNodeTypeNotFound,
};
@@ -95,6 +99,11 @@ typedef NS_ENUM(NSInteger, IJSVGBlendMode) {
IJSVGBlendModeLuminosity = kCGBlendModeLuminosity
};
typedef NS_ENUM(NSInteger, IJSVGOverflowVisibility) {
IJSVGOverflowVisibilityHidden,
IJSVGOverflowVisibilityVisible
};
static CGFloat IJSVGInheritedFloatValue = -99.9999991;
@interface IJSVGNode : NSObject <NSCopying>
@@ -116,6 +125,7 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991;
@property (nonatomic, retain) IJSVGUnitLength* fillOpacity;
@property (nonatomic, retain) IJSVGUnitLength* strokeOpacity;
@property (nonatomic, retain) IJSVGUnitLength* strokeWidth;
@property (nonatomic, retain) IJSVGUnitLength* offset;
@property (nonatomic, retain) NSColor* fillColor;
@property (nonatomic, retain) NSColor* strokeColor;
@property (nonatomic, copy) NSString* identifier;
@@ -139,6 +149,10 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991;
@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;
+183 -149
View File
@@ -9,146 +9,134 @@
#import "IJSVGDef.h"
#import "IJSVGNode.h"
#import "IJSVGUtils.h"
#import "IJSVGGroup.h"
@implementation IJSVGNode
@synthesize title;
@synthesize desc;
@synthesize shouldRender;
@synthesize type;
@synthesize name;
@synthesize classNameList;
@synthesize className;
@synthesize unicode;
@synthesize x;
@synthesize y;
@synthesize width;
@synthesize height;
@synthesize fillColor;
@synthesize fillOpacity;
@synthesize strokeColor;
@synthesize strokeOpacity;
@synthesize strokeWidth;
@synthesize opacity;
@synthesize identifier;
@synthesize parentNode;
@synthesize intermediateParentNode;
@synthesize transforms;
@synthesize windingRule;
@synthesize def;
@synthesize fillGradient;
@synthesize fillPattern;
@synthesize strokeGradient;
@synthesize strokePattern;
@synthesize clipPath;
@synthesize lineCapStyle;
@synthesize lineJoinStyle;
@synthesize strokeDashArrayCount;
@synthesize strokeDashArray;
@synthesize strokeDashOffset;
@synthesize usesDefaultFillColor;
@synthesize svg;
@synthesize mask;
@synthesize units;
@synthesize contentUnits;
@synthesize blendMode;
- (void)dealloc
{
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)([title release]), title = nil;
(void)([desc release]), desc = 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)([_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)([_title release]), _title = nil;
(void)([_desc release]), _desc = 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;
[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;
}
if([string isEqualToString:@"title"]) {
if(strcmp(name, "title") == 0) {
return IJSVGNodeTypeTitle;
}
if([string isEqualToString:@"desc"]) {
if(strcmp(name, "desc") == 0) {
return IJSVGNodeTypeDesc;
}
// are we commong HTML? - if so just treat as a group
if (IJSVGIsCommonHTMLElementName(string) == YES) {
return IJSVGNodeTypeGroup;
}
return IJSVGNodeTypeNotFound;
}
@@ -160,6 +148,50 @@
return self;
}
+ (void)walkNodeTree:(IJSVGNode*)node
handler:(IJSVGNodeWalkHandler)handler
{
BOOL allowChildNodes = YES;
BOOL stop = NO;
[self _walkNodeTree:node
handler:handler
allowChildNodes:&allowChildNodes
stop:&stop];
}
+ (void)_walkNodeTree:(IJSVGNode*)node
handler:(IJSVGNodeWalkHandler)handler
allowChildNodes:(BOOL*)allowChildNodes
stop:(BOOL*)stop
{
// run the handler and instantly stop
// if stop is set
handler(node, allowChildNodes, stop);
if(*stop == YES) {
return;
}
// child nodes only work for nodes
// that are type group
if(*allowChildNodes == NO ||
[node isKindOfClass:IJSVGGroup.class] == NO) {
*allowChildNodes = YES;
return;
}
// iterate over the childnodes
IJSVGGroup* group = (IJSVGGroup*)node;
for(IJSVGNode* childNode in group.childNodes) {
[self _walkNodeTree:childNode
handler:handler
allowChildNodes:allowChildNodes
stop:stop];
if(*stop == YES) {
return;
}
}
}
- (void)applyPropertiesFromNode:(IJSVGNode*)node
{
self.title = node.title;
@@ -205,6 +237,7 @@
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));
@@ -243,9 +276,10 @@
self.units = IJSVGUnitInherit;
self.blendMode = IJSVGBlendModeNormal;
self.overflowVisibility = IJSVGOverflowVisibilityVisible;
if (flag) {
def = [[IJSVGDef alloc] init];
if (flag == YES) {
_def = [[IJSVGDef alloc] init];
}
}
return self;
@@ -254,146 +288,146 @@
- (IJSVGDef*)defForID:(NSString*)anID
{
IJSVGDef* aDef = nil;
if ((aDef = [def defForID:anID]) != nil) {
if ((aDef = [_def defForID:anID]) != nil) {
return aDef;
}
if (parentNode != nil) {
return [parentNode defForID:anID];
if (_parentNode != nil) {
return [_parentNode defForID:anID];
}
return nil;
}
- (void)addDef:(IJSVGNode*)aDef
{
[def addDef:aDef];
[_def addDef:aDef];
}
// 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;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (NSColor*)strokeColor
{
if (strokeColor != nil)
return strokeColor;
if (strokeColor == nil && parentNode != nil)
return parentNode.strokeColor;
if (_strokeColor != nil)
return _strokeColor;
if (_strokeColor == nil && _parentNode != nil)
return _parentNode.strokeColor;
return nil;
}
- (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
{
if (fillColor == nil && parentNode != nil) {
return parentNode.fillColor;
if (_fillColor == nil && _parentNode != nil) {
return _parentNode.fillColor;
}
return fillColor;
return _fillColor;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGGradient*)fillGradient
{
if (fillGradient == nil && parentNode != nil) {
return parentNode.fillGradient;
if (_fillGradient == nil && _parentNode != nil) {
return _parentNode.fillGradient;
}
return fillGradient;
return _fillGradient;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGPattern*)fillPattern
{
if (fillPattern == nil && parentNode != nil) {
return parentNode.fillPattern;
if (_fillPattern == nil && _parentNode != nil) {
return _parentNode.fillPattern;
}
return fillPattern;
return _fillPattern;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGGradient*)strokeGradient
{
if (strokeGradient == nil && parentNode != nil) {
return parentNode.strokeGradient;
if (_strokeGradient == nil && _parentNode != nil) {
return _parentNode.strokeGradient;
}
return strokeGradient;
return _strokeGradient;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGPattern*)strokePattern
{
if (strokePattern == nil && parentNode != nil) {
return parentNode.strokePattern;
if (_strokePattern == nil && _parentNode != nil) {
return _parentNode.strokePattern;
}
return strokePattern;
return _strokePattern;
}
@end
@@ -6,8 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGBezierPathAdditions.h"
#import "IJSVGNode.h"
#import <IJSVG/IJSVGNode.h>
#import <Foundation/Foundation.h>
@class IJSVGGroup;
@@ -23,15 +22,14 @@ 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;
@property (nonatomic, assign, readonly) BOOL isStroked;
- (void)close;
- (NSPoint)currentPoint;
+30 -26
View File
@@ -11,18 +11,11 @@
@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,7 +23,7 @@
{
if ((self = [super init]) != nil) {
_primitiveType = kIJSVGPrimitivePathTypePath;
_path = NSBezierPath.bezierPath.retain;
_path = CGPathCreateMutable();
}
return self;
}
@@ -38,34 +31,45 @@
- (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
- (BOOL)isStroked
{
if (_CGPath != nil) {
CGPathRelease(_CGPath);
}
_CGPath = nil;
}
- (CGPathRef)CGPath
{
if (_CGPath == nil) {
_CGPath = [_path newCGPathRef:NO];
}
return _CGPath;
return (self.strokeColor != nil && self.strokeColor.alphaComponent != 0.f) ||
self.strokePattern != nil ||
self.strokeGradient != nil;
}
@end
@@ -6,8 +6,8 @@
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import "IJSVGGroup.h"
#import "IJSVGImage.h"
#import <IJSVG/IJSVGGroup.h>
#import <IJSVG/IJSVGImage.h>
#import <Foundation/Foundation.h>
@interface IJSVGPattern : IJSVGGroup {
@@ -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
@@ -7,33 +7,28 @@
//
#import "IJSVGRadialGradient.h"
#import "IJSVGParser.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;
(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;
grad.fx = _fx;
grad.fy = _fy;
grad.cx = _cx;
grad.cy = _cy;
grad.radius = _radius;
return grad;
}
@@ -41,9 +36,10 @@
gradient:(IJSVGRadialGradient*)gradient
{
// cx defaults to 50% if not specified
NSDictionary* kv = @{ @"cx" : @"cx",
@"cy" : @"cy",
@"r" : @"radius" };
NSDictionary* kv = @{
IJSVGAttributeCX : @"cx",
IJSVGAttributeCY : @"cy",
IJSVGAttributeR : @"radius" };
for (NSString* key in kv.allKeys) {
NSString* str = [element attributeForName:key].stringValue;
@@ -64,13 +60,13 @@
gradient.fy = gradient.cy;
// needs fixing
NSString* fx = [element attributeForName:@"fx"].stringValue;
NSString* fx = [element attributeForName:IJSVGAttributeFX].stringValue;
if (fx != nil) {
gradient.fx = [IJSVGUnitLength unitWithString:fx
fromUnitType:gradient.units];
}
NSString* fy = [element attributeForName:@"fy"].stringValue;
NSString* fy = [element attributeForName:IJSVGAttributeFY].stringValue;
if (fx != nil) {
gradient.fy = [IJSVGUnitLength unitWithString:fy
fromUnitType:gradient.units];
@@ -81,7 +77,9 @@
}
NSArray* colors = nil;
CGFloat* colorStops = [self.class computeColorStopsFromString:element colors:&colors];
CGFloat* colorStops = [self.class computeColorStops:gradient
colors:&colors];
NSGradient* ret = [[[NSGradient alloc] initWithColors:colors
atLocations:colorStops
colorSpace:IJSVGColor.defaultColorSpace] autorelease];
@@ -112,15 +110,15 @@
}
// compute size based on percentages
CGFloat x = [self.cx computeValue:CGRectGetWidth(boundingBox)];
CGFloat y = [self.cy computeValue:CGRectGetHeight(boundingBox)];
CGFloat x = [_cx computeValue:CGRectGetWidth(boundingBox)];
CGFloat y = [_cy computeValue:CGRectGetHeight(boundingBox)];
startPoint = CGPointMake(x, y);
CGFloat val = MIN(CGRectGetWidth(boundingBox),
CGRectGetHeight(boundingBox));
radius = [self.radius computeValue:val];
radius = [_radius computeValue:val];
CGFloat ex = [self.fx computeValue:CGRectGetWidth(boundingBox)];
CGFloat ey = [self.fy computeValue:CGRectGetHeight(boundingBox)];
CGFloat ex = [_fx computeValue:CGRectGetWidth(boundingBox)];
CGFloat ey = [_fy computeValue:CGRectGetHeight(boundingBox)];
gradientEndPoint = CGPointMake(ex, ey);
gradientStartPoint = startPoint;
@@ -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,66 +6,74 @@
// 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 "IJSVGUnitRect.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/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 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 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 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;
@@ -85,13 +93,10 @@ static NSString const* IJSVGAttributePoints = @"points";
@interface IJSVGParser : IJSVGGroup {
NSRect viewBox;
IJSVGUnitSize* intrinsicSize;
@private
id<IJSVGParserDelegate> _delegate;
NSXMLDocument* _document;
NSMutableArray<IJSVGNode*>* _glyphs;
NSMutableArray<IJSVGPath*>* _glyphs;
IJSVGStyleSheet* _styleSheet;
NSMutableDictionary<NSString*, NSXMLElement*>* _defNodes;
NSMutableDictionary<NSString*, NSXMLElement*>* _baseDefNodes;
@@ -9,10 +9,56 @@
#import "IJSVG.h"
#import "IJSVGParser.h"
@implementation IJSVGParser
NSString* const IJSVGAttributeViewBox = @"viewBox";
NSString* const IJSVGAttributeID = @"id";
NSString* const IJSVGAttributeClass = @"class";
NSString* const IJSVGAttributeX = @"x";
NSString* const IJSVGAttributeY = @"y";
NSString* const IJSVGAttributeWidth = @"width";
NSString* const IJSVGAttributeHeight = @"height";
NSString* const IJSVGAttributeOpacity = @"opacity";
NSString* const IJSVGAttributeStrokeOpacity = @"stroke-opacity";
NSString* const IJSVGAttributeStrokeWidth = @"stroke-width";
NSString* const IJSVGAttributeStrokeDashOffset = @"stroke-dashoffset";
NSString* const IJSVGAttributeFillOpacity = @"fill-opacity";
NSString* const IJSVGAttributeClipPath = @"clip-path";
NSString* const IJSVGAttributeMask = @"mask";
NSString* const IJSVGAttributeGradientUnits = @"gradientUnits";
NSString* const IJSVGAttributeMaskUnits = @"maskUnits";
NSString* const IJSVGAttributeMaskContentUnits = @"maskContentUnits";
NSString* const IJSVGAttributeTransform = @"transform";
NSString* const IJSVGAttributeGradientTransform = @"gradientTransform";
NSString* const IJSVGAttributeUnicode = @"unicode";
NSString* const IJSVGAttributeStrokeLineCap = @"stroke-linecap";
NSString* const IJSVGAttributeLineJoin = @"stroke-linejoin";
NSString* const IJSVGAttributeStroke = @"stroke";
NSString* const IJSVGAttributeStrokeDashArray = @"stroke-dasharray";
NSString* const IJSVGAttributeFill = @"fill";
NSString* const IJSVGAttributeFillRule = @"fill-rule";
NSString* const IJSVGAttributeBlendMode = @"mix-blend-mode";
NSString* const IJSVGAttributeDisplay = @"display";
NSString* const IJSVGAttributeStyle = @"style";
NSString* const IJSVGAttributeD = @"d";
NSString* const IJSVGAttributeXLink = @"xlink:href";
NSString* const IJSVGAttributeX1 = @"x1";
NSString* const IJSVGAttributeX2 = @"x2";
NSString* const IJSVGAttributeY1 = @"y1";
NSString* const IJSVGAttributeY2 = @"y2";
NSString* const IJSVGAttributeRX = @"rx";
NSString* const IJSVGAttributeRY = @"ry";
NSString* const IJSVGAttributeCX = @"cx";
NSString* const IJSVGAttributeCY = @"cy";
NSString* const IJSVGAttributeR = @"r";
NSString* const IJSVGAttributeFX = @"fx";
NSString* const IJSVGAttributeFY = @"fy";
NSString* const IJSVGAttributePoints = @"points";
NSString* const IJSVGAttributeOffset = @"offset";
NSString* const IJSVGAttributeStopColor = @"stop-color";
NSString* const IJSVGAttributeStopOpacity = @"stop-opacity";
NSString* const IJSVGAttributeHref = @"href";
NSString* const IJSVGAttributeOverflow = @"overflow";
@synthesize viewBox;
@synthesize intrinsicSize = _intrinsicSize;
@implementation IJSVGParser
static NSDictionary* _IJSVGAttributeDictionaryFloats = nil;
static NSDictionary* _IJSVGAttributeDictionaryNodes = nil;
@@ -22,25 +68,25 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
+ (void)load
{
_IJSVGAttributeDictionaryFloats = [@{
(NSString*)IJSVGAttributeX : @"x",
(NSString*)IJSVGAttributeY : @"y",
(NSString*)IJSVGAttributeWidth : @"width",
(NSString*)IJSVGAttributeHeight : @"height",
(NSString*)IJSVGAttributeOpacity : @"opacity",
(NSString*)IJSVGAttributeStrokeOpacity : @"strokeOpacity",
(NSString*)IJSVGAttributeStrokeWidth : @"strokeWidth",
(NSString*)IJSVGAttributeStrokeDashOffset : @"strokeDashOffset",
(NSString*)IJSVGAttributeFillOpacity : @"fillOpacity" } retain];
IJSVGAttributeX : @"x",
IJSVGAttributeY : @"y",
IJSVGAttributeWidth : @"width",
IJSVGAttributeHeight : @"height",
IJSVGAttributeOpacity : @"opacity",
IJSVGAttributeStrokeOpacity : @"strokeOpacity",
IJSVGAttributeStrokeWidth : @"strokeWidth",
IJSVGAttributeStrokeDashOffset : @"strokeDashOffset",
IJSVGAttributeFillOpacity : @"fillOpacity" } retain];
_IJSVGAttributeDictionaryNodes = [@{
(NSString*)IJSVGAttributeClipPath : @"clipPath",
(NSString*)IJSVGAttributeMask : @"mask" } retain];
IJSVGAttributeClipPath : @"clipPath",
IJSVGAttributeMask : @"mask" } retain];
_IJSVGAttributeDictionaryUnits = [@{
(NSString*)IJSVGAttributeGradientUnits : @"units",
(NSString*)IJSVGAttributeMaskUnits : @"units",
(NSString*)IJSVGAttributeMaskContentUnits : @"contentUnits"} retain];
IJSVGAttributeGradientUnits : @"units",
IJSVGAttributeMaskUnits : @"units",
IJSVGAttributeMaskContentUnits : @"contentUnits"} retain];
_IJSVGAttributeDictionaryTransforms = [@{
(NSString*)IJSVGAttributeTransform : @"transforms",
(NSString*)IJSVGAttributeGradientTransform : @"transforms" } retain];
IJSVGAttributeTransform : @"transforms",
IJSVGAttributeGradientTransform : @"transforms" } retain];
}
+ (IJSVGParser*)groupForFileURL:(NSURL*)aURL
@@ -206,7 +252,7 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
- (NSSize)size
{
return viewBox.size;
return _viewBox.size;
}
- (void)_parse
@@ -220,35 +266,38 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
// find the sizebox!
NSXMLNode* attribute = nil;
if ((attribute = [svgElement attributeForName:(NSString*)IJSVGAttributeViewBox]) != nil) {
if ((attribute = [svgElement attributeForName:IJSVGAttributeViewBox]) != nil) {
// we have a viewbox...
CGFloat* box = [IJSVGUtils parseViewBox:[attribute stringValue]];
viewBox = NSMakeRect(box[0], box[1], box[2], box[3]);
free(box);
CGFloat* box = [IJSVGUtils parseViewBox:attribute.stringValue];
_viewBox = NSMakeRect(box[0], box[1], box[2], box[3]);
(void)free(box);
} else {
// there is no view box so find the width and height
NSString* wAtt = [svgElement attributeForName:(NSString*)IJSVGAttributeWidth].stringValue;
NSString* hAtt = [svgElement attributeForName:(NSString*)IJSVGAttributeHeight].stringValue;
NSString* wAtt = [svgElement attributeForName:IJSVGAttributeWidth].stringValue;
NSString* hAtt = [svgElement attributeForName:IJSVGAttributeHeight].stringValue;
IJSVGUnitLength* wLength = [IJSVGUnitLength unitWithString:wAtt];
IJSVGUnitLength* hLength = [IJSVGUnitLength unitWithString:hAtt];
CGFloat w = wLength.value;
CGFloat h = hLength.value;
// its possible wlength or hlength are nil
CGFloat w = wLength ? wLength.value : 0.f;
CGFloat h = hLength ? hLength.value : 0.f;
if (h == 0.f && w != 0.f) {
h = w;
} else if (w == 0.f && h != 0.f) {
w = h;
}
viewBox = NSMakeRect(0.f, 0.f, w, h);
_viewBox = NSMakeRect(0.f, 0.f, w, h);
}
// parse the width and height....
NSString* w = [svgElement attributeForName:(NSString*)IJSVGAttributeWidth].stringValue;
NSString* h = [svgElement attributeForName:(NSString*)IJSVGAttributeHeight].stringValue;
NSString* w = [svgElement attributeForName:IJSVGAttributeWidth].stringValue;
NSString* h = [svgElement attributeForName:IJSVGAttributeHeight].stringValue;
// by default just the the width and height from the viewbox unless
// specified otherwise
IJSVGUnitLength* wl = [IJSVGUnitLength unitWithFloat:viewBox.size.width];
IJSVGUnitLength* hl = [IJSVGUnitLength unitWithFloat:viewBox.size.height];
IJSVGUnitLength* wl = [IJSVGUnitLength unitWithFloat:_viewBox.size.width];
IJSVGUnitLength* hl = [IJSVGUnitLength unitWithFloat:_viewBox.size.height];
if (w != nil) {
wl = [IJSVGUnitLength unitWithString:w];
}
@@ -432,7 +481,6 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
if (fillDefID != nil) {
// find the object
id obj = [self definedObjectForID:fillDefID];
// what type is it?
if ([obj isKindOfClass:[IJSVGGradient class]]) {
node.fillGradient = (IJSVGGradient*)obj;
@@ -469,6 +517,34 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
}
});
// offset
attr(IJSVGAttributeOffset, ^(NSString* value) {
node.offset = [IJSVGUnitLength unitWithString:value];
});
// stop-opacity
attr(IJSVGAttributeStopOpacity, ^(NSString* value) {
node.fillOpacity = [IJSVGUnitLength unitWithString:value];
});
// stop-color
attr(IJSVGAttributeStopColor, ^(NSString* value) {
node.fillColor = [IJSVGColor colorFromString:value];
if(node.fillOpacity.value != 1.f) {
node.fillColor = [IJSVGColor changeAlphaOnColor:node.fillColor
to:node.fillOpacity.value];
}
});
// overflow
attr(IJSVGAttributeOverflow, ^(NSString* value) {
if([value.lowercaseString isEqualToString:@"hidden"]) {
node.overflowVisibility = IJSVGOverflowVisibilityHidden;
} else {
node.overflowVisibility = IJSVGOverflowVisibilityVisible;
}
});
// is there a title or desc?
for(NSXMLElement* childElement in element.children) {
IJSVGNodeType type = [IJSVGNode typeForString:childElement.localName
@@ -544,7 +620,7 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
return svgs;
}
- (void)addGlyph:(IJSVGNode*)glyph
- (void)addGlyph:(IJSVGPath*)glyph
{
if (_glyphs == nil) {
_glyphs = [[NSMutableArray alloc] init];
@@ -566,9 +642,17 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
switch (node.type) {
// mask
case IJSVGNodeTypeMask: {
node.overflowVisibility = IJSVGOverflowVisibilityHidden;
node.units = IJSVGUnitObjectBoundingBox;
break;
}
// clippath
case IJSVGNodeTypeClipPath: {
node.units = IJSVGUnitObjectBoundingBox;
node.overflowVisibility = IJSVGOverflowVisibilityHidden;
break;
}
// gradient
case IJSVGNodeTypeRadialGradient:
@@ -622,7 +706,7 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
if (_baseDefNodes == nil) {
_baseDefNodes = [[NSMutableDictionary alloc] init];
}
NSString* defID = [childDef attributeForName:@"id"].stringValue;
NSString* defID = [childDef attributeForName:IJSVGAttributeID].stringValue;
if (defID != nil) {
_baseDefNodes[defID] = childDef;
}
@@ -676,8 +760,8 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
// if its a sub svg, we can remove the attributes for x and y
// this is required or it could go out of bounds before the exporter
// hits the layers from the groups :)
[element removeAttributeForName:@"x"];
[element removeAttributeForName:@"y"];
[element removeAttributeForName:IJSVGAttributeX];
[element removeAttributeForName:IJSVGAttributeY];
// work out the SVG
NSError* error = nil;
@@ -709,8 +793,8 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
case IJSVGNodeTypeGlyph: {
// no path data
if ([element attributeForName:(NSString*)IJSVGAttributeD] == nil ||
[[element attributeForName:(NSString*)IJSVGAttributeD] stringValue].length == 0) {
if ([element attributeForName:IJSVGAttributeD] == nil ||
[[element attributeForName:IJSVGAttributeD] stringValue].length == 0) {
break;
}
@@ -726,14 +810,14 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
ignoreAttributes:nil];
// pass the commands for it
[self _parsePathCommandData:[[element attributeForName:(NSString*)IJSVGAttributeD] stringValue]
[self _parsePathCommandData:[[element attributeForName:IJSVGAttributeD] stringValue]
intoPath:path];
// check the size...
if (NSIsEmptyRect([path path].controlPointBounds)) {
if (CGRectIsEmpty(path.controlPointBoundingBox) == YES) {
break;
}
// add the glyph
[self addGlyph:path];
break;
@@ -791,7 +875,7 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
[self _parseElementForCommonAttributes:element
node:path
ignoreAttributes:nil];
[self _parsePathCommandData:[[element attributeForName:(NSString*)IJSVGAttributeD] stringValue]
[self _parsePathCommandData:[[element attributeForName:IJSVGAttributeD] stringValue]
intoPath:path];
[parentGroup addDef:path];
@@ -860,7 +944,7 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
[self _setupDefaultsForNode:path];
[self _parseElementForCommonAttributes:element
node:path
ignoreAttributes:@[ @"x", @"y" ]];
ignoreAttributes:@[ IJSVGAttributeX, IJSVGAttributeY ]];
[parentGroup addDef:path];
break;
}
@@ -943,7 +1027,7 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
}
// due to this being a carbon clone, we need to clear the ID
if ([element attributeForName:(NSString*)IJSVGAttributeID] == nil) {
if ([element attributeForName:IJSVGAttributeID] == nil) {
node.identifier = nil;
}
@@ -955,8 +1039,8 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
node.intermediateParentNode = subGroup;
// is there a width and height?
CGFloat x = [element attributeForName:(NSString*)IJSVGAttributeX].stringValue.floatValue;
CGFloat y = [element attributeForName:(NSString*)IJSVGAttributeY].stringValue.floatValue;
CGFloat x = [element attributeForName:IJSVGAttributeX].stringValue.floatValue;
CGFloat y = [element attributeForName:IJSVGAttributeY].stringValue.floatValue;
// we need to add a transform to the subgroup
subGroup.transforms = @[ [IJSVGTransform transformByTranslatingX:x y:y] ];
@@ -969,12 +1053,25 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
// says ignore x, y, width, height and xlink:href...
[self _parseElementForCommonAttributes:element
node:node
ignoreAttributes:@[ @"x", @"y", @"width",
@"height", @"xlink:href" ]];
ignoreAttributes:@[IJSVGAttributeX, IJSVGAttributeY,
IJSVGAttributeWidth, IJSVGAttributeHeight,
IJSVGAttributeXLink]];
[parentGroup addDef:node];
break;
}
// stop color
case IJSVGNodeTypeStop: {
IJSVGNode* node = [[[IJSVGNode alloc] init] autorelease];
node.type = IJSVGNodeTypeStop;
[self _setupDefaultsForNode:node];
[self _parseElementForCommonAttributes:element
node:node
ignoreAttributes:nil];
[parentGroup addChild:node];
break;
}
// linear gradient
case IJSVGNodeTypeLinearGradient: {
@@ -995,6 +1092,9 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
[self _parseElementForCommonAttributes:elementCopy
node:grad
ignoreAttributes:nil];
[self _parseBlock:elementCopy
intoGroup:grad
def:NO];
grad.gradient = [IJSVGLinearGradient parseGradient:elementCopy
gradient:grad];
[parentGroup addDef:grad];
@@ -1007,6 +1107,9 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
[self _parseElementForCommonAttributes:element
node:gradient
ignoreAttributes:nil];
[self _parseBlock:element
intoGroup:gradient
def:NO];
gradient.gradient = [IJSVGLinearGradient parseGradient:element
gradient:gradient];
[parentGroup addDef:gradient];
@@ -1032,6 +1135,9 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
[self _parseElementForCommonAttributes:elementCopy
node:grad
ignoreAttributes:nil];
[self _parseBlock:elementCopy
intoGroup:grad
def:NO];
grad.gradient = [IJSVGRadialGradient parseGradient:elementCopy
gradient:grad];
[parentGroup addDef:grad];
@@ -1044,6 +1150,9 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
[self _parseElementForCommonAttributes:element
node:gradient
ignoreAttributes:nil];
[self _parseBlock:element
intoGroup:gradient
def:NO];
gradient.gradient = [IJSVGRadialGradient parseGradient:element
gradient:gradient];
[parentGroup addDef:gradient];
@@ -1105,8 +1214,9 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
ignoreAttributes:nil];
// from base64
NSXMLNode* attributeNode = [self resolveXLinkAttributeForElement:element];
[image loadFromBase64EncodedString:attributeNode.stringValue];
NSXMLNode* attributeNode = [self resolveXLinkAttributeForElement:element] ?:
[element attributeForName:IJSVGAttributeHref];
[image loadFromString:attributeNode.stringValue];
// add to parent
[parentGroup addChild:image];
@@ -1119,10 +1229,10 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
- (NSXMLNode*)resolveXLinkAttributeForElement:(NSXMLElement*)element
{
NSString* const namespaceURI = @"http://www.w3.org/1999/xlink";
NSXMLNode* attributeNode = [element attributeForLocalName:@"href"
NSXMLNode* attributeNode = [element attributeForLocalName:IJSVGAttributeHref
URI:namespaceURI];
if (attributeNode == nil) {
attributeNode = [element attributeForName:(NSString*)IJSVGAttributeXLink];
attributeNode = [element attributeForName:IJSVGAttributeXLink];
}
return attributeNode;
}
@@ -1254,10 +1364,10 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
// convert a line into a command,
// basically MX1 Y1LX2 Y2
path.primitiveType = kIJSVGPrimitivePathTypeLine;
CGFloat x1 = [element attributeForName:(NSString*)IJSVGAttributeX1].stringValue.floatValue;
CGFloat y1 = [element attributeForName:(NSString*)IJSVGAttributeY1].stringValue.floatValue;
CGFloat x2 = [element attributeForName:(NSString*)IJSVGAttributeX2].stringValue.floatValue;
CGFloat y2 = [element attributeForName:(NSString*)IJSVGAttributeY2].stringValue.floatValue;
CGFloat x1 = [element attributeForName:IJSVGAttributeX1].stringValue.floatValue;
CGFloat y1 = [element attributeForName:IJSVGAttributeY1].stringValue.floatValue;
CGFloat x2 = [element attributeForName:IJSVGAttributeX2].stringValue.floatValue;
CGFloat y2 = [element attributeForName:IJSVGAttributeY2].stringValue.floatValue;
// use sprintf as its quicker then stringWithFormat...
char* buffer;
@@ -1271,23 +1381,27 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
intoPath:(IJSVGPath*)path
{
path.primitiveType = kIJSVGPrimitivePathTypeCircle;
CGFloat cX = [element attributeForName:(NSString*)IJSVGAttributeCX].stringValue.floatValue;
CGFloat cY = [element attributeForName:(NSString*)IJSVGAttributeCY].stringValue.floatValue;
CGFloat r = [element attributeForName:(NSString*)IJSVGAttributeR].stringValue.floatValue;
NSRect rect = NSMakeRect(cX - r, cY - r, r * 2, r * 2);
path.path = [NSBezierPath bezierPathWithOvalInRect:rect];
CGFloat cX = [element attributeForName:IJSVGAttributeCX].stringValue.floatValue;
CGFloat cY = [element attributeForName:IJSVGAttributeCY].stringValue.floatValue;
CGFloat r = [element attributeForName:IJSVGAttributeR].stringValue.floatValue;
CGRect rect = CGRectMake(cX - r, cY - r, r * 2, r * 2);
CGPathRef nPath = CGPathCreateWithEllipseInRect(rect, NULL);
path.path = (CGMutablePathRef)nPath;
CGPathRelease(nPath);
}
- (void)_parseEllipse:(NSXMLElement*)element
intoPath:(IJSVGPath*)path
{
path.primitiveType = kIJSVGPrimitivePathTypeEllipse;
CGFloat cX = [element attributeForName:(NSString*)IJSVGAttributeCX].stringValue.floatValue;
CGFloat cY = [element attributeForName:(NSString*)IJSVGAttributeCY].stringValue.floatValue;
CGFloat rX = [element attributeForName:(NSString*)IJSVGAttributeRX].stringValue.floatValue;
CGFloat rY = [element attributeForName:(NSString*)IJSVGAttributeRY].stringValue.floatValue;
CGFloat cX = [element attributeForName:IJSVGAttributeCX].stringValue.floatValue;
CGFloat cY = [element attributeForName:IJSVGAttributeCY].stringValue.floatValue;
CGFloat rX = [element attributeForName:IJSVGAttributeRX].stringValue.floatValue;
CGFloat rY = [element attributeForName:IJSVGAttributeRY].stringValue.floatValue;
NSRect rect = NSMakeRect(cX - rX, cY - rY, rX * 2, rY * 2);
path.path = [NSBezierPath bezierPathWithOvalInRect:rect];
CGPathRef nPath = CGPathCreateWithEllipseInRect(rect, NULL);
path.path = (CGMutablePathRef)nPath;
CGPathRelease(nPath);
}
- (void)_parsePolyline:(NSXMLElement*)element
@@ -1312,7 +1426,7 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
intoPath:(IJSVGPath*)path
closePath:(BOOL)closePath
{
NSString* points = [element attributeForName:(NSString*)IJSVGAttributePoints].stringValue;
NSString* points = [element attributeForName:IJSVGAttributePoints].stringValue;
NSInteger count = 0;
CGFloat* params = [IJSVGUtils commandParameters:points
count:&count];
@@ -1323,13 +1437,13 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
return;
}
const int defBufferSize = 5;
const int defBufferSize = 10;
char* buffer;
asprintf(&buffer, "M%f %f L", params[0], params[1]);
// compute a default buffer
size_t bSize = strlen(buffer);
size_t strLength = bSize;
// compute a default buffer - bSize is strlen + 1 for null byte
size_t bSize = strlen(buffer) + 1;
size_t strLength = bSize - 1;
// for every pair of coordinates
for(int i = 2; i < count; i+= 2) {
@@ -1338,19 +1452,22 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
size_t sSize = strlen(subbuf);
// if the new size of the string is large than the buffer
// increase the buffer up another def size
if((strLength + sSize + 1) > bSize) {
// increase the buffer up another def size - note, we always
// plus 2 incase the close path needs to be appended on the end
if((strLength + sSize + 2) > bSize) {
size_t nLength = MAX(sSize, defBufferSize) + 2;
buffer = realloc(buffer, sizeof(char)*(bSize+nLength));
bSize += nLength;
}
// append thr string onto the buffer, increment the
// append the string onto the buffer, increment the
// string length and free the subbuffer memory
strcat(buffer, subbuf);
strLength += sSize;
(void)free(subbuf), subbuf = NULL;
}
// append the close path if required
if(closePath == YES) {
strcat(buffer, "z");
}
@@ -1369,27 +1486,28 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil;
{
path.primitiveType = kIJSVGPrimitivePathTypeRect;
// width and height
CGFloat width = [IJSVGUtils floatValue:[element attributeForName:(NSString*)IJSVGAttributeWidth].stringValue
CGFloat width = [IJSVGUtils floatValue:[element attributeForName:IJSVGAttributeWidth].stringValue
fallBackForPercent:self.viewBox.size.width];
CGFloat height = [IJSVGUtils floatValue:[element attributeForName:(NSString*)IJSVGAttributeHeight].stringValue
CGFloat height = [IJSVGUtils floatValue:[element attributeForName:IJSVGAttributeHeight].stringValue
fallBackForPercent:self.viewBox.size.height];
// rect uses x and y as start of path, not move path object -_-
CGFloat x = [IJSVGUtils floatValue:[element attributeForName:(NSString*)IJSVGAttributeX].stringValue
CGFloat x = [IJSVGUtils floatValue:[element attributeForName:IJSVGAttributeX].stringValue
fallBackForPercent:self.viewBox.size.width];
CGFloat y = [IJSVGUtils floatValue:[element attributeForName:(NSString*)IJSVGAttributeY].stringValue
CGFloat y = [IJSVGUtils floatValue:[element attributeForName:IJSVGAttributeY].stringValue
fallBackForPercent:self.viewBox.size.height];
// radius
CGFloat rX = [element attributeForName:(NSString*)IJSVGAttributeRX].stringValue.floatValue;
CGFloat rY = [element attributeForName:(NSString*)IJSVGAttributeRY].stringValue.floatValue;
if ([element attributeForName:(NSString*)IJSVGAttributeRY] == nil) {
CGFloat rX = [element attributeForName:IJSVGAttributeRX].stringValue.floatValue;
CGFloat rY = [element attributeForName:IJSVGAttributeRY].stringValue.floatValue;
if ([element attributeForName:IJSVGAttributeRY] == nil) {
rY = rX;
}
path.path = [NSBezierPath bezierPathWithRoundedRect:NSMakeRect(x, y, width, height)
xRadius:rX
yRadius:rY];
CGRect rect = CGRectMake(x, y, width, height);
CGPathRef nPath = CGPathCreateWithRoundedRect(rect, rX, rY, NULL);
path.path = (CGMutablePathRef)nPath;
CGPathRelease(nPath);
}
@end
@@ -6,8 +6,8 @@
// 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;
@@ -26,7 +26,6 @@
@implementation IJSVGLayerTree
@synthesize viewBox;
@synthesize style = _style;
- (void)dealloc
@@ -74,59 +73,40 @@
{
// any x and y?
CGFloat x = [node.x computeValue:layer.frame.size.width];
CGFloat y = [node.y computeValue:layer.frame.size.height];
CGRect frame = layer.bounds;
CGFloat x = [node.x computeValue:frame.size.width];
CGFloat y = [node.y computeValue:frame.size.height];
// do some magic transform
// no need to do anything if no transform, or x or y == 0
if (transforms.count == 0 && x == 0.f && y == 0.f) {
return layer;
}
// simply cascade all the transforms onto the identity
CGAffineTransform identity = CGAffineTransformIdentity;
if (x != 0.f || y != 0.f) {
// we must add translate to the stack
NSMutableArray* trans = nil;
if (transforms != nil) {
trans = [[transforms mutableCopy] autorelease];
} else {
trans = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
}
[trans addObject:[IJSVGTransform transformByTranslatingX:x y:y]];
transforms = trans;
identity = CGAffineTransformTranslate(identity, x, y);
}
// add any transforms
IJSVGLayer* topLayer = nil;
IJSVGLayer* parentLayer = nil;
for (IJSVGTransform* transform in transforms) {
// make sure we apply the transform to the parent
// so they stack
IJSVGGroupLayer* childLayer = [[[IJSVGGroupLayer alloc] init] autorelease];
childLayer.affineTransform = transform.CGAffineTransform;
// add it to the parent layer
if (parentLayer != nil) {
[parentLayer addSublayer:childLayer];
} else {
// make sure we keep track of the top most layer
topLayer = childLayer;
}
// reset parent layer to the new child
parentLayer = childLayer;
// this used to be done with each transform being added to its own
// group layer, but we can simply use one and then apply
// the transforms in reverse order, has same outcome with less memory
IJSVGGroupLayer* parentLayer = [[[IJSVGGroupLayer alloc] init] autorelease];
for(IJSVGTransform* transform in transforms.reverseObjectEnumerator) {
identity = CGAffineTransformConcat(identity, transform.CGAffineTransform);
}
// swap the layer around
parentLayer.affineTransform = identity;
[parentLayer addSublayer:layer];
layer = topLayer;
return layer;
return parentLayer;
}
- (void)applyDefaultsToLayer:(IJSVGLayer*)layer
fromNode:(IJSVGNode*)node
{
CGFloat opacity = node.opacity.value;
layer.opacity = opacity;
if(opacity != 1.f) {
layer.opacity = opacity;
}
// setup the blending mode
if (node.blendMode != IJSVGBlendModeNormal) {
@@ -164,14 +144,15 @@
}
IJSVGGroupLayer* groupLayer = [[[IJSVGGroupLayer alloc] init] autorelease];
for (IJSVGNode* node in group.children) {
for (IJSVGNode* node in group.childNodes) {
[groupLayer addSublayer:[self layerForNode:node]];
}
groupLayer.frame = (CGRect){
groupLayer.frame = (CGRect) {
.origin = CGPointZero,
.size = (CGSize){
.size = (CGSize) {
.width = group.width.value,
.height = group.height.value }
.height = group.height.value
}
};
// mask it - forgot groups can have masks too, doh! simple
@@ -204,7 +185,7 @@
// setup path and layer
IJSVGShapeLayer* layer = [[[IJSVGShapeLayer alloc] init] autorelease];
layer.primitiveType = path.primitiveType;
CGPathRef introPath = path.CGPath;
CGPathRef introPath = path.path;
*originalBoundingBox = CGRectIntegral(CGPathGetBoundingBox(introPath));
layer.originalPathOrigin = (*originalBoundingBox).origin;
@@ -299,9 +280,14 @@
NSColor* fColor = path.fillColor;
BOOL hasColor = (fColor.alphaComponent == 0.f || fColor == nil) == NO;
BOOL hasFill = path.fillPattern != nil || path.fillGradient != nil;
// is there an overriding style in the sheet?
if (_style.fillColor && (hasFill || hasColor || fColor == nil)) {
fColor = _style.fillColor;
} else if (fColor != nil && path.fillOpacity.value != 1.f) {
}
// if there is a color, change the opacity if required
if (fColor != nil && path.fillOpacity.value != 1.f) {
fColor = [IJSVGColor changeAlphaOnColor:fColor
to:path.fillOpacity.value];
}
@@ -442,7 +428,7 @@
// the gradient drawing layer
gradient.colorList = _style.colorList;
IJSVGGradientLayer* gradLayer = [[[IJSVGGradientLayer alloc] init] autorelease];
gradLayer.viewBox = self.viewBox;
gradLayer.viewBox = _viewBox;
gradLayer.frame = layer.bounds;
gradLayer.gradient = gradient;
gradLayer.absoluteTransform = [self absoluteTransform:path];
@@ -603,7 +589,9 @@
// dashing
strokeLayer.lineDashPhase = path.strokeDashOffset.value;
strokeLayer.lineDashPattern = [self lineDashPattern:path];
if(path.strokeDashArrayCount != 0.f) {
strokeLayer.lineDashPattern = [self lineDashPattern:path];
}
return strokeLayer;
}
@@ -616,13 +604,13 @@
IJSVGGroupLayer* maskLayer = [[[IJSVGGroupLayer alloc] init] autorelease];
// add clip mask
if (node.clipPath != nil) {
if (node.clipPath != nil && node.clipPath.overflowVisibility == IJSVGOverflowVisibilityHidden) {
IJSVGLayer* clip = [self layerForNode:node.clipPath];
// adjust the frame
if (node.clipPath.units == IJSVGUnitObjectBoundingBox) {
[self adjustLayer:clip
toParentLayerFrame:layer];
toParentLayerFrame:layer];
}
// add the layer
@@ -630,26 +618,27 @@
}
// add the actual mask
if (node.mask != nil) {
if (node.mask != nil && node.mask.overflowVisibility == IJSVGOverflowVisibilityHidden) {
IJSVGLayer* mask = [self layerForNode:node.mask];
// only move if bounding box
if (node.mask.units == IJSVGUnitObjectBoundingBox) {
[self adjustLayer:mask
toParentLayerFrame:layer];
toParentLayerFrame:layer];
}
// add the layer
[maskLayer addSublayer:mask];
}
// recursive colourize for each item
NSColor* color = [IJSVGColor computeColorSpace:NSColor.whiteColor];
[self _recursiveColorLayersFromLayer:maskLayer
withColor:color.CGColor];
// add the mask
layer.mask = maskLayer;
if(maskLayer.sublayers.count != 0) {
// recursive colourize for each item
NSColor* color = [IJSVGColor computeColorSpace:NSColor.whiteColor];
[self _recursiveColorLayersFromLayer:maskLayer
withColor:color.CGColor];
layer.mask = maskLayer;
}
}
}
@@ -689,54 +678,54 @@
- (NSArray<NSNumber*>*)lineDashPattern:(IJSVGNode*)node
{
NSMutableArray* arr = [[[NSMutableArray alloc] init] autorelease];
NSMutableArray* arr = [[[NSMutableArray alloc] initWithCapacity:node.strokeDashArrayCount] autorelease];
for (NSInteger i = 0; i < node.strokeDashArrayCount; i++) {
[arr addObject:@((CGFloat)node.strokeDashArray[i])];
}
return [[arr copy] autorelease];
return arr;
}
- (NSString*)lineJoin:(IJSVGLineJoinStyle)joinStyle
{
switch (joinStyle) {
default:
case IJSVGLineJoinStyleMiter: {
return kCALineJoinMiter;
}
case IJSVGLineJoinStyleBevel: {
return kCALineJoinBevel;
}
case IJSVGLineJoinStyleRound: {
return kCALineJoinRound;
}
default:
case IJSVGLineJoinStyleMiter: {
return kCALineJoinMiter;
}
case IJSVGLineJoinStyleBevel: {
return kCALineJoinBevel;
}
case IJSVGLineJoinStyleRound: {
return kCALineJoinRound;
}
}
}
- (NSString*)lineCap:(IJSVGLineCapStyle)capStyle
{
switch (capStyle) {
default:
case IJSVGLineCapStyleButt: {
return kCALineCapButt;
}
case IJSVGLineCapStyleRound: {
return kCALineCapRound;
}
case IJSVGLineCapStyleSquare: {
return kCALineCapSquare;
}
default:
case IJSVGLineCapStyleButt: {
return kCALineCapButt;
}
case IJSVGLineCapStyleRound: {
return kCALineCapRound;
}
case IJSVGLineCapStyleSquare: {
return kCALineCapSquare;
}
}
}
- (NSString*)fillRule:(IJSVGWindingRule)rule
{
switch (rule) {
case IJSVGWindingRuleEvenOdd: {
return kCAFillRuleEvenOdd;
}
default: {
return kCAFillRuleNonZero;
}
case IJSVGWindingRuleEvenOdd: {
return kCAFillRuleEvenOdd;
}
default: {
return kCAFillRuleNonZero;
}
}
}
@@ -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;
}
@@ -16,8 +16,6 @@
#define SPECIFICITY_CLASS 10
#define SPECIFICITY_IDENTIFIER 100
@synthesize specificity;
BOOL IJSVGStyleSheetIsSiblingCombinator(IJSVGStyleSheetSelectorCombinator combinator)
{
return combinator == IJSVGStyleSheetSelectorCombinatorNextSibling ||
@@ -43,11 +41,11 @@ IJSVGNode * IJSVGStyleSheetPreviousNode(IJSVGNode * node)
IJSVGGroup * group = (IJSVGGroup *)node.parentNode;
if([group isKindOfClass:[IJSVGGroup class]] == NO)
return nil;
NSInteger currentIndex = [group.children indexOfObject:node];
NSInteger currentIndex = [group.childNodes indexOfObject:node];
if(currentIndex == 0) {
return nil;
}
return group.children[currentIndex-1];
return group.childNodes[currentIndex-1];
};
IJSVGNode * IJSVGStyleSheetNextNode(IJSVGNode * node)
@@ -56,11 +54,11 @@ IJSVGNode * IJSVGStyleSheetNextNode(IJSVGNode * node)
if([group isKindOfClass:[IJSVGGroup class]] == NO) {
return nil;
}
NSInteger currentIndex = [group.children indexOfObject:node];
if(currentIndex == group.children.count-1) {
NSInteger currentIndex = [group.childNodes indexOfObject:node];
if(currentIndex == group.childNodes.count-1) {
return nil;
}
return group.children[currentIndex+1];
return group.childNodes[currentIndex+1];
};
IJSVGStyleSheetSelectorRaw * IJSVGStyleSheetPreviousSelector(IJSVGStyleSheetSelectorRaw * aSelector, NSArray * _rawSelectors)
@@ -149,7 +147,7 @@ BOOL IJSVGStyleSheetMatchSelector(IJSVGNode * node, IJSVGStyleSheetSelectorRaw *
}
// grab the children
NSArray * nodes = parentNode.children;
NSArray * nodes = parentNode.childNodes;
NSInteger index = [nodes indexOfObject:aNode];
// doesnt contain the child
@@ -228,7 +226,7 @@ BOOL IJSVGStyleSheetMatchSelector(IJSVGNode * node, IJSVGStyleSheetSelectorRaw *
// matches the next selector and... contains the node in question
IJSVGStyleSheetSelectorRaw * s = IJSVGStyleSheetNextSelector(aSelector,_rawSelectors);
if(IJSVGStyleSheetMatchSelector(parentNode, s) &&
[parentNode.children containsObject:aNode]) {
[parentNode.childNodes containsObject:aNode]) {
// set the new starting selector and node
aSelector = s;
aNode = parentNode;
@@ -276,15 +274,16 @@ BOOL IJSVGStyleSheetMatchSelector(IJSVGNode * node, IJSVGStyleSheetSelectorRaw *
// 1 for a tag
if(rawSelector.tag != nil) {
self.specificity += SPECIFICITY_TAG;
_specificity += SPECIFICITY_TAG;
}
// 100 for a id
if(rawSelector.identifier != nil)
self.specificity += SPECIFICITY_IDENTIFIER;
if(rawSelector.identifier != nil) {
_specificity += SPECIFICITY_IDENTIFIER;
}
// 10 for a class
self.specificity += (rawSelector.classes.count*SPECIFICITY_CLASS);
_specificity += (rawSelector.classes.count*SPECIFICITY_CLASS);
}
}
@@ -18,14 +18,9 @@ typedef NS_ENUM(NSUInteger, IJSVGStyleSheetSelectorCombinator) {
};
@interface IJSVGStyleSheetSelectorRaw : NSObject {
NSString* tag;
NSString* identifier;
@private
NSMutableArray* classes;
IJSVGStyleSheetSelectorCombinator combinator;
NSString* combinatorString;
}
@property (nonatomic, copy) NSString* tag;
@@ -10,14 +10,14 @@
@implementation IJSVGStyleSheetSelectorRaw
@synthesize classes, identifier, tag, combinator, combinatorString;
@synthesize classes;
- (void)dealloc
{
(void)([classes release]), classes = nil;
(void)([identifier release]), identifier = nil;
(void)([tag release]), tag = nil;
(void)([combinatorString release]), combinatorString = nil;
(void)([_identifier release]), _identifier = nil;
(void)([_tag release]), _tag = nil;
(void)([_combinatorString release]), _combinatorString = nil;
[super dealloc];
}
@@ -25,8 +25,8 @@
{
if ((self = [super init]) != nil) {
classes = [[NSMutableArray alloc] init];
combinator = IJSVGStyleSheetSelectorCombinatorDescendant;
combinatorString = @" ";
_combinator = IJSVGStyleSheetSelectorCombinatorDescendant;
_combinatorString = @" ";
}
return self;
}
@@ -38,7 +38,8 @@
- (NSString*)description
{
return [NSString stringWithFormat:@"Combinator: %@, Tag: %@, Classes: %@, Identifier: %@", combinatorString, tag, classes, identifier];
return [NSString stringWithFormat:@"Combinator: %@, Tag: %@, Classes: %@, Identifier: %@",
_combinatorString, _tag, classes, _identifier];
}
@end
@@ -6,7 +6,7 @@
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import "IJSVGUnitLength.h"
#import <IJSVG/IJSVGUnitLength.h>
@interface IJSVGGradientUnitLength : IJSVGUnitLength
@@ -0,0 +1,25 @@
//
// IJSVGParsing.h
// IJSVG
//
// Created by Curtis Hard on 04/02/2021.
// Copyright © 2021 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef struct {
char* name;
char* parameters;
} IJSVGParsingStringMethod;
IJSVGParsingStringMethod* IJSVGParsingStringMethodCreate(void);
void IJSVGParsingStringMethodRelease(IJSVGParsingStringMethod* stringMethod);
IJSVGParsingStringMethod** IJSVGParsingMethodParseString(const char* string,
NSUInteger* count);
void IJSVGParsingStringMethodsRelease(IJSVGParsingStringMethod** methods,
NSUInteger count);
@interface IJSVGParsing : NSObject
@end
@@ -0,0 +1,127 @@
//
// IJSVGParsing.m
// IJSVG
//
// Created by Curtis Hard on 04/02/2021.
// Copyright © 2021 Curtis Hard. All rights reserved.
//
#import "IJSVGParsing.h"
#import "IJSVGUtils.h"
IJSVGParsingStringMethod* IJSVGParsingStringMethodCreate(void)
{
IJSVGParsingStringMethod* method = (IJSVGParsingStringMethod*)malloc(sizeof(IJSVGParsingStringMethod));
method->name = NULL;
method->parameters = NULL;
return method;
}
void IJSVGParsingStringMethodRelease(IJSVGParsingStringMethod* stringMethod)
{
if(stringMethod->name != NULL) {
(void)free(stringMethod->name), stringMethod->name = NULL;
}
if(stringMethod->parameters != NULL) {
(void)free(stringMethod->parameters), stringMethod->parameters = NULL;
}
if(stringMethod != NULL) {
(void)free(stringMethod), stringMethod = NULL;
}
}
void IJSVGParsingStringMethodsRelease(IJSVGParsingStringMethod** methods,
NSUInteger count)
{
for(int i = 0; i < count; i++) {
IJSVGParsingStringMethodRelease(methods[i]);
}
(void)free(methods), methods = NULL;
}
IJSVGParsingStringMethod** IJSVGParsingMethodParseString(const char* string,
NSUInteger* count)
{
const char* charString = string;
unsigned long length = strlen(string);
char* buffer = (char*)calloc(sizeof(char), length);
char* originBuffer = buffer;
int bufferIndex = 0;
const size_t defBufferSize = 5;
size_t currentBufferSize = defBufferSize;
NSUInteger methodCount = 0;
IJSVGParsingStringMethod* method = NULL;
IJSVGParsingStringMethod** methods = NULL;
methods = (IJSVGParsingStringMethod**)malloc(sizeof(IJSVGParsingStringMethod*)*currentBufferSize);
// each command requires a name and parameters, store for later use
for(int i = 0; i < length; i++) {
char currentChar = *charString++;
// start of params - store the command name as its current in the buffer
if(currentChar == '(') {
// rest the pointer to beginning
buffer = originBuffer;
//write here
if(method == NULL) {
method = IJSVGParsingStringMethodCreate();
method->name = (char*)calloc(sizeof(char),bufferIndex+1);
memcpy(method->name, buffer, sizeof(char)*bufferIndex);
IJSVGTrimCharBuffer(method->name);
}
// write null up until the limit we reached
memset(buffer, '\0', bufferIndex);
bufferIndex = 0;
continue;
}
// end of params - store the params into the buffer
if(currentChar == ')') {
// rest the pointer to beginning
buffer = originBuffer;
// there has to be a method at this point, if not, something is wrong
// in the syntax
if(method != NULL) {
method->parameters = (char*)calloc(sizeof(char),bufferIndex+1);
memcpy(method->parameters, buffer, sizeof(char)*bufferIndex);
IJSVGTrimCharBuffer(method->parameters);
// now we can add
if(methodCount + 1 > currentBufferSize) {
currentBufferSize += defBufferSize;
*methods = *(IJSVGParsingStringMethod**)realloc(methods, sizeof(IJSVGParsingStringMethod*)*currentBufferSize);
}
methods[methodCount++] = method;
method = NULL;
}
// write null up until the limit we reached
memset(buffer, '\0', bufferIndex);
bufferIndex = 0;
continue;
}
// increment the buffer count
*buffer++ = currentChar;
bufferIndex++;
}
// left over
if(method != NULL) {
(void)IJSVGParsingStringMethodRelease(method), method = NULL;
}
buffer = originBuffer;
*count = methodCount;
(void)free(buffer), buffer = NULL;
return methods;
}
@implementation IJSVGParsing
@end
@@ -20,10 +20,12 @@ BOOL IJSVGBeginTransaction(void)
// of the catransaction for background composites
[CATransaction begin];
[CATransaction setDisableActions:YES];
[CATransaction lock];
return YES;
};
void IJSVGEndTransaction(void)
{
[CATransaction unlock];
[CATransaction commit];
};
@@ -6,7 +6,7 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGUtils.h"
#import <IJSVG/IJSVGUtils.h>
#import <Foundation/Foundation.h>
@class IJSVGTransform;
@@ -14,7 +14,7 @@
typedef CGFloat (^IJSVGTransformParameterModifier)(NSInteger index, CGFloat value);
typedef void (^IJSVGTransformApplyBlock)(IJSVGTransform* transform);
typedef NS_OPTIONS(NSInteger, IJSVGTransformCommand) {
typedef NS_ENUM(NSInteger, IJSVGTransformCommand) {
IJSVGTransformCommandMatrix,
IJSVGTransformCommandTranslate,
IJSVGTransformCommandTranslateX,
@@ -27,11 +27,6 @@ typedef NS_OPTIONS(NSInteger, IJSVGTransformCommand) {
};
@interface IJSVGTransform : NSObject {
IJSVGTransformCommand command;
CGFloat* parameters;
NSInteger parameterCount;
NSInteger sort;
}
@property (nonatomic, assign) IJSVGTransformCommand command;
@@ -49,7 +44,6 @@ NSString* IJSVGTransformAttributeString(CGAffineTransform transform);
+ (NSString*)affineTransformToSVGTransformComponentString:(CGAffineTransform)transform;
+ (NSArray<IJSVGTransform*>*)transformsFromAffineTransform:(CGAffineTransform)affineTransform;
+ (NSArray<IJSVGTransform*>*)transformsForString:(NSString*)string;
+ (NSBezierPath*)transformedPath:(IJSVGPath*)path;
+ (NSString*)affineTransformToSVGMatrixString:(CGAffineTransform)affineTransform;
+ (NSString*)affineTransformToSVGMatrixString:(CGAffineTransform)transform
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
@@ -8,28 +8,24 @@
#import "IJSVGMath.h"
#import "IJSVGTransform.h"
#import "IJSVGParsing.h"
@implementation IJSVGTransform
@synthesize command;
@synthesize parameters;
@synthesize parameterCount;
@synthesize sort;
- (void)dealloc
{
(void)free(parameters);
(void)free(_parameters);
[super dealloc];
}
- (id)copyWithZone:(NSZone*)zone
{
IJSVGTransform* trans = [[self.class alloc] init];
trans.command = self.command;
trans.parameters = (CGFloat*)malloc(sizeof(CGFloat) * self.parameterCount);
trans.sort = sort;
trans.parameterCount = self.parameterCount;
memcpy(trans.parameters, self.parameters, sizeof(CGFloat) * self.parameterCount);
trans.command = _command;
trans.parameters = (CGFloat*)malloc(sizeof(CGFloat) * _parameterCount);
trans.sort = _sort;
trans.parameterCount = _parameterCount;
memcpy(trans.parameters, _parameters, sizeof(CGFloat) * _parameterCount);
return trans;
}
@@ -83,19 +79,49 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
- (void)recalculateWithBounds:(CGRect)bounds
{
CGFloat max = bounds.size.width > bounds.size.height ? bounds.size.width : bounds.size.height;
switch (self.command) {
switch (_command) {
case IJSVGTransformCommandRotate: {
if (self.parameterCount == 1) {
if (_parameterCount == 1) {
return;
}
self.parameters[1] = self.parameters[1] * max;
self.parameters[2] = self.parameters[2] * max;
_parameters[1] = _parameters[1] * max;
_parameters[2] = _parameters[2] * max;
}
default:
return;
}
}
+ (IJSVGTransformCommand)commandForCommandCString:(char*)str
{
IJSVGCharBufferToLower(str);
if (strcmp(str, "matrix") == 0) {
return IJSVGTransformCommandMatrix;
}
if (strcmp(str, "translate") == 0) {
return IJSVGTransformCommandTranslate;
}
if (strcmp(str, "translatex") == 0) {
return IJSVGTransformCommandTranslateX;
}
if (strcmp(str, "translatey") == 0) {
return IJSVGTransformCommandTranslateY;
}
if (strcmp(str, "scale") == 0) {
return IJSVGTransformCommandScale;
}
if (strcmp(str, "skewx") == 0) {
return IJSVGTransformCommandSkewX;
}
if (strcmp(str, "skewy") == 0) {
return IJSVGTransformCommandSkewY;
}
if (strcmp(str, "rotate") == 0) {
return IJSVGTransformCommandRotate;
}
return IJSVGTransformCommandNotImplemented;
}
+ (IJSVGTransformCommand)commandForCommandString:(NSString*)str
{
str = str.lowercaseString;
@@ -139,222 +165,74 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
+ (NSArray<IJSVGTransform*>*)transformsForString:(NSString*)string
{
NSMutableArray<IJSVGTransform*>* array = nil;
array = [[[NSMutableArray alloc] init] autorelease];
NSMutableArray<IJSVGTransform*>* transforms = nil;
transforms = [[[NSMutableArray alloc] init] autorelease];
// setup the buffer for the string to be stored in
const char* charString = string.UTF8String;
unsigned long length = strlen(charString);
char* buffer = (char*)calloc(sizeof(char), length);
char* originBuffer = buffer;
int bufferIndex = 0;
// each command requires a name and parameters, store for later use
NSString* commandName = nil;
NSString* params = nil;
for(int i = 0; i < length; i++) {
char currentChar = *charString++;
// start of params - store the command name as its current in the buffer
if(currentChar == '(') {
// rest the pointer to beginning
buffer = originBuffer;
IJSVGTrimCharBuffer(buffer);
commandName = [NSString stringWithUTF8String:buffer];
// write null up until the limit we reached
memset(buffer, '\0', bufferIndex);
bufferIndex = 0;
IJSVGParsingStringMethod** methods = NULL;
NSUInteger count = 0;
methods = IJSVGParsingMethodParseString(charString, &count);
for(int i = 0; i < count; i++) {
IJSVGParsingStringMethod* method = methods[i];
IJSVGTransformCommand commandType;
commandType = [self.class commandForCommandCString:method->name];
if(commandType == IJSVGTransformCommandNotImplemented) {
(void)IJSVGParsingStringMethodRelease(method), method = NULL;
continue;
}
// end of params - store the params into the buffer
if(currentChar == ')') {
// rest the pointer to beginning
buffer = originBuffer;
params = [NSString stringWithUTF8String:buffer];
// write null up until the limit we reached
memset(buffer, '\0', bufferIndex);
bufferIndex = 0;
// at this point we can just add the command
IJSVGTransformCommand commandType = [self.class commandForCommandString:commandName];
if(commandType == IJSVGTransformCommandNotImplemented) {
continue;
}
// create a new transform object and parse the parameters
NSInteger count = 0;
IJSVGTransform* transform = [[[self.class alloc] init] autorelease];
transform.command = commandType;
transform.sort = [self.class sortForTransformCommand:commandType];
transform.parameters = [IJSVGUtils commandParameters:params
count:&count];
transform.parameterCount = count;
// add to the list of transforms to return
[array addObject:transform];
// reset these values
commandName = nil;
params = nil;
continue;
}
// increment the buffer count
*buffer++ = currentChar;
bufferIndex++;
// create a new transform object and parse the parameters
NSInteger count = 0;
IJSVGTransform* transform = [[[self.class alloc] init] autorelease];
transform.command = commandType;
transform.sort = [self.class sortForTransformCommand:commandType];
transform.parameters = [IJSVGUtils scanFloatsFromCString:method->parameters
size:&count];
transform.parameterCount = count;
// add to the list of transforms to return
[transforms addObject:transform];
(void)IJSVGParsingStringMethodRelease(method), method = NULL;
}
buffer = originBuffer;
(void)free(buffer), buffer = NULL;
return array;
}
+ (NSBezierPath*)transformedPath:(IJSVGPath*)path
{
if (path.transforms.count == 0) {
return path.path;
}
NSBezierPath* cop = [[path.path copy] autorelease];
for (IJSVGTransform* transform in path.transforms) {
NSAffineTransform* at = NSAffineTransform.transform;
switch (transform.command) {
// matrix
case IJSVGTransformCommandMatrix: {
at.transformStruct = (NSAffineTransformStruct) {
.m11 = transform.parameters[0],
.m12 = transform.parameters[1],
.m21 = transform.parameters[2],
.m22 = transform.parameters[3],
.tX = transform.parameters[4],
.tY = transform.parameters[5],
};
break;
}
// skewX
case IJSVGTransformCommandSkewX: {
CGFloat degrees = transform.parameters[0];
CGFloat radians = degrees * M_PI / 180.f;
at.transformStruct = (NSAffineTransformStruct) {
.m11 = 1.f,
.m12 = 0.f,
.m21 = tan(radians),
.m22 = 1.f,
.tX = 0.f,
.tY = 0.f
};
break;
}
// skewX
case IJSVGTransformCommandSkewY: {
CGFloat degrees = transform.parameters[0];
CGFloat radians = degrees * M_PI / 180.f;
at.transformStruct = (NSAffineTransformStruct) {
.m11 = 1.f,
.m12 = tan(radians),
.m21 = 0.f,
.m22 = 1.f,
.tX = 0.f,
.tY = 0.f
};
break;
}
// translate
case IJSVGTransformCommandTranslate: {
if (transform.parameterCount == 1)
[at translateXBy:transform.parameters[0]
yBy:0];
else
[at translateXBy:transform.parameters[0]
yBy:transform.parameters[1]];
break;
}
// translateX
case IJSVGTransformCommandTranslateX: {
[at translateXBy:transform.parameters[0] yBy:0.f];
break;
}
// translateY
case IJSVGTransformCommandTranslateY: {
[at translateXBy:0.f yBy:transform.parameters[0]];
break;
}
// scale
case IJSVGTransformCommandScale: {
if (transform.parameterCount == 1)
[at scaleBy:transform.parameters[0]];
else
[at scaleXBy:transform.parameters[0]
yBy:transform.parameters[1]];
break;
}
// rotate
case IJSVGTransformCommandRotate: {
if (transform.parameterCount == 1)
[at rotateByDegrees:transform.parameters[0]];
else {
CGFloat centerX = transform.parameters[1];
CGFloat centerY = transform.parameters[2];
CGFloat angle = transform.parameters[0] * (M_PI / 180.f);
[at translateXBy:centerX
yBy:centerY];
[at rotateByRadians:angle];
[at translateXBy:-1.f * centerX
yBy:-1.f * centerY];
}
break;
}
// do nothing
case IJSVGTransformCommandNotImplemented: {
}
}
[cop transformUsingAffineTransform:at];
}
return cop;
(void)free(methods), methods = NULL;
return transforms;
}
- (CGAffineTransform)CGAffineTransform
{
return [self CGAffineTransformWithModifier:nil];
return [self stackIdentity:CGAffineTransformIdentity];
}
- (CGAffineTransform)stackIdentity:(CGAffineTransform)identity
{
switch (self.command) {
switch (_command) {
// translate
case IJSVGTransformCommandTranslate: {
if (self.parameterCount == 1) {
return CGAffineTransformTranslate(identity, self.parameters[0], 0.f);
if (_parameterCount == 1) {
return CGAffineTransformTranslate(identity, _parameters[0], 0.f);
}
return CGAffineTransformTranslate(identity, self.parameters[0], self.parameters[1]);
return CGAffineTransformTranslate(identity, _parameters[0], _parameters[1]);
}
// translateX
case IJSVGTransformCommandTranslateX: {
return CGAffineTransformTranslate(identity, self.parameters[0], 0.f);
return CGAffineTransformTranslate(identity, _parameters[0], 0.f);
}
// translateY
case IJSVGTransformCommandTranslateY: {
return CGAffineTransformTranslate(identity, 0.f, self.parameters[0]);
return CGAffineTransformTranslate(identity, 0.f, _parameters[0]);
}
// rotate
case IJSVGTransformCommandRotate: {
if (self.parameterCount == 1) {
return CGAffineTransformRotate(identity, (self.parameters[0] / 180) * M_PI);
if (_parameterCount == 1) {
return CGAffineTransformRotate(identity, (_parameters[0] / 180) * M_PI);
}
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
CGFloat p2 = self.parameters[2];
CGFloat p0 = _parameters[0];
CGFloat p1 = _parameters[1];
CGFloat p2 = _parameters[2];
CGFloat angle = p0 * (M_PI / 180.f);
identity = CGAffineTransformTranslate(identity, p1, p2);
@@ -364,35 +242,35 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// scale
case IJSVGTransformCommandScale: {
CGFloat p0 = self.parameters[0];
if (self.parameterCount == 1) {
CGFloat p0 = _parameters[0];
if (_parameterCount == 1) {
return CGAffineTransformScale(identity, p0, p0);
}
CGFloat p1 = self.parameters[1];
CGFloat p1 = _parameters[1];
return CGAffineTransformScale(identity, p0, p1);
}
// matrix
case IJSVGTransformCommandMatrix: {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
CGFloat p2 = self.parameters[2];
CGFloat p3 = self.parameters[3];
CGFloat p4 = self.parameters[4];
CGFloat p5 = self.parameters[5];
CGFloat p0 = _parameters[0];
CGFloat p1 = _parameters[1];
CGFloat p2 = _parameters[2];
CGFloat p3 = _parameters[3];
CGFloat p4 = _parameters[4];
CGFloat p5 = _parameters[5];
return CGAffineTransformMake(p0, p1, p2, p3, p4, p5);
}
// skewX
case IJSVGTransformCommandSkewX: {
CGFloat degrees = self.parameters[0];
CGFloat degrees = _parameters[0];
CGFloat radians = degrees * M_PI / 180.f;
return CGAffineTransformMake(1.f, 0.f, tan(radians), 1.f, 0.f, 0.f);
}
// skewY
case IJSVGTransformCommandSkewY: {
CGFloat degrees = self.parameters[0];
CGFloat degrees = _parameters[0];
CGFloat radians = degrees * M_PI / 180.f;
return CGAffineTransformMake(1.f, tan(radians), 0.f, 1.f, 0.f, 0.f);
}
@@ -406,15 +284,15 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
- (CGAffineTransform)CGAffineTransformWithModifier:(IJSVGTransformParameterModifier)modifier
{
switch (self.command) {
switch (_command) {
// matrix
case IJSVGTransformCommandMatrix: {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
CGFloat p2 = self.parameters[2];
CGFloat p3 = self.parameters[3];
CGFloat p4 = self.parameters[4];
CGFloat p5 = self.parameters[5];
CGFloat p0 = _parameters[0];
CGFloat p1 = _parameters[1];
CGFloat p2 = _parameters[2];
CGFloat p3 = _parameters[3];
CGFloat p4 = _parameters[4];
CGFloat p5 = _parameters[5];
if (modifier != nil) {
p0 = modifier(0, p0);
p1 = modifier(1, p1);
@@ -428,11 +306,11 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// translate
case IJSVGTransformCommandTranslate: {
CGFloat p0 = self.parameters[0];
if (self.parameterCount == 1) {
CGFloat p0 = _parameters[0];
if (_parameterCount == 1) {
return CGAffineTransformMakeTranslation(p0, 0);
}
CGFloat p1 = self.parameters[1];
CGFloat p1 = _parameters[1];
if (modifier != nil) {
p0 = modifier(0, p0);
p1 = modifier(1, p1);
@@ -442,7 +320,7 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// translateX
case IJSVGTransformCommandTranslateX: {
CGFloat p0 = self.parameters[0];
CGFloat p0 = _parameters[0];
if (modifier != nil) {
p0 = modifier(0, p0);
}
@@ -451,7 +329,7 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// translateY
case IJSVGTransformCommandTranslateY: {
CGFloat p0 = self.parameters[0];
CGFloat p0 = _parameters[0];
if (modifier != nil) {
p0 = modifier(0, p0);
}
@@ -460,11 +338,11 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// scale
case IJSVGTransformCommandScale: {
CGFloat p0 = self.parameters[0];
if (self.parameterCount == 1) {
CGFloat p0 = _parameters[0];
if (_parameterCount == 1) {
return CGAffineTransformMakeScale(p0, p0);
}
CGFloat p1 = self.parameters[1];
CGFloat p1 = _parameters[1];
if (modifier != nil) {
p0 = modifier(0, p0);
p1 = modifier(1, p1);
@@ -474,7 +352,7 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// skewX
case IJSVGTransformCommandSkewX: {
CGFloat degrees = self.parameters[0];
CGFloat degrees = _parameters[0];
if (modifier != nil) {
degrees = modifier(0, degrees);
}
@@ -484,7 +362,7 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// skewY
case IJSVGTransformCommandSkewY: {
CGFloat degrees = self.parameters[0];
CGFloat degrees = _parameters[0];
if (modifier != nil) {
degrees = modifier(0, degrees);
}
@@ -494,12 +372,12 @@ void IJSVGApplyTransform(NSArray<IJSVGTransform*>* transforms, IJSVGTransformApp
// rotate
case IJSVGTransformCommandRotate: {
if (self.parameterCount == 1) {
return CGAffineTransformMakeRotation((self.parameters[0] / 180) * M_PI);
if (_parameterCount == 1) {
return CGAffineTransformMakeRotation((_parameters[0] / 180) * M_PI);
} else {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
CGFloat p2 = self.parameters[2];
CGFloat p0 = _parameters[0];
CGFloat p1 = _parameters[1];
CGFloat p2 = _parameters[2];
if (modifier != nil) {
p0 = modifier(0, p0);
p1 = modifier(1, p1);
@@ -12,11 +12,6 @@
@implementation IJSVGUnitLength
@synthesize value;
@synthesize type;
@synthesize inherit;
@synthesize originalType;
+ (IJSVGUnitLength*)unitWithFloat:(CGFloat)number
{
IJSVGUnitLength* unit = [[[self alloc] init] autorelease];
@@ -56,6 +51,32 @@
return unit;
}
+ (IJSVGUnitLengthType)typeForCString:(const char*)chars
{
if(IJSVGCharBufferHasSuffix((char*)chars, "%")) {
return IJSVGUnitLengthTypePercentage;
}
if(IJSVGCharBufferHasSuffix((char*)chars, "cm")) {
return IJSVGUnitLengthTypeCM;
}
if(IJSVGCharBufferHasSuffix((char*)chars, "mm")) {
return IJSVGUnitLengthTypeMM;
}
if(IJSVGCharBufferHasSuffix((char*)chars, "in")) {
return IJSVGUnitLengthTypeIN;
}
if(IJSVGCharBufferHasSuffix((char*)chars, "pt")) {
return IJSVGUnitLengthTypePT;
}
if(IJSVGCharBufferHasSuffix((char*)chars, "pc")) {
return IJSVGUnitLengthTypePC;
}
if(IJSVGCharBufferHasSuffix((char*)chars, "px")) {
return IJSVGUnitLengthTypePX;
}
return IJSVGUnitLengthTypeNumber;
}
+ (IJSVGUnitLengthType)typeForString:(NSString*)string
{
if([string hasSuffix:@"%"] == YES) {
@@ -118,19 +139,44 @@
{
// just return noting for inherit, node will deal
// with the rest...hopefully
NSCharacterSet* cSet = NSCharacterSet.whitespaceCharacterSet;
string = [string stringByTrimmingCharactersInSet:cSet];
if ([string isEqualToString:@"inherit"]) {
if(string == nil) {
return nil;
}
char* chars = IJSVGTimmedCharBufferCreate(string.UTF8String);
// is inherit or just nothing
size_t strl = strlen(chars);
if (strcmp(chars, "inherit") == 0 || strl == 0) {
(void)free(chars), chars = NULL;
return nil;
}
// grab the float value from the string
NSInteger length;
CGFloat* floats = [IJSVGUtils scanFloatsFromCString:chars
floatCount:1
charCount:(NSUInteger)strl
size:&length];
// not sure how this ended up but nothing returned
// even though there should had been
if(length == 0) {
(void)free(floats), floats = NULL;
return nil;
}
IJSVGUnitLength* unit = [[[self alloc] init] autorelease];
unit.value = string.floatValue;
unit.value = floats[0];
unit.type = IJSVGUnitLengthTypeNumber;
IJSVGUnitLengthType type = [self typeForString:string];
IJSVGUnitLengthType type = [self typeForCString:chars];
unit.originalType = type;
// memory free
(void)(free(floats)), floats = NULL;
(void)free(chars), chars = NULL;
switch(type) {
case IJSVGUnitLengthTypePercentage: {
unit.value = [self convertUnitValue:unit.value
@@ -149,7 +195,7 @@
- (CGFloat)computeValue:(CGFloat)anotherValue
{
if (self.type == IJSVGUnitLengthTypePercentage) {
return ((anotherValue / 100.f) * (self.value * 100.f));
return ((anotherValue / 100.f) * (_value * 100.f));
}
return self.value;
}
@@ -170,17 +216,17 @@
- (NSString*)stringValueWithFloatingPointOptions:(IJSVGFloatingPointOptions)options
{
if (self.type == IJSVGUnitLengthTypePercentage) {
if (_type == IJSVGUnitLengthTypePercentage) {
return [NSString stringWithFormat:@"%@%%",
IJSVGShortFloatStringWithOptions(self.value * 100.f, options)];
IJSVGShortFloatStringWithOptions(_value * 100.f, options)];
}
return IJSVGShortFloatStringWithOptions(self.value, options);
return IJSVGShortFloatStringWithOptions(_value, options);
}
- (NSString*)description
{
return [NSString stringWithFormat:@"%f%@",
self.value, (self.type == IJSVGUnitLengthTypePercentage ? @"%" : @"")];
_value, (_value == IJSVGUnitLengthTypePercentage ? @"%" : @"")];
}
@end
@@ -6,7 +6,7 @@
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import "IJSVGUnitLength.h"
#import <IJSVG/IJSVGUnitLength.h>
#import <Foundation/Foundation.h>
@interface IJSVGUnitPoint : NSObject
@@ -10,9 +10,6 @@
@implementation IJSVGUnitPoint
@synthesize x = _x;
@synthesize y = _y;
- (void)dealloc
{
(void)[_x release], _x = nil;
@@ -6,8 +6,8 @@
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import "IJSVGUnitPoint.h"
#import "IJSVGUnitSize.h"
#import <IJSVG/IJSVGUnitPoint.h>
#import <IJSVG/IJSVGUnitSize.h>
#import <Foundation/Foundation.h>
@interface IJSVGUnitRect : NSObject
@@ -10,9 +10,6 @@
@implementation IJSVGUnitRect
@synthesize size = _size;
@synthesize origin = _origin;
- (void)dealloc
{
(void)[_size release], _size = nil;
@@ -6,7 +6,7 @@
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import "IJSVGUnitLength.h"
#import <IJSVG/IJSVGUnitLength.h>
#import <Foundation/Foundation.h>
@interface IJSVGUnitSize : NSObject
@@ -10,9 +10,6 @@
@implementation IJSVGUnitSize
@synthesize width = _width;
@synthesize height = _height;
- (void)dealloc
{
(void)[_width release], _width = nil;
@@ -6,9 +6,9 @@
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGCommand.h"
#import "IJSVGGradientUnitLength.h"
#import "IJSVGStringAdditions.h"
#import <IJSVG/IJSVGCommand.h>
#import <IJSVG/IJSVGGradientUnitLength.h>
#import <IJSVG/IJSVGStringAdditions.h>
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@@ -21,10 +21,13 @@ CGFloat angle(CGPoint a, CGPoint b);
CGFloat radians_to_degrees(CGFloat radians);
CGFloat degrees_to_radians(CGFloat degrees);
BOOL IJSVGCharBufferIsHEX(char* buffer);
BOOL IJSVGCharBufferHasPrefix(char* pre, char* str);
BOOL IJSVGCharBufferHasSuffix(char* s1, char* s2);
char* IJSVGTimmedCharBufferCreate(const char* buffer);
void IJSVGTrimCharBuffer(char* buffer);
BOOL IJSVGIsCommonHTMLElementName(NSString* str);
NSArray* IJSVGCommonHTMLElementNames(void);
void IJSVGCharBufferToLower(char* buffer);
size_t IJSVGCharBufferHash(char* buffer);
IJSVGFloatingPointOptions IJSVGFloatingPointOptionsDefault(void);
IJSVGFloatingPointOptions IJSVGFloatingPointOptionsMake(BOOL round, int precision);
@@ -45,6 +48,8 @@ BOOL IJSVGIsSVGLayer(CALayer* layer);
+ (IJSVGWindingRule)windingRuleForString:(NSString*)string;
+ (IJSVGLineJoinStyle)lineJoinStyleForString:(NSString*)string;
+ (IJSVGLineCapStyle)lineCapStyleForString:(NSString*)string;
+ (IJSVGLineJoinStyle)lineJoinStyleForCGLineJoin:(CGLineJoin)lineJoin;
+ (IJSVGLineCapStyle)lineCapStyleForCGLineCap:(CGLineCap)lineCap;
+ (IJSVGUnitType)unitTypeForString:(NSString*)string;
+ (IJSVGBlendMode)blendModeForString:(NSString*)string;
+ (NSString* _Nullable)mixBlendingModeForBlendMode:(IJSVGBlendMode)blendMode;
@@ -60,6 +65,12 @@ BOOL IJSVGIsSVGLayer(CALayer* layer);
fallBackForPercent:(CGFloat)viewBox;
+ (CGFloat*)scanFloatsFromString:(NSString*)string
size:(NSInteger*)length;
+ (CGFloat*)scanFloatsFromCString:(const char*)buffer
size:(NSInteger*)length;
+ (CGFloat*)scanFloatsFromCString:(const char*)buffer
floatCount:(NSUInteger)floatCount
charCount:(NSUInteger)charCount
size:(NSInteger*)length;
+ (IJSVGFontTraits)fontStyleStringForString:(NSString*)string;
+ (IJSVGFontTraits)fontWeightTraitForString:(NSString*)string
weight:(CGFloat*)weight;
+157 -176
View File
@@ -10,11 +10,56 @@
#import "IJSVGShapeLayer.h"
#import "IJSVGUtils.h"
#import "IJSVGExporterPathInstruction.h"
#import "IJSVGParsing.h"
@implementation IJSVGUtils
void IJSVGTrimCharBuffer(char* buffer)
BOOL IJSVGCharBufferIsHEX(char* buffer) {
char c;
while((c = *buffer++)) {
BOOL flag = ((c == '#') ||
(c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'));
if(flag == NO) {
return NO;
}
}
return YES;
}
BOOL IJSVGCharBufferHasPrefix(char *str, char *pre)
{
return strncmp(pre, str, strlen(pre)) == 0;
}
BOOL IJSVGCharBufferHasSuffix(char* s1, char* s2)
{
size_t slen = strlen(s1);
size_t tlen = strlen(s2);
if (tlen > slen) {
return NO;
}
return strcmp(s1 + slen - tlen, s2) == 0;
}
char* IJSVGTimmedCharBufferCreate(const char* buffer)
{
unsigned long start = 0;
unsigned long length = strlen(buffer);
while(length-1 > 0 && isspace(buffer[length-1])) {
length--;
}
while(isspace(buffer[start])) {
start++;
}
char* chars = (char*)malloc(sizeof(char)*((length-start)+1) ?: sizeof(char));
memcpy(chars, &buffer[start], length-start);
chars[length] = '\0';
return chars;
}
void IJSVGTrimCharBuffer(char* buffer) {
char* ptr = buffer;
unsigned long length = strlen(ptr);
while(length-1 > 0 && isspace(ptr[length-1])) {
@@ -27,153 +72,22 @@ void IJSVGTrimCharBuffer(char* buffer)
memmove(buffer, ptr, length+1);
}
BOOL IJSVGIsCommonHTMLElementName(NSString* str)
void IJSVGCharBufferToLower(char* buffer)
{
str = str.lowercaseString;
return [IJSVGCommonHTMLElementNames() containsObject:str];
};
for(char *p = buffer; *p; p++) {
*p = tolower(*p);
}
}
NSArray* IJSVGCommonHTMLElementNames(void)
size_t IJSVGCharBufferHash(char* buffer)
{
static NSArray* names = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
names = [@[ @"a",
@"abbr",
@"acronym",
@"abbr",
@"address",
@"applet",
@"embed",
@"object",
@"area",
@"article",
@"aside",
@"audio",
@"b",
@"base",
@"basefont",
@"bdi",
@"bdo",
@"big",
@"blockquote",
@"body",
@"br",
@"button",
@"canvas",
@"caption",
@"center",
@"cite",
@"code",
@"col",
@"colgroup",
@"colgroup",
@"datalist",
@"dd",
@"del",
@"details",
@"dfn",
@"dialog",
@"dir",
@"ul",
@"div",
@"dl",
@"dt",
@"em",
@"embed",
@"fieldset",
@"figcaption",
@"figure",
@"figure",
@"font",
@"footer",
@"form",
@"frame",
@"frameset",
@"h1",
@"h6",
@"head",
@"header",
@"hr",
@"html",
@"i",
@"iframe",
@"img",
@"input",
@"ins",
@"kbd",
@"label",
@"input",
@"legend",
@"fieldset",
@"li",
@"link",
@"main",
@"map",
@"mark",
@"menu",
@"menuitem",
@"meta",
@"meter",
@"nav",
@"noframes",
@"noscript",
@"object",
@"ol",
@"optgroup",
@"option",
@"output",
@"p",
@"param",
@"picture",
@"pre",
@"progress",
@"q",
@"rp",
@"rt",
@"ruby",
@"s",
@"samp",
@"script",
@"section",
@"select",
@"small",
@"source",
@"video",
@"audio",
@"span",
@"strike",
@"del",
@"s",
@"strong",
@"style",
@"sub",
@"summary",
@"details",
@"sup",
@"table",
@"tbody",
@"td",
@"template",
@"textarea",
@"tfoot",
@"th",
@"thead",
@"time",
@"title",
@"tr",
@"track",
@"video",
@"audio",
@"tt",
@"u",
@"ul",
@"var",
@"video",
@"wbr" ] retain];
});
return names;
};
unsigned long hash = 5381;
int c;
while ((c = *buffer++)) {
hash = ((hash << 5) + hash) + c;
}
return hash;
}
NSString* IJSVGShortenFloatString(NSString* string)
{
@@ -263,7 +177,16 @@ NSString* IJSVGPointToCommandString(CGPoint point)
BOOL IJSVGIsLegalCommandCharacter(unichar aChar)
{
if ((aChar | ('M' ^ 'm')) == 'm' || (aChar | ('Z' ^ 'z')) == 'z' || (aChar | ('C' ^ 'c')) == 'c' || (aChar | ('L' ^ 'l')) == 'l' || (aChar | ('S' ^ 's')) == 's' || (aChar | ('Q' ^ 'q')) == 'q' || (aChar | ('H' ^ 'h')) == 'h' || (aChar | ('V' ^ 'v')) == 'v' || (aChar | ('T' ^ 't')) == 't' || (aChar | ('A' ^ 'a')) == 'a') {
if ((aChar | ('M' ^ 'm')) == 'm' ||
(aChar | ('Z' ^ 'z')) == 'z' ||
(aChar | ('C' ^ 'c')) == 'c' ||
(aChar | ('L' ^ 'l')) == 'l' ||
(aChar | ('S' ^ 's')) == 's' ||
(aChar | ('Q' ^ 'q')) == 'q' ||
(aChar | ('H' ^ 'h')) == 'h' ||
(aChar | ('V' ^ 'v')) == 'v' ||
(aChar | ('T' ^ 't')) == 't' ||
(aChar | ('A' ^ 'a')) == 'a') {
return YES;
}
return NO;
@@ -324,34 +247,34 @@ CGFloat degrees_to_radians(CGFloat degrees)
+ (NSString* _Nullable)defURL:(NSString*)string
{
// insta check for URL
NSCharacterSet* set = NSCharacterSet.whitespaceCharacterSet;
string = [string stringByTrimmingCharactersInSet:set];
NSString* check = [string substringToIndex:3].lowercaseString;
if ([check isEqualToString:@"url"] == NO) {
const char* str = string.UTF8String;
NSUInteger count = 0;
IJSVGParsingStringMethod** methods;
methods = IJSVGParsingMethodParseString(str, &count);
if(count == 0) {
IJSVGParsingStringMethodsRelease(methods, count);
return nil;
}
static NSRegularExpression* _reg = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_reg = [[NSRegularExpression alloc] initWithPattern:@"url\\(['\"]?([^)]+?)['\"]?\\)"
options:0
error:nil];
});
__block NSString* foundID = nil;
[_reg enumerateMatchesInString:string
options:0
range:NSMakeRange(0, string.length)
usingBlock:^(NSTextCheckingResult* result,
NSMatchingFlags flags, BOOL* stop) {
if ((foundID = [string substringWithRange:[result rangeAtIndex:1]]) != nil) {
*stop = YES;
}
}];
if ([foundID hasPrefix:@"#"] == YES) {
foundID = [foundID substringFromIndex:1];
// what type of method is it?
IJSVGParsingStringMethod* method = methods[0];
IJSVGCharBufferToLower(method->name);
if(strcmp(method->name, "url") != 0) {
(void)IJSVGParsingStringMethodsRelease(methods, count), methods = NULL;
return nil;
}
// remove the #
char* parameters = method->parameters;
if(parameters[0] == '#') {
parameters++;
}
// make the nsstring
NSString* foundID = [NSString stringWithUTF8String:parameters];
// release the stuff
(void)IJSVGParsingStringMethodsRelease(methods, count), methods = NULL;
return foundID;
}
@@ -406,6 +329,42 @@ CGFloat degrees_to_radians(CGFloat degrees)
return IJSVGLineCapStyleButt;
}
+ (IJSVGLineCapStyle)lineCapStyleForCGLineCap:(CGLineCap)lineCap
{
switch(lineCap) {
case kCGLineCapButt: {
return IJSVGLineCapStyleButt;
}
case kCGLineCapRound: {
return IJSVGLineCapStyleRound;
}
case kCGLineCapSquare: {
return IJSVGLineCapStyleSquare;
}
default: {
return IJSVGLineCapStyleInherit;
}
}
}
+ (IJSVGLineJoinStyle)lineJoinStyleForCGLineJoin:(CGLineJoin)lineJoin
{
switch(lineJoin) {
case kCGLineJoinRound: {
return IJSVGLineJoinStyleRound;
}
case kCGLineJoinMiter: {
return IJSVGLineJoinStyleMiter;
}
case kCGLineJoinBevel: {
return IJSVGLineJoinStyleBevel;
}
default: {
return IJSVGLineJoinStyleInherit;
}
}
}
+ (IJSVGUnitType)unitTypeForString:(NSString*)string
{
if ([string isEqualToString:@"userSpaceOnUse"]) {
@@ -516,9 +475,28 @@ CGFloat degrees_to_radians(CGFloat degrees)
+ (CGFloat*)scanFloatsFromString:(NSString*)string
size:(NSInteger*)length
{
return [self.class scanFloatsFromCString:string.UTF8String
size:length];
}
+ (CGFloat*)scanFloatsFromCString:(const char*)buffer
size:(NSInteger*)length
{
IJSVGPathDataStream* stream = IJSVGPathDataStreamCreateDefault();
CGFloat* floats = IJSVGParsePathDataStreamSequence(string.UTF8String, string.length,
CGFloat* floats = IJSVGParsePathDataStreamSequence(buffer, strlen(buffer),
stream, NULL, 1, length);
IJSVGPathDataStreamRelease(stream);
return floats;
}
+ (CGFloat*)scanFloatsFromCString:(const char*)buffer
floatCount:(NSUInteger)floatCount
charCount:(NSUInteger)charCount
size:(NSInteger*)length
{
IJSVGPathDataStream* stream = IJSVGPathDataStreamCreate(floatCount, charCount);
CGFloat* floats = IJSVGParsePathDataStreamSequence(buffer, strlen(buffer),
stream, NULL, 1, length);
IJSVGPathDataStreamRelease(stream);
return floats;
@@ -528,8 +506,9 @@ CGFloat degrees_to_radians(CGFloat degrees)
{
IJSVGPathDataStream* stream = IJSVGPathDataStreamCreate(4,
IJSVG_STREAM_CHAR_BLOCK_SIZE);
CGFloat* floats = IJSVGParsePathDataStreamSequence(string.UTF8String,
string.length, stream, NULL, 1, NULL);
const char* str = string.UTF8String;
CGFloat* floats = IJSVGParsePathDataStreamSequence(str, strlen(str),
stream, NULL, 1, NULL);
IJSVGPathDataStreamRelease(stream);
return floats;
}
@@ -538,8 +517,9 @@ CGFloat degrees_to_radians(CGFloat degrees)
fallBackForPercent:(CGFloat)fallBack
{
CGFloat val = [string floatValue];
if ([string rangeOfString:@"%"].location != NSNotFound)
if ([string rangeOfString:@"%"].location != NSNotFound) {
val = (fallBack * val) / 100;
}
return val;
}
@@ -555,8 +535,9 @@ CGFloat degrees_to_radians(CGFloat degrees)
+ (CGFloat)floatValue:(NSString*)string
{
if ([string isEqualToString:@"inherit"])
if ([string isEqualToString:@"inherit"]) {
return IJSVGInheritedFloatValue;
}
return [string floatValue];
}