Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 399e9271b4 | |||
| f9107094a4 | |||
| 7a64c122cf | |||
| 8ee96c4a5b | |||
| 7cace854bd | |||
| 91cf59bd32 | |||
| b5e15445a5 | |||
| e7577a3e76 | |||
| 25b38fa128 | |||
| 8ef14224dd | |||
| ebfc31cdb1 | |||
| ccfcfbf228 | |||
| 4fe1d594b0 | |||
| 61d5bb99d5 | |||
| faccec3e01 | |||
| 66e9c9000e | |||
| 2006e68c2a | |||
| 01aea1129d | |||
| 388982d37b | |||
| 941130d1f2 | |||
| b9e964dc50 | |||
| 8532819b6a | |||
| e576803bb7 | |||
| a6a802a36b | |||
| b50963292c | |||
| c40c63c48d | |||
| 6d21b56416 | |||
| dbdd8a6971 | |||
| b7ba91aa82 | |||
| 98efacf93c | |||
| 1b3cf5a1a0 | |||
| 35f8663631 | |||
| 6d4eb76803 | |||
| 36dae3ad1b | |||
| 8149293b7b | |||
| 1993c55cb8 | |||
| 1f13134ccd | |||
| 435489f65f | |||
| 4bcbcc374b | |||
| cb544d7a6b | |||
| a4cd0bcc4e | |||
| 02b03d8862 | |||
| 94ae7c2757 | |||
| 27371a9d78 | |||
| 3c1422a65f | |||
| 8ed817aa43 | |||
| ca754ff61c | |||
| 53da0fca4b | |||
| 1259ce1e63 | |||
| 70cd111b1c | |||
| 15b0805bf9 | |||
| e11e28c54f | |||
| f320bd4684 | |||
| 2faeda560f | |||
| c56a5ecca1 | |||
| e85f67358e | |||
| 0fb27d4e94 | |||
| 9bce41afff | |||
| 09b0cc6d96 | |||
| 58fbb01ff2 | |||
| 5a5b144ab1 | |||
| 359cff7569 | |||
| 57bfe633e7 | |||
| c757fb4b33 | |||
| 4d1141a889 | |||
| 481359ec58 | |||
| fb14d7be65 | |||
| 0e4a5528f7 | |||
| 36216a51ed | |||
| 9e1ffd5d46 | |||
| 05d0b6e173 | |||
| 9cecea923c | |||
| 8ff1fc860e | |||
| edef84274d | |||
| 484675f666 | |||
| 2f574e3cbb | |||
| b64b0f3735 | |||
| c0cdb41ddc | |||
| 258a617564 | |||
| bf40002fe0 | |||
| 2ef0bd88ef | |||
| 0aca44ef76 | |||
| a3e512d7b8 | |||
| 0ffd20e87c | |||
| 0f0943816c | |||
| ddb5b2f746 | |||
| 80bc3ed1d8 | |||
| 1a7a7b4d7b | |||
| 0876a1e976 | |||
| 6747160504 | |||
| d2059e4d2f | |||
| 71c47a313e | |||
| 70f9c29fe5 | |||
| 5f8f3d8be5 | |||
| e26ebe300f | |||
| c0c46ad2b7 | |||
| abade39e2a | |||
| 2f7a6aa213 | |||
| 2598046369 | |||
| 5424e32a63 | |||
| a8a7f8d356 | |||
| a3bde2f710 | |||
| 0cc22cad44 | |||
| 448d46e8ec |
@@ -7,6 +7,13 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
343A19181FB2212C000652A2 /* IJSVGGradientUnitLength.m in Sources */ = {isa = PBXBuildFile; fileRef = 343A19171FB2212C000652A2 /* IJSVGGradientUnitLength.m */; };
|
||||
590C87EB201F9888004A1554 /* json.svg in Resources */ = {isa = PBXBuildFile; fileRef = 590C87EA201F9888004A1554 /* json.svg */; };
|
||||
590C87ED201FA08C004A1554 /* intertwingly.svg in Resources */ = {isa = PBXBuildFile; fileRef = 590C87EC201FA08C004A1554 /* intertwingly.svg */; };
|
||||
590C87EF201FA093004A1554 /* NewTux.svg in Resources */ = {isa = PBXBuildFile; fileRef = 590C87EE201FA092004A1554 /* NewTux.svg */; };
|
||||
590C87F1201FBF27004A1554 /* AJ_Digital_Camera.svg in Resources */ = {isa = PBXBuildFile; fileRef = 590C87F0201FBF27004A1554 /* AJ_Digital_Camera.svg */; };
|
||||
590C87F4201FC9E4004A1554 /* radialgradient2.svg in Resources */ = {isa = PBXBuildFile; fileRef = 590C87F3201FC9E3004A1554 /* radialgradient2.svg */; };
|
||||
590C87F6201FD0D4004A1554 /* car.svg in Resources */ = {isa = PBXBuildFile; fileRef = 590C87F5201FD0D4004A1554 /* car.svg */; };
|
||||
591704CC1E2016E400012644 /* IJSVGExporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 591704CB1E2016E400012644 /* IJSVGExporter.m */; };
|
||||
591A13E11E19838F001D1629 /* IJSVGText.m in Sources */ = {isa = PBXBuildFile; fileRef = 591A13E01E19838F001D1629 /* IJSVGText.m */; };
|
||||
59265CE81C4F840400F333F0 /* css.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59265CE71C4F840400F333F0 /* css.svg */; };
|
||||
@@ -32,6 +39,8 @@
|
||||
5986308A19BA106D00CF15EA /* SVGExampleView5.m in Sources */ = {isa = PBXBuildFile; fileRef = 5986308919BA106D00CF15EA /* SVGExampleView5.m */; };
|
||||
5986308C19BA180E00CF15EA /* dashed.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5986308B19BA180E00CF15EA /* dashed.svg */; };
|
||||
598759F61E242C850024CC3F /* IJSVGExporterPathInstruction.m in Sources */ = {isa = PBXBuildFile; fileRef = 598759F51E242C850024CC3F /* IJSVGExporterPathInstruction.m */; };
|
||||
5991A2A9201E30E600913E3B /* gradients.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5991A2A8201E30E600913E3B /* gradients.svg */; };
|
||||
5991A2AB201E310200913E3B /* heart.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5991A2AA201E310200913E3B /* heart.svg */; };
|
||||
599465DC1C4AA87200A2EEF3 /* IJSVGStyleSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 599465D51C4AA87200A2EEF3 /* IJSVGStyleSheet.m */; };
|
||||
599465DD1C4AA87200A2EEF3 /* IJSVGStyleSheetRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 599465D71C4AA87200A2EEF3 /* IJSVGStyleSheetRule.m */; };
|
||||
599465DE1C4AA87200A2EEF3 /* IJSVGStyleSheetSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 599465D91C4AA87200A2EEF3 /* IJSVGStyleSheetSelector.m */; };
|
||||
@@ -42,6 +51,11 @@
|
||||
59A3DA6D1E283022003E59A9 /* IJSVGTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 59A3DA6C1E283022003E59A9 /* IJSVGTransaction.m */; };
|
||||
59B93C6D19B7D1840063E823 /* paperplane.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59B93C6C19B7D1840063E823 /* paperplane.svg */; };
|
||||
59B93C6F19B7D32C0063E823 /* products.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59B93C6E19B7D32C0063E823 /* products.svg */; };
|
||||
59D1E39E2022577500C54672 /* IJSVGQuartzRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 59D1E39D2022577500C54672 /* IJSVGQuartzRenderer.m */; };
|
||||
59D1E3A0202279CA00C54672 /* Group.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59D1E39F202279CA00C54672 /* Group.svg */; };
|
||||
59D6C4142049939800F16F13 /* move.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59D6C4132049939800F16F13 /* move.svg */; };
|
||||
59D6C4162049947E00F16F13 /* sprite-1.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59D6C4152049947E00F16F13 /* sprite-1.svg */; };
|
||||
59D6C4182049977E00F16F13 /* fa-brands.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59D6C4172049977E00F16F13 /* fa-brands.svg */; };
|
||||
59E0F5ED1E29964700F757F7 /* IJSVGUnitLength.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E0F5EC1E29964700F757F7 /* IJSVGUnitLength.m */; };
|
||||
59E2645119BA240D008A6FDB /* IJSVG.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E2641C19BA240D008A6FDB /* IJSVG.m */; };
|
||||
59E2645219BA240D008A6FDB /* IJSVGBezierPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E2641E19BA240D008A6FDB /* IJSVGBezierPathAdditions.m */; };
|
||||
@@ -90,6 +104,14 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
343A19161FB2212C000652A2 /* IJSVGGradientUnitLength.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGGradientUnitLength.h; sourceTree = "<group>"; };
|
||||
343A19171FB2212C000652A2 /* IJSVGGradientUnitLength.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGGradientUnitLength.m; sourceTree = "<group>"; };
|
||||
590C87EA201F9888004A1554 /* json.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = json.svg; sourceTree = "<group>"; };
|
||||
590C87EC201FA08C004A1554 /* intertwingly.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = intertwingly.svg; sourceTree = "<group>"; };
|
||||
590C87EE201FA092004A1554 /* NewTux.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = NewTux.svg; sourceTree = "<group>"; };
|
||||
590C87F0201FBF27004A1554 /* AJ_Digital_Camera.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = AJ_Digital_Camera.svg; sourceTree = "<group>"; };
|
||||
590C87F3201FC9E3004A1554 /* radialgradient2.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = radialgradient2.svg; sourceTree = "<group>"; };
|
||||
590C87F5201FD0D4004A1554 /* car.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = car.svg; sourceTree = "<group>"; };
|
||||
591704CA1E2016E400012644 /* IJSVGExporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGExporter.h; sourceTree = "<group>"; };
|
||||
591704CB1E2016E400012644 /* IJSVGExporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGExporter.m; sourceTree = "<group>"; };
|
||||
591A13DF1E19838F001D1629 /* IJSVGText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGText.h; sourceTree = "<group>"; };
|
||||
@@ -136,6 +158,8 @@
|
||||
5986308B19BA180E00CF15EA /* dashed.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dashed.svg; sourceTree = "<group>"; };
|
||||
598759F41E242C850024CC3F /* IJSVGExporterPathInstruction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGExporterPathInstruction.h; sourceTree = "<group>"; };
|
||||
598759F51E242C850024CC3F /* IJSVGExporterPathInstruction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGExporterPathInstruction.m; sourceTree = "<group>"; };
|
||||
5991A2A8201E30E600913E3B /* gradients.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = gradients.svg; sourceTree = "<group>"; };
|
||||
5991A2AA201E310200913E3B /* heart.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = heart.svg; sourceTree = "<group>"; };
|
||||
599465D41C4AA87200A2EEF3 /* IJSVGStyleSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGStyleSheet.h; sourceTree = "<group>"; };
|
||||
599465D51C4AA87200A2EEF3 /* IJSVGStyleSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGStyleSheet.m; sourceTree = "<group>"; };
|
||||
599465D61C4AA87200A2EEF3 /* IJSVGStyleSheetRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGStyleSheetRule.h; sourceTree = "<group>"; };
|
||||
@@ -154,6 +178,12 @@
|
||||
59A3DA6C1E283022003E59A9 /* IJSVGTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGTransaction.m; sourceTree = "<group>"; };
|
||||
59B93C6C19B7D1840063E823 /* paperplane.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = paperplane.svg; sourceTree = "<group>"; };
|
||||
59B93C6E19B7D32C0063E823 /* products.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = products.svg; sourceTree = "<group>"; };
|
||||
59D1E39C2022577500C54672 /* IJSVGQuartzRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGQuartzRenderer.h; sourceTree = "<group>"; };
|
||||
59D1E39D2022577500C54672 /* IJSVGQuartzRenderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGQuartzRenderer.m; sourceTree = "<group>"; };
|
||||
59D1E39F202279CA00C54672 /* Group.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Group.svg; sourceTree = "<group>"; };
|
||||
59D6C4132049939800F16F13 /* move.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = move.svg; sourceTree = "<group>"; };
|
||||
59D6C4152049947E00F16F13 /* sprite-1.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "sprite-1.svg"; sourceTree = "<group>"; };
|
||||
59D6C4172049977E00F16F13 /* fa-brands.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "fa-brands.svg"; sourceTree = "<group>"; };
|
||||
59E0F5EB1E29964700F757F7 /* IJSVGUnitLength.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGUnitLength.h; sourceTree = "<group>"; };
|
||||
59E0F5EC1E29964700F757F7 /* IJSVGUnitLength.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGUnitLength.m; sourceTree = "<group>"; };
|
||||
59E2641B19BA240D008A6FDB /* IJSVG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVG.h; sourceTree = "<group>"; };
|
||||
@@ -291,6 +321,17 @@
|
||||
5956657A19B62F4600D805FF /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
59D6C4172049977E00F16F13 /* fa-brands.svg */,
|
||||
59D1E39F202279CA00C54672 /* Group.svg */,
|
||||
590C87F5201FD0D4004A1554 /* car.svg */,
|
||||
590C87F3201FC9E3004A1554 /* radialgradient2.svg */,
|
||||
590C87F0201FBF27004A1554 /* AJ_Digital_Camera.svg */,
|
||||
590C87EC201FA08C004A1554 /* intertwingly.svg */,
|
||||
59D6C4152049947E00F16F13 /* sprite-1.svg */,
|
||||
590C87EE201FA092004A1554 /* NewTux.svg */,
|
||||
590C87EA201F9888004A1554 /* json.svg */,
|
||||
5991A2AA201E310200913E3B /* heart.svg */,
|
||||
5991A2A8201E30E600913E3B /* gradients.svg */,
|
||||
59265CE71C4F840400F333F0 /* css.svg */,
|
||||
59459CEB19B906FE00CE493B /* clipped.svg */,
|
||||
59F799E119B880CE00096CB7 /* htc_one.svg */,
|
||||
@@ -299,6 +340,7 @@
|
||||
595665DD19B6309C00D805FF /* test.svg */,
|
||||
5986308619BA104800CF15EA /* linecap.svg */,
|
||||
5986308B19BA180E00CF15EA /* dashed.svg */,
|
||||
59D6C4132049939800F16F13 /* move.svg */,
|
||||
5956657B19B62F4600D805FF /* Info.plist */,
|
||||
5956657C19B62F4600D805FF /* main.m */,
|
||||
5956659A19B62F9500D805FF /* IJSVGExample-Prefix.pch */,
|
||||
@@ -392,6 +434,8 @@
|
||||
59E2643C19BA240D008A6FDB /* IJSVGForeignObject.m */,
|
||||
59E2643D19BA240D008A6FDB /* IJSVGGradient.h */,
|
||||
59E2643E19BA240D008A6FDB /* IJSVGGradient.m */,
|
||||
343A19161FB2212C000652A2 /* IJSVGGradientUnitLength.h */,
|
||||
343A19171FB2212C000652A2 /* IJSVGGradientUnitLength.m */,
|
||||
59E2643F19BA240D008A6FDB /* IJSVGGroup.h */,
|
||||
59E2644019BA240D008A6FDB /* IJSVGGroup.m */,
|
||||
59E2644119BA240D008A6FDB /* IJSVGLinearGradient.h */,
|
||||
@@ -434,6 +478,8 @@
|
||||
597046A11E24352700A60138 /* IJSVGStrokeLayer.m */,
|
||||
59E0F5EB1E29964700F757F7 /* IJSVGUnitLength.h */,
|
||||
59E0F5EC1E29964700F757F7 /* IJSVGUnitLength.m */,
|
||||
59D1E39C2022577500C54672 /* IJSVGQuartzRenderer.h */,
|
||||
59D1E39D2022577500C54672 /* IJSVGQuartzRenderer.m */,
|
||||
);
|
||||
name = source;
|
||||
path = ../../source;
|
||||
@@ -483,7 +529,7 @@
|
||||
5956656F19B62F4600D805FF /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0600;
|
||||
LastUpgradeCheck = 0920;
|
||||
ORGANIZATIONNAME = "Curtis Hard";
|
||||
TargetAttributes = {
|
||||
5956657619B62F4600D805FF = {
|
||||
@@ -521,13 +567,25 @@
|
||||
files = (
|
||||
59B93C6D19B7D1840063E823 /* paperplane.svg in Resources */,
|
||||
5956658219B62F4600D805FF /* Images.xcassets in Resources */,
|
||||
590C87ED201FA08C004A1554 /* intertwingly.svg in Resources */,
|
||||
59D6C4182049977E00F16F13 /* fa-brands.svg in Resources */,
|
||||
590C87F1201FBF27004A1554 /* AJ_Digital_Camera.svg in Resources */,
|
||||
590C87EF201FA093004A1554 /* NewTux.svg in Resources */,
|
||||
59D6C4162049947E00F16F13 /* sprite-1.svg in Resources */,
|
||||
59265CE81C4F840400F333F0 /* css.svg in Resources */,
|
||||
595665DE19B6309C00D805FF /* test.svg in Resources */,
|
||||
590C87EB201F9888004A1554 /* json.svg in Resources */,
|
||||
59D1E3A0202279CA00C54672 /* Group.svg in Resources */,
|
||||
59F799E219B880CE00096CB7 /* htc_one.svg in Resources */,
|
||||
59B93C6F19B7D32C0063E823 /* products.svg in Resources */,
|
||||
5991A2A9201E30E600913E3B /* gradients.svg in Resources */,
|
||||
5986308719BA104800CF15EA /* linecap.svg in Resources */,
|
||||
5956658519B62F4600D805FF /* MainMenu.xib in Resources */,
|
||||
590C87F4201FC9E4004A1554 /* radialgradient2.svg in Resources */,
|
||||
59459CEC19B906FE00CE493B /* clipped.svg in Resources */,
|
||||
590C87F6201FD0D4004A1554 /* car.svg in Resources */,
|
||||
59D6C4142049939800F16F13 /* move.svg in Resources */,
|
||||
5991A2AB201E310200913E3B /* heart.svg in Resources */,
|
||||
5986308C19BA180E00CF15EA /* dashed.svg in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -550,6 +608,7 @@
|
||||
59E2646919BA240D008A6FDB /* IJSVGStyle.m in Sources */,
|
||||
59E2646419BA240D008A6FDB /* IJSVGLinearGradient.m in Sources */,
|
||||
595665DC19B6302600D805FF /* README.md in Sources */,
|
||||
59D1E39E2022577500C54672 /* IJSVGQuartzRenderer.m in Sources */,
|
||||
5941DAFD1CF9BC9B00B3A911 /* IJSVGImage.m in Sources */,
|
||||
599465DE1C4AA87200A2EEF3 /* IJSVGStyleSheetSelector.m in Sources */,
|
||||
5948DED61BB2BFE5004156FF /* IJSVGWriter.m in Sources */,
|
||||
@@ -557,6 +616,7 @@
|
||||
5986308A19BA106D00CF15EA /* SVGExampleView5.m in Sources */,
|
||||
59E2645619BA240D008A6FDB /* IJSVGCommandArc.m in Sources */,
|
||||
59E8ABF91E219C860032A80C /* IJSVGMath.m in Sources */,
|
||||
343A19181FB2212C000652A2 /* IJSVGGradientUnitLength.m in Sources */,
|
||||
59265CEB1C4F843E00F333F0 /* SVGExampleView6.m in Sources */,
|
||||
5941DAFE1CF9BC9B00B3A911 /* IJSVGPattern.m in Sources */,
|
||||
59E8ABF01E211EDB0032A80C /* IJSVGShapeLayer.m in Sources */,
|
||||
@@ -676,7 +736,7 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx10.12;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -712,7 +772,7 @@
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx10.12;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "D72EFE5A8E718DCC42CFCBCE6A16E2165A4F6596",
|
||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
|
||||
|
||||
},
|
||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
|
||||
"D72EFE5A8E718DCC42CFCBCE6A16E2165A4F6596" : 0,
|
||||
"91965C60FB9217304DAC6A57C88233440365843F" : 9223372036854775807
|
||||
},
|
||||
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "83EE8FFF-CAF0-4D33-92A0-4CA5D3A6A33C",
|
||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
|
||||
"D72EFE5A8E718DCC42CFCBCE6A16E2165A4F6596" : "IJSVG\/",
|
||||
"91965C60FB9217304DAC6A57C88233440365843F" : "..\/..\/.."
|
||||
},
|
||||
"DVTSourceControlWorkspaceBlueprintNameKey" : "IJSVGExample",
|
||||
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
|
||||
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "IJSVGExample\/IJSVGExample.xcodeproj",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/daveyheuser\/Iconjar-mac.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "91965C60FB9217304DAC6A57C88233440365843F"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/curthard89\/IJSVG.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "D72EFE5A8E718DCC42CFCBCE6A16E2165A4F6596"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0600"
|
||||
LastUpgradeVersion = "0920"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -37,10 +37,11 @@
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
@@ -62,17 +63,22 @@
|
||||
ReferencedContainer = "container:IJSVGExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "5956657619B62F4600D805FF"
|
||||
@@ -85,12 +91,13 @@
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "5956657619B62F4600D805FF"
|
||||
|
||||
|
After Width: | Height: | Size: 515 KiB |
@@ -1,8 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11201" systemVersion="16B2657" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11201"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
@@ -670,19 +671,19 @@
|
||||
<window title="IJSVGExample" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="335" y="390" width="556" height="679"/>
|
||||
<rect key="contentRect" x="335" y="390" width="600" height="479"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
|
||||
<view key="contentView" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="556" height="679"/>
|
||||
<view key="contentView" misplaced="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="479"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<customView misplaced="YES" id="hhC-1S-CCX" customClass="SVGView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="556" height="679"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="479"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</customView>
|
||||
</subviews>
|
||||
</view>
|
||||
<point key="canvasLocation" x="550" y="495.5"/>
|
||||
<point key="canvasLocation" x="98" y="496.5"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="193px" height="172px" viewBox="0 0 193 172" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Group">
|
||||
<rect id="Rectangle-Copy" fill="#E80000" x="53" y="41" width="140" height="131"></rect>
|
||||
<rect id="Rectangle" fill="#1813DF" x="0" y="0" width="140" height="131"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 679 B |
@@ -0,0 +1,72 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="500pt" height="800">
|
||||
<defs>
|
||||
<linearGradient id="a">
|
||||
<stop offset="0" stop-color="#3f2600" stop-opacity=".6"/>
|
||||
<stop offset="1" stop-color="#3f2600" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="d">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity=".65"/>
|
||||
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="e">
|
||||
<stop offset="0" stop-color="#ffa63f"/>
|
||||
<stop offset="1" stop-color="#ff0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b">
|
||||
<stop offset="0" stop-color="#ffeed7"/>
|
||||
<stop offset="1" stop-color="#bdbfc2"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity=".8"/>
|
||||
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="f" cx="446.77762" cy="1219.4125" r="195.07191" fx="446.77762" fy="1219.4125" gradientTransform="scale(1.04523 .95673)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<linearGradient id="g" x1="400.57785" x2="400.84448" y1="369.53015" y2="304.07886" gradientTransform="scale(.57526 1.73834)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
|
||||
<linearGradient id="h" x1="303.01761" x2="297.0856" y1="237.93179" y2="330.09561" gradientTransform="scale(1.11607 .896)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
|
||||
<linearGradient id="i" x1="378.93771" x2="380.27319" y1="278.60202" y2="243.91606" gradientTransform="scale(.8165 1.22474)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
|
||||
<linearGradient id="j" x1="381.38742" x2="380.5517" y1="277.495" y2="245.68338" gradientTransform="scale(.8165 1.22474)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
|
||||
<linearGradient id="k" x1="379.09573" x2="376.79556" y1="240.92712" y2="281.01636" gradientTransform="scale(.8165 1.22474)" gradientUnits="userSpaceOnUse" xlink:href="#d"/>
|
||||
<linearGradient id="l" x1="389.63535" x2="387.06866" y1="242.28218" y2="281.32513" gradientTransform="scale(.8165 1.22474)" gradientUnits="userSpaceOnUse" xlink:href="#d"/>
|
||||
<linearGradient id="m" x1="437.57941" x2="437.57941" y1="528.87177" y2="394.10361" gradientTransform="scale(.81285 1.23023)" gradientUnits="userSpaceOnUse" spreadMethod="reflect" xlink:href="#b"/>
|
||||
<linearGradient id="n" x1="375.17325" x2="377.48541" y1="419.78485" y2="324.03815" gradientTransform="scale(.64978 1.53897)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
|
||||
<linearGradient id="o" x1="320.75104" x2="321.32224" y1="498.17776" y2="614.50439" gradientTransform="scale(1.0748 .9304)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
|
||||
<linearGradient id="p" x1="322.48257" x2="323.2514" y1="435.26761" y2="488.48251" gradientTransform="scale(1.077 .9285)" gradientUnits="userSpaceOnUse" xlink:href="#d"/>
|
||||
<linearGradient id="q" x1="411.2215" x2="411.2215" y1="242.94365" y2="331.44858" gradientTransform="scale(.5717 1.74915)" gradientUnits="userSpaceOnUse" xlink:href="#d"/>
|
||||
<linearGradient id="r" x1="867.34546" x2="867.33453" y1="234.73897" y2="314.83911" gradientTransform="scale(.57267 1.74621)" gradientUnits="userSpaceOnUse" xlink:href="#d"/>
|
||||
<linearGradient id="s" x1="236.25362" x2="212.5099" y1="657.11133" y2="737.41229" gradientTransform="scale(1.01151 .98862)" gradientUnits="userSpaceOnUse" xlink:href="#e"/>
|
||||
<linearGradient id="t" x1="381.56607" x2="279.64313" y1="655.73102" y2="386.66583" gradientTransform="scale(1.0655 .93853)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
|
||||
<linearGradient id="u" x1="218.11714" x2="203.12654" y1="630.30475" y2="737.8537" gradientTransform="scale(1.00985 .99025)" gradientUnits="userSpaceOnUse" xlink:href="#e"/>
|
||||
<linearGradient id="v" x1="117.88966" x2="182.24524" y1="587.23602" y2="704.73077" gradientTransform="scale(1.00772 .99233)" gradientUnits="userSpaceOnUse" xlink:href="#d"/>
|
||||
<linearGradient id="w" x1="223.10072" x2="230.53499" y1="570.41809" y2="710.97723" gradientTransform="scale(.9995 1.0005)" gradientUnits="userSpaceOnUse" xlink:href="#d"/>
|
||||
<linearGradient id="x" x1="316.93988" x2="371.60889" y1="474.01779" y2="582.63507" gradientTransform="scale(1.0655 .93853)" gradientUnits="userSpaceOnUse" xlink:href="#d"/>
|
||||
<linearGradient id="y" x1="284.68652" x2="285.45923" y1="410.46326" y2="485.69934" gradientTransform="scale(1.21868 .82056)" gradientUnits="userSpaceOnUse" xlink:href="#e"/>
|
||||
<linearGradient id="z" x1="288.82358" x2="288.37628" y1="398.85422" y2="482.55939" gradientTransform="scale(1.22194 .81837)" gradientUnits="userSpaceOnUse" xlink:href="#d"/>
|
||||
</defs>
|
||||
<path fill="url(#f)" d="M670.88202 1166.6423a203.89551 186.63016 0 1 1-407.79102 0 203.89551 186.63016 0 1 1 407.79102 0z" transform="matrix(1.4177 0 0 .41474 -349.5468 157.94132)"/>
|
||||
<path d="M154.23535 558.54232c-27.985-40.27875-33.4425-171.23 31.85875-251.7875 32.3575-38.6475 40.6125-65.58125 43.16375-101.74125 1.73625-41.22375-29.15625-164.3025 87.35375-173.68 118.0075-9.415 111.67 107.075 110.99125 168.71875-.56375 52.05 38.2175 81.55125 64.745 122.11875 49.09875 74.5375 44.94625 202.875-9.24875 272.34-68.64625 86.96-127.44125 49.2475-166.4875 52.4125-73.11625 4.0025-75.53375 42.98875-162.37625-88.38125z"/>
|
||||
<path fill="url(#g)" d="M246.571 470.864c-12.239 12.496-44.396 69.092 4.869 105.36 17.369 12.633-16.377 59.495-32.005 36.308-27.57-41.618-8.831-106.941 8.315-130.188 11.652-16.487 29.23-22.676 18.821-11.48z" transform="matrix(-1.67739 -.02245 -.02112 1.4709 862.8276 -357.26968)"/>
|
||||
<path stroke="#000" stroke-width=".97729802" d="M256.513 459.837c-19.915 17.717-56.176 80.091-3.288 120.606 17.37 12.632-15.393 52.463-33.79 32.089-63.963-70.82 1.669-152.254 24.262-180.25 20.192-24.347 38.078 5.752 12.816 27.555z" transform="matrix(-1.67755 0 0 1.52374 863.8676 -382.33468)"/>
|
||||
<path fill="url(#h)" d="M399.56879 258.15753a58.37323 46.863022 0 1 1-116.74646 0 58.37323 46.863022 0 1 1 116.74646 0z" transform="matrix(1.26626 -.07137 -.04598 1.19574 -108.6094 -190.01368)"/>
|
||||
<path fill="url(#i)" d="M328.86324 320.64151a18.087479 27.131195 0 1 1-36.17496 0 18.087479 27.131195 0 1 1 36.17496 0z" transform="matrix(1.30445 -.07553 .07713 1.34257 -165.9954 -241.86968)"/>
|
||||
<path fill="url(#j)" d="M328.86324 320.64151a18.087479 27.131195 0 1 1-36.17496 0 18.087479 27.131195 0 1 1 36.17496 0z" transform="matrix(-1.81082 .04951 .03173 1.55333 896.7076 -349.02968)"/>
|
||||
<path d="M326.35672821 170.5767474a14.88487384 23.14205282 2.00317994 1 0 29.77908237.06371242 14.88487384 23.14205282 2.00317994 1 0-29.77908237-.06371242zm-52.12561644.13467082a10.82556484 19.2435689-4.93566296 1 1-21.50167273 2.6153013 10.82556484 19.2435689-4.93566296 1 1 21.50167273-2.6153013z"/>
|
||||
<path fill="url(#k)" d="M328.86324 320.64151a18.087479 27.131195 0 1 1-36.17496 0 18.087479 27.131195 0 1 1 36.17496 0z" transform="matrix(-.48032 -.03645 -.0468 .4756 502.7436 22.75972)"/>
|
||||
<path fill="url(#l)" d="M328.86324 320.64151a18.087479 27.131195 0 1 1-36.17496 0 18.087479 27.131195 0 1 1 36.17496 0z" transform="matrix(.35691 -.04082 .04132 .39854 138.5816 50.73832)"/>
|
||||
<path fill="url(#m)" d="M258.702 495.425c12.836-29.103 40.114-80.226 40.695-119.758 0-31.442 94.179-38.951 101.737-7.558 7.558 31.393 26.741 78.483 38.95 101.156 12.208 22.672 47.809 94.695 9.884 157.546-34.157 55.644-137.725 99.666-193.01-7.557-18.603-37.207-15.285-83.315 1.744-123.829z" transform="matrix(1.25 0 0 1.25 -125.2984 -232.48268)"/>
|
||||
<path fill="url(#n)" d="M242.905 473.815c-11.263 18.967-35.5 69.309 12.137 102.047 51.311 34.82 46.473 97.062-15.607 61.955-56.777-31.789-22.845-137.778-4.51-162.266 12.107-17.214 29.897-38.031 7.98-1.736z" transform="matrix(1.38936 -.11107 .10221 1.30214 -202.3394 -230.19068)"/>
|
||||
<path stroke="#000" stroke-width="1.5625" d="M195.34285 330.39232c-21.83125 35.635-74.22125 119.76625-4.11 163.40375 94.46125 57.94375 67.71625 115.88875-18.49875 63.7175-121.38625-72.83-14.78125-219.52 33.91375-275.4225 55.555-62.75 10.6925 11.35875-11.305 48.30125z"/>
|
||||
<path fill="url(#o)" d="M421.481 504.727c0 32.412-29.272 74.516-79.528 74.138-51.828.455-73.949-41.726-73.949-74.138 0-32.412 34.379-58.717 76.739-58.717 42.359 0 76.738 26.305 76.738 58.717z" font-size="12" transform="matrix(1.30209 0 0 1.22525 -140.7104 -217.80968)"/>
|
||||
<path fill="url(#p)" d="M398.227 412.292c-.612 38.572-23.18 47.671-51.74 47.671-28.561 0-49.292-5.694-51.741-47.671 0-26.314 23.18-41.542 51.741-41.542 28.56 0 51.74 15.228 51.74 41.542z" font-size="12" transform="matrix(1.1868 0 0 1.06708 -100.1294 -164.33068)"/>
|
||||
<path fill="url(#q)" d="M234.285 456.475c17.716-26.996 55.015-68.364 6.977 5.813-38.951 61.043-14.403 100.273-1.744 111.039 36.527 32.562 34.966 54.349 6.395 37.206-61.625-36.626-48.835-98.248-11.628-154.058z" transform="matrix(1.25 0 0 1.25 -125.2984 -231.75768)"/>
|
||||
<path fill="url(#r)" d="M490.662 467.52c-15.319-31.701-64.134-111.902 2.326-18.603 60.461 84.297 18.022 143.013 10.464 148.827-7.557 5.813-33.137 17.44-25.579-2.907 7.557-20.347 45.234-58.973 12.789-127.317z" transform="matrix(1.25 0 0 1.25 -125.2984 -231.75768)"/>
|
||||
<path fill="url(#s)" stroke="#e68c3f" stroke-width="6.25" d="M220.915 716.921c-40.442-21.416-99.252 4.124-77.902-54.066 4.276-13.238-6.375-33.008.581-45.926 8.139-15.698 25.58-12.209 36.045-22.674 10.318-10.891 16.859-29.649 36.044-26.742 19.184 2.907 31.945 26.461 45.344 55.229 9.883 20.638 44.941 49.664 42.65 72.758-2.696 35.5-43.027 42.19-82.762 21.421z" transform="matrix(1.25 0 0 1.25 -125.2984 -231.75768)"/>
|
||||
<path fill="url(#t)" d="M415.072 495.764c-3.007 24.906-35.813 76.627-69.518 81.534-34.26 5.336-66.432-34.06-74.147-71.114-9.889-41.206 22.587-57.6 71.938-56.627 53.301 1.654 74.121 13.891 71.727 46.207z" font-size="12" transform="matrix(.5982 .26858 -.23962 .61721 389.8156 76.21132)"/>
|
||||
<path fill="url(#u)" stroke="#e68c3f" stroke-width="6.25067997" d="M220.274 718.402c-41.327-23.59-99.894 5.605-77.261-55.547 4.736-13.068-6.596-33.552.36-46.47 8.139-15.698 25.801-11.665 36.266-22.13 10.318-10.891 18.827-27.868 38.012-24.961 19.184 2.907 29.977 24.68 43.376 53.448 9.883 20.638 43.415 48.971 41.124 72.065-2.696 35.5-42.724 45.471-81.877 23.595z" transform="matrix(-1.1685 .42314 .47528 1.16478 417.5906 -278.07368)"/>
|
||||
<path fill="url(#v)" d="M216.482 675.68c-86.531-57.503-47.308-70.96-36.843-81.425 10.318-10.891 18.827-27.868 38.012-24.961 19.184 2.907 29.977 24.68 43.376 53.448 9.883 20.638 43.06 48.918 41.124 72.065-2.616 27.11-48.19 5.487-85.669-19.127z" transform="matrix(-.9451 .34375 .42408 .95606 403.5756 -128.59468)"/>
|
||||
<path fill="url(#w)" d="M216.506 677.071c-86.531-57.503-46.797-73.57-33.946-81.28 15.399-9.942 15.158-30.831 34.343-27.924 19.184 2.907 30.725 26.107 44.124 54.875 9.883 20.638 43.06 48.918 41.124 72.065-2.616 27.11-48.166 6.878-85.645-17.736z" transform="matrix(1.00431 -.05229 -.0174 1.04575 -66.5614 -92.71798)"/>
|
||||
<path d="M514.16392618 492.89388153c-6.69409764 11.6729672-34.29007759 30.19600752-52.6812594 25.36804862-18.76593057-4.73521422-27.21700072-31.1485461-23.54211688-51.13877146 3.41150018-22.51206827 23.54139341-23.63093696 48.7850296-12.52637614 27.13954878 12.29170413 35.346749 22.8266733 27.43834668 38.29709898z" font-size="12"/>
|
||||
<path fill="url(#x)" d="M415.072 495.764c-3.007 24.906-35.813 76.627-69.518 81.534-34.26 5.336-66.432-34.06-74.147-71.114-9.889-41.206 22.587-57.6 71.938-56.627 53.301 1.654 74.121 13.891 71.727 46.207z" font-size="12" transform="matrix(.35123 .14946 -.12886 .34347 413.7696 254.03832)"/>
|
||||
<path fill="url(#y)" stroke="#e68c3f" stroke-width="3.75" d="M309.954 338.729c7.147-6.77 24.811-27.066 57.961-5.755 6.162 4.01 11.162 4.377 23.021 9.455 23.726 9.749 12.382 33.259-12.744 41.108-10.758 3.489-20.536 16.556-40.129 15.439-16.734-.977-21.119-11.874-31.398-17.906-18.269-10.311-20.965-24.254-11.1-31.653 9.866-7.399 13.725-10.059 14.389-10.688z" transform="matrix(1.25 0 0 1.25 -125.2984 -231.75768)"/>
|
||||
<path fill="none" stroke="#e68c3f" stroke-width="3.125" d="M363.76535 215.29857c-12.35375.72625-39.24125 27.61375-67.5825 27.61375-28.34125 0-45.055-26.16125-49.415-26.16125"/>
|
||||
<path fill="url(#z)" d="M309.954 338.729c7.147-6.77 29.691-25.348 59.588-6.328 6.299 3.766 12.804 7.865 22.478 13.464 19.162 11.748 9.671 28.678-13.286 39.39-10.418 4.495-27.593 14.415-40.671 13.721-14.533-1.408-23.935-11.399-33.567-17.906-17.67-12.303-16.597-22.237-8.389-30.508 6.205-5.679 13.183-11.204 13.847-11.833z" transform="matrix(.62789 0 0 .59567 81.6136 -12.43538)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="447px" height="336px" viewBox="0 0 447 336" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Rectangle</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
|
||||
<stop stop-color="#1E54F6" offset="0%"></stop>
|
||||
<stop stop-color="#EE2323" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<rect id="path-2" x="0" y="0" width="447" height="336"></rect>
|
||||
</defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Rectangle">
|
||||
<use fill="#D8D8D8" fill-rule="evenodd" xlink:href="#path-2"></use>
|
||||
<rect stroke="url(#linearGradient-1)" stroke-width="10" x="5" y="5" width="437" height="326"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 992 B |
@@ -12,7 +12,7 @@
|
||||
|
||||
- (IJSVG *)svg
|
||||
{
|
||||
return [[IJSVG svgNamed:@"paperplane"] retain];
|
||||
return [[IJSVG svgNamed:@"heart"] retain];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -11,10 +11,9 @@
|
||||
|
||||
@interface SVGView : NSView {
|
||||
|
||||
@private
|
||||
IJSVG * svg;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -22,8 +22,9 @@
|
||||
if( ( self = [super initWithFrame:frameRect] ) != nil )
|
||||
{
|
||||
svg = [self svg];
|
||||
svg.renderQuality = IJSVGRenderQualityLow;
|
||||
svg.renderingBackingScaleHelper = ^{
|
||||
return [svg computeBackingScale:self.window.backingScaleFactor];
|
||||
return self.window.backingScaleFactor;
|
||||
};
|
||||
}
|
||||
return self;
|
||||
@@ -31,7 +32,7 @@
|
||||
|
||||
- (IJSVG *)svg
|
||||
{
|
||||
return [[IJSVG svgNamed:@"test"] retain];
|
||||
return [IJSVG svgNamed:@"car"];
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect
|
||||
|
||||
|
After Width: | Height: | Size: 515 KiB |
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="595.279px" height="841.89px" viewBox="0 0 595.279 841.89" enable-background="new 0 0 595.279 841.89"
|
||||
xml:space="preserve">
|
||||
<radialGradient id="SVGID_1_" cx="229.116" cy="650.3413" r="160.6544" gradientTransform="matrix(0.1736 0.9848 0.4924 -0.0868 -59.0956 137.9539)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#FFFFFF"/>
|
||||
<stop offset="0.0573" style="stop-color:#CECECE"/>
|
||||
<stop offset="0.1221" style="stop-color:#9E9E1F"/>
|
||||
<stop offset="0.1907" style="stop-color:#74D974"/>
|
||||
<stop offset="0.2638" style="stop-color:#5050D1"/>
|
||||
<stop offset="0.5767" style="stop-color:#D44033"/>
|
||||
<stop offset="0.6687" style="stop-color:#D000FF"/>
|
||||
<stop offset="0.773" style="stop-color:#0C0CE3"/>
|
||||
<stop offset="0.8528" style="stop-color:#030303"/>
|
||||
<stop offset="0.9632" style="stop-color:#000000"/>
|
||||
</radialGradient>
|
||||
<ellipse fill="url(#SVGID_1_)" stroke="#000000" stroke-miterlimit="10" cx="251.667" cy="315.818" rx="185.064" ry="131.819"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf8" standalone="no"?>
|
||||
<svg viewBox="0 0 372.77139 350.83377" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<clipPath id="clipPath4110">
|
||||
<path d="m1349.1479-106.15129v-302.89728c-47.8699-80.88268-146.0391-44.39418-170.2081,0-74.5507,130.56653 130.5076,196.53622 170.2081,302.89728z"/>
|
||||
<path d="m1349.1479-106.15129v-302.89728c47.8699-80.88268 146.0391-44.39418 170.2081,0 74.5507,130.56653-130.5076,196.53622-170.2081,302.89728z"/>
|
||||
</clipPath>
|
||||
<filter height="1.35511" id="filter4031" width="1.33422" x="-.16711" y="-.17756">
|
||||
<feGaussianBlur stdDeviation="25.9554"/>
|
||||
</filter>
|
||||
<linearGradient id="linearGradient4159">
|
||||
<stop offset="0" stop-color="#fff"/>
|
||||
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient gradientUnits="userSpaceOnUse" id="linearGradient4169" x2="0" xlink:href="#linearGradient4159" y1="386.94983" y2="490.03617"/>
|
||||
<linearGradient gradientUnits="userSpaceOnUse" id="linearGradient4165" x2="0" xlink:href="#linearGradient4159" y1="387.32314" y2="490.40948"/>
|
||||
</defs>
|
||||
<g transform="translate(-202.18571,-396.94529)">
|
||||
<g clip-path="url(#clipPath4110)" transform="translate(-960.57649,853.93036)">
|
||||
<path d="m1349.1479-106.15129v-302.89728c-47.8699-80.88268-146.0391-44.39418-170.2081,0-74.5507,130.56653 130.5076,196.53622 170.2081,302.89728z"/>
|
||||
<path d="m1349.1479-106.15129v-302.89728c47.8699-80.88268 146.0391-44.39418 170.2081,0 74.5507,130.56653-130.5076,196.53622-170.2081,302.89728z"/>
|
||||
</g>
|
||||
<g clip-path="url(#clipPath4110)" fill="#f00" filter="url(#filter4031)" transform="translate(-960.57649,853.93036)">
|
||||
<path d="m1349.1479-106.15129v-302.89728c-47.8699-80.88268-146.0391-44.39418-170.2081,0-74.5507,130.56653 130.5076,196.53622 170.2081,302.89728z" fill="#f00"/>
|
||||
<path d="m1349.1479-106.15129v-302.89728c47.8699-80.88268 146.0391-44.39418 170.2081,0 74.5507,130.56653-130.5076,196.53622-170.2081,302.89728z" fill="#f00"/>
|
||||
</g>
|
||||
<path d="m370.99493,455.63757c0,30.71925-28.70788,20.90496-64.58143,20.90496s-65.328,9.81429-65.328-20.90496 29.08123-55.62213 64.95466-55.62213 64.95477,24.90287 64.95477,55.62213z" fill="url(#linearGradient4165)"/>
|
||||
<path d="m535.62152,455.26427c0,30.71925-28.7079,20.90496-64.5815,20.90496s-65.328,9.81429-65.328-20.90496 29.0813-55.62213 64.9547-55.62213 64.9548,24.90287 64.9548,55.62213z" fill="url(#linearGradient4169)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<radialGradient id="s1" fx=".4" fy=".2">
|
||||
<stop stop-color="#FE8"/><stop stop-color="#D70" offset="1"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="s2" fx=".8" fy=".5" xlink:href="#s1"/>
|
||||
<radialGradient id="s3" fx=".5" fy=".9" xlink:href="#s1"/>
|
||||
<radialGradient id="s4" fx=".1" fy=".5" xlink:href="#s1"/>
|
||||
</defs>
|
||||
<g stroke="#940">
|
||||
<path d="M73,29c-37-40-62-24-52,4l6-7c-8-16,7-26,42,9z" fill="url(#s1)"/>
|
||||
<path d="M47,8c33-16,48,21,9,47l-6-5c38-27,20-44,5-37z" fill="url(#s2)"/>
|
||||
<path d="M77,32c22,30,10,57-39,51l-1-8c3,3,67,5,36-36z" fill="url(#s3)"/>
|
||||
<path d="M58,84c-4,20-38-4-8-24l-6-5c-36,43,15,56,23,27z" fill="url(#s4)"/>
|
||||
<path d="M40,14c-40,37-37,52-9,68l1-8c-16-13-29-21,16-56z" fill="url(#s1)"/>
|
||||
<path d="M31,33c19,23,20,7,35,41l-9,1.7c-4-19-8-14-31-37z" fill="url(#s2)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 962 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 102">
|
||||
<radialGradient id="jsongrad" cx="65" cy="90" r="100" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#EEF"/><stop offset="1"/></radialGradient>
|
||||
<path d="M61,02 A 49,49 0,0,0 39,98 C 9,79 10,24 45,25 C 72,24 65,75 50,75 C 93,79 91,21 62,02" id="jsonswirl" fill="url(#jsongrad)"/>
|
||||
<use xlink:href="#jsonswirl" transform="rotate(180 50,50)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 472 B |
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="744.09448819"
|
||||
height="1052.3622047"
|
||||
id="svg2"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.46"
|
||||
sodipodi:docname="drawing.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||
<defs id="defs4">
|
||||
<linearGradient id="linearGradient3193">
|
||||
<stop style="stop-color:#0000ff;stop-opacity:1;" offset="0" id="stop3195" />
|
||||
<stop style="stop-color:#ff0000;stop-opacity:1;" offset="1" id="stop3197" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3193"
|
||||
id="linearGradient3199"
|
||||
x1="128.57143"
|
||||
y1="289.50504"
|
||||
x2="425.71428"
|
||||
y2="289.50504"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(1.1,1.1) translate(20,20) rotate(10) scale(.9,.9)">
|
||||
</linearGradient>
|
||||
<radialGradient id="radialGradient3193">
|
||||
<stop style="stop-color:#0000ff;stop-opacity:1;" offset="0" />
|
||||
<stop style="stop-color:#ff0000;stop-opacity:1;" offset="1" />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
xlink:href="#radialGradient3193"
|
||||
id="radialGradient3199"
|
||||
cx="300.57143"
|
||||
cy="529.50504"
|
||||
fx="400.57143"
|
||||
fy="529.50504"
|
||||
r="100.71428"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(1.1,1.1) translate(20,20) rotate(10) scale(.9,.9)">
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<rect
|
||||
style="fill:url(#linearGradient3199);fill-opacity:1"
|
||||
id="rect3191"
|
||||
width="397.14285"
|
||||
height="157.14285"
|
||||
x="128.57143"
|
||||
y="160.93361" />
|
||||
<rect
|
||||
style="fill:url(#radialGradient3199);fill-opacity:1"
|
||||
id="rect3192"
|
||||
width="397.14285"
|
||||
height="157.14285"
|
||||
x="128.57143"
|
||||
y="400.93361" />
|
||||
|
||||
<rect id='myrect' x="400" y="20" width="60" height="60" rx="3" ry="3"
|
||||
style="opacity:0.5;fill:#0000FF;stroke:rgb(0, 255, 0);stroke-width:10px;stroke-opacity:0.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:15;stroke-dasharray:5,3,2;stroke-dashoffset:3"/>
|
||||
|
||||
|
||||
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 590 KiB After Width: | Height: | Size: 522 KiB |
@@ -6,18 +6,6 @@ Orignaly written for IconJar (in development)
|
||||
|
||||
It also supports the NSPasteboards writing protocol, an IJSVG object can be put onto the pasteboard and application like Sketch and Photoshop can paste them into the document as vector objects (generated PDF's on the fly).
|
||||
|
||||
Example app
|
||||
====
|
||||
There is an example application provided, it will generate this test bed for SVG's
|
||||
|
||||
The example screen contains six SVG's rendered in individual views, from left to right.
|
||||
* First example shows transforms and colours
|
||||
* Second example shows linear gradients
|
||||
* Third example shows defined paths being reused
|
||||
* Forth example shows transforms such as translate being used and stroke colours
|
||||
* Fith example shows clip paths being used
|
||||
* Sixth example shows dashed stroke array's being used
|
||||
|
||||
Quick Start
|
||||
====
|
||||
Add all the IJSVG library files into your project, import the IJSVG.h into the files you wish to use the SVG's. The easiest way to
|
||||
@@ -39,12 +27,49 @@ IJSVG does allow you to directly draw the SVG into any focused drawing context f
|
||||
[svg drawInRect:self.bounds];
|
||||
}
|
||||
|
||||
#### Helpers
|
||||
|
||||
IJSVG provides a very simple way of helping out the backing scale factor of the drawing context when the SVG is drawn. Due to CALayers defaulting to 1.0 when custom drawing methods are implemented, they do not know about your backing scale factor. Luckily you can simply do this:
|
||||
|
||||
__block IJSVG * svg ....
|
||||
svg.renderingBackingScaleHelper = ^{
|
||||
return [svg computeBackingScale:someView.window.backingScaleFactor];
|
||||
};
|
||||
|
||||
# Exporting
|
||||
#### Yes, thats right... CALayers -> SVG (whos idea was this?!)
|
||||
|
||||
IJSVG provides a way of exporting the rendered layer tree back to an SVG file. This isnt 100% perfect but it does a good job of generating what IJSVG renders (have yet to find an issue).
|
||||
|
||||
Its a simple as doing this:
|
||||
|
||||
IJSVG * svg ...
|
||||
IJSVGExporter * exporter = [[IJSVGExporter alloc] initWithSVG:svg options:IJSVGExporterOptionAll];
|
||||
NSString * svgString = [exporter SVGString];
|
||||
|
||||
Which will give you back the SVG code to put into a file, there are various options you can give it for more XML manipulation such s collpasing groups and converting transform's from matrix's back to their human readable counter parts.
|
||||
|
||||
The fun part is you can actually create an SVG object from a IJSVGLayer and work with them without needing to load a file, for example:
|
||||
|
||||
// create group layer and a shape layer (subclass of CAShapeLayer)
|
||||
IJSVGGroupLayer * baseSVGGroup = ....
|
||||
IJSVGShapeLayer * shapeLayer = ....
|
||||
[baseSVGGroup addSublayer:shapeLayer];
|
||||
|
||||
// create the SVG - note the viewbox!
|
||||
IJSVG * svg = [[IJSVG alloc] initWithSVGLayer:baseSVGGroup viewBox:NSMakeRect(0.f, 0.f, 50.f, 50.f)];
|
||||
.... do what you want
|
||||
|
||||
and now you would have a usuable SVG to render where ever you would like and more importantly you can now export that back to an SVG file.
|
||||
|
||||
All layers must be of generic type IJSVGLayer or IJSVGShapeLayer, it will throw an exception if you do not use these. Also if you go out the scope of what those layers can do, it wont render nor will it export (get malformed results).
|
||||
|
||||
# What it supports
|
||||
* Elements: def, use, g, path, clipPath, circle, elipse, rect, polyline, polygon and line (supports groups heirachy and inheritance, clip-paths etc)
|
||||
* Commands: A, M, L, H, V, C, S, T, Q and Z and full support for multiple parameters of each type
|
||||
* Transformations: matrix, rotate (not around a point - currently), translate, scale transformations
|
||||
* Transformations: matrix, rotate, translate, scale and skew transformations
|
||||
* Stroking: stroking, stroke color, stroke opacity, dashed, dashed offset and phase, stroke line cap style
|
||||
* Filling: fill color, fill mode (winding rules), fill opacity, linear gradients, radial gradients
|
||||
* Filling: fill color, fill mode (winding rules), fill opacity, linear gradients, radial gradients and patterns
|
||||
* Color: supports all predefined colors from the SVG spec and hex values
|
||||
* Caching: has basic caching implemenation
|
||||
* CSS: Basic embedded style sheets are support with very basic selectors
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-cayman
|
||||
@@ -12,8 +12,11 @@
|
||||
#import "IJSVGLayerTree.h"
|
||||
#import "IJSVGGroupLayer.h"
|
||||
#import "IJSVGImageLayer.h"
|
||||
#import "IJSVGExporter.h"
|
||||
#import "IJSVGRendering.h"
|
||||
|
||||
@class IJSVG;
|
||||
@class IJSVGQuartzRenderer;
|
||||
|
||||
void IJSVGBeginTransactionLock();
|
||||
void IJSVGEndTransactionLock();
|
||||
@@ -33,8 +36,6 @@ withSVGString:(NSString *)subSVGString;
|
||||
|
||||
@end
|
||||
|
||||
typedef CGFloat (^IJSVGRenderingBackingScaleFactorHelper)();
|
||||
|
||||
@interface IJSVG : NSObject <NSPasteboardWriting, IJSVGParserDelegate> {
|
||||
|
||||
@private
|
||||
@@ -46,6 +47,10 @@ typedef CGFloat (^IJSVGRenderingBackingScaleFactorHelper)();
|
||||
CGRect _viewBox;
|
||||
CGSize _proposedViewSize;
|
||||
CGFloat _lastProposedBackingScale;
|
||||
IJSVGRenderQuality _lastProposedRenderQuality;
|
||||
CGFloat _backingScale;
|
||||
NSMutableDictionary * _replacementColors;
|
||||
IJSVGQuartzRenderer * _quartzRenderer;
|
||||
|
||||
struct {
|
||||
unsigned int shouldHandleForeignObject: 1;
|
||||
@@ -65,14 +70,22 @@ typedef CGFloat (^IJSVGRenderingBackingScaleFactorHelper)();
|
||||
// fillColor, strokeColor, pattern and gradient fill
|
||||
@property (nonatomic, retain) NSColor * fillColor;
|
||||
@property (nonatomic, retain) NSColor * strokeColor;
|
||||
@property (nonatomic, assign) CGFloat strokeWidth;
|
||||
@property (nonatomic, assign) IJSVGLineCapStyle lineCapStyle;
|
||||
@property (nonatomic, assign) IJSVGLineJoinStyle lineJoinStyle;
|
||||
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
|
||||
@property (nonatomic, assign) BOOL clipToViewport;
|
||||
|
||||
- (void)prepForDrawingInView:(NSView *)view;
|
||||
- (BOOL)isFont;
|
||||
- (IJSVGGroup *)rootNode;
|
||||
- (NSRect)viewBox;
|
||||
- (NSArray *)glyphs;
|
||||
- (NSArray<IJSVGPath *> *)glyphs;
|
||||
- (NSString *)identifier;
|
||||
- (IJSVGLayer *)layer;
|
||||
- (IJSVGLayer *)layerWithTree:(IJSVGLayerTree *)tree;
|
||||
- (NSArray<IJSVG *> *)subSVGs:(BOOL)recursive;
|
||||
- (NSString *)SVGStringWithOptions:(IJSVGExporterOptions)options;
|
||||
|
||||
- (CGFloat)computeBackingScale:(CGFloat)scale;
|
||||
- (void)discardDOM;
|
||||
@@ -135,6 +148,8 @@ typedef CGFloat (^IJSVGRenderingBackingScaleFactorHelper)();
|
||||
- (BOOL)drawInRect:(NSRect)rect;
|
||||
- (BOOL)drawInRect:(NSRect)rect
|
||||
error:(NSError **)error;
|
||||
- (void)drawInRect:(NSRect)rect
|
||||
context:(CGContextRef)context;
|
||||
|
||||
- (NSData *)PDFData;
|
||||
- (NSData *)PDFData:(NSError **)error;
|
||||
@@ -142,4 +157,15 @@ typedef CGFloat (^IJSVGRenderingBackingScaleFactorHelper)();
|
||||
- (NSData *)PDFDataWithRect:(NSRect)rect
|
||||
error:(NSError **)error;
|
||||
|
||||
- (void)beginVectorDraw;
|
||||
- (void)endVectorDraw;
|
||||
|
||||
// colors
|
||||
- (NSArray<NSColor *> *)visibleColors;
|
||||
- (void)setReplacementColors:(NSDictionary<NSColor *, NSColor *> *)colors;
|
||||
- (void)removeReplacementColor:(NSColor *)color;
|
||||
- (void)replaceColor:(NSColor *)color
|
||||
withColor:(NSColor *)newColor;
|
||||
- (void)removeAllReplacementColors;
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,21 +9,31 @@
|
||||
#import "IJSVG.h"
|
||||
#import "IJSVGCache.h"
|
||||
#import "IJSVGTransaction.h"
|
||||
#import "IJSVGExporter.h"
|
||||
|
||||
@implementation IJSVG
|
||||
|
||||
@synthesize fillColor;
|
||||
@synthesize strokeColor;
|
||||
@synthesize strokeWidth;
|
||||
@synthesize lineCapStyle;
|
||||
@synthesize lineJoinStyle;
|
||||
@synthesize renderingBackingScaleHelper;
|
||||
@synthesize clipToViewport;
|
||||
@synthesize renderQuality;
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
IJSVGBeginTransactionLock();
|
||||
[renderingBackingScaleHelper release], renderingBackingScaleHelper = nil;
|
||||
[fillColor release], fillColor = nil;
|
||||
[strokeColor release], strokeColor = nil;
|
||||
[_group release], _group = nil;
|
||||
[_layerTree release], _layerTree = nil;
|
||||
[_replacementColors release], _replacementColors = nil;
|
||||
[_quartzRenderer release], _quartzRenderer = nil;
|
||||
[super dealloc];
|
||||
IJSVGEndTransactionLock();
|
||||
}
|
||||
|
||||
+ (id)svgNamed:(NSString *)string
|
||||
@@ -257,6 +267,7 @@
|
||||
[IJSVGCache cacheSVG:self
|
||||
fileURL:aURL];
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
return self;
|
||||
@@ -327,7 +338,14 @@
|
||||
|
||||
- (void)_setupBasicsFromAnyInitializer
|
||||
{
|
||||
_lastProposedBackingScale = 1.f;
|
||||
self.clipToViewport = YES;
|
||||
self.renderQuality = IJSVGRenderQualityOptimized;
|
||||
|
||||
// setup low level backing scale
|
||||
_lastProposedBackingScale = 0.f;
|
||||
self.renderingBackingScaleHelper = ^CGFloat{
|
||||
return 1.f;
|
||||
};
|
||||
}
|
||||
|
||||
- (NSString *)identifier
|
||||
@@ -347,12 +365,17 @@
|
||||
return _viewBox;
|
||||
}
|
||||
|
||||
- (IJSVGGroup *)rootNode
|
||||
{
|
||||
return _group;
|
||||
}
|
||||
|
||||
- (BOOL)isFont
|
||||
{
|
||||
return [_group isFont];
|
||||
}
|
||||
|
||||
- (NSArray *)glyphs
|
||||
- (NSArray<IJSVGPath *> *)glyphs
|
||||
{
|
||||
return [_group glyphs];
|
||||
}
|
||||
@@ -362,6 +385,14 @@
|
||||
return [_group subSVGs:recursive];
|
||||
}
|
||||
|
||||
- (NSString *)SVGStringWithOptions:(IJSVGExporterOptions)options
|
||||
{
|
||||
IJSVGExporter * exporter = [[[IJSVGExporter alloc] initWithSVG:self
|
||||
size:self.viewBox.size
|
||||
options:options] autorelease];
|
||||
return [exporter SVGString];
|
||||
}
|
||||
|
||||
- (NSImage *)imageWithSize:(NSSize)aSize
|
||||
{
|
||||
return [self imageWithSize:aSize
|
||||
@@ -391,7 +422,7 @@
|
||||
{
|
||||
NSImage * im = [[[NSImage alloc] initWithSize:aSize] autorelease];
|
||||
[im lockFocus];
|
||||
CGContextRef ref = [[NSGraphicsContext currentContext] graphicsPort];
|
||||
CGContextRef ref = [[NSGraphicsContext currentContext] CGContext];
|
||||
CGContextSaveGState(ref);
|
||||
if(flipped) {
|
||||
CGContextTranslateCTM(ref, 0.f, aSize.height);
|
||||
@@ -444,10 +475,13 @@
|
||||
CGContextScaleCTM( context, 1, -1 );
|
||||
CGContextTranslateCTM( context, 0, -box.size.height);
|
||||
|
||||
// draw the icon
|
||||
[self _drawInRect:(NSRect)box
|
||||
context:context
|
||||
error:error];
|
||||
// make sure we set the masks to path bits n bobs
|
||||
[self _beginVectorDraw]; {
|
||||
// draw the icon
|
||||
[self _drawInRect:(NSRect)box
|
||||
context:context
|
||||
error:error];
|
||||
} [self _endVectorDraw];
|
||||
|
||||
CGContextEndPage(context);
|
||||
|
||||
@@ -458,6 +492,56 @@
|
||||
return data;
|
||||
}
|
||||
|
||||
- (void)endVectorDraw
|
||||
{
|
||||
[self _endVectorDraw];
|
||||
}
|
||||
|
||||
- (void)beginVectorDraw
|
||||
{
|
||||
[self _beginVectorDraw];
|
||||
}
|
||||
|
||||
- (void)_beginVectorDraw
|
||||
{
|
||||
// turn on converts masks to PDF's
|
||||
// as PDF context and layer masks dont work
|
||||
void (^block)(CALayer * layer, BOOL isMask) = ^void (CALayer * layer, BOOL isMask) {
|
||||
((IJSVGLayer *)layer).convertMasksToPaths = YES;
|
||||
};
|
||||
[IJSVGLayer recursivelyWalkLayer:self.layer
|
||||
withBlock:block];
|
||||
}
|
||||
|
||||
- (void)_endVectorDraw
|
||||
{
|
||||
// turn of convert masks to paths as not
|
||||
// needed for generic rendering
|
||||
void (^block)(CALayer * layer, BOOL isMask) = ^void (CALayer * layer, BOOL isMask) {
|
||||
((IJSVGLayer *)layer).convertMasksToPaths = NO;
|
||||
};
|
||||
[IJSVGLayer recursivelyWalkLayer:self.layer
|
||||
withBlock:block];
|
||||
}
|
||||
|
||||
- (void)prepForDrawingInView:(NSView *)view
|
||||
{
|
||||
// kill the render
|
||||
if(view == nil) {
|
||||
self.renderingBackingScaleHelper = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
// construct the layer before drawing
|
||||
[self layer];
|
||||
|
||||
// set the scale
|
||||
__block NSView * weakView = view;
|
||||
self.renderingBackingScaleHelper = ^CGFloat{
|
||||
return weakView.window.screen.backingScaleFactor;
|
||||
};
|
||||
}
|
||||
|
||||
- (BOOL)drawAtPoint:(NSPoint)point
|
||||
size:(NSSize)aSize
|
||||
{
|
||||
@@ -484,12 +568,13 @@
|
||||
error:(NSError **)error
|
||||
{
|
||||
return [self _drawInRect:rect
|
||||
context:[[NSGraphicsContext currentContext] graphicsPort]
|
||||
context:[[NSGraphicsContext currentContext] CGContext]
|
||||
error:error];
|
||||
}
|
||||
|
||||
- (CGFloat)computeBackingScale:(CGFloat)actualScale
|
||||
{
|
||||
_backingScale = actualScale;
|
||||
return (CGFloat)(_scale + actualScale);
|
||||
}
|
||||
|
||||
@@ -499,8 +584,8 @@
|
||||
// we also need to calculate the viewport so we can clip
|
||||
// the drawing if needed
|
||||
NSRect viewPort = NSZeroRect;
|
||||
viewPort.origin.x = round(rect.size.width/2-(_proposedViewSize.width/2)*_clipScale);
|
||||
viewPort.origin.y = round(rect.size.height/2-(_proposedViewSize.height/2)*_clipScale);;
|
||||
viewPort.origin.x = round((rect.size.width/2-(_proposedViewSize.width/2)*_clipScale) + rect.origin.x);
|
||||
viewPort.origin.y = round((rect.size.height/2-(_proposedViewSize.height/2)*_clipScale) + rect.origin.y);
|
||||
viewPort.size.width = _proposedViewSize.width*_clipScale;
|
||||
viewPort.size.height = _proposedViewSize.height*_clipScale;
|
||||
|
||||
@@ -522,6 +607,14 @@
|
||||
return viewPort;
|
||||
}
|
||||
|
||||
- (void)drawInRect:(NSRect)rect
|
||||
context:(CGContextRef)context
|
||||
{
|
||||
[self _drawInRect:rect
|
||||
context:context
|
||||
error:nil];
|
||||
}
|
||||
|
||||
- (BOOL)_drawInRect:(NSRect)rect
|
||||
context:(CGContextRef)ref
|
||||
error:(NSError **)error
|
||||
@@ -530,63 +623,71 @@
|
||||
@synchronized (self) {
|
||||
CGContextSaveGState(ref);
|
||||
@try {
|
||||
|
||||
[self _beginDraw:rect];
|
||||
|
||||
// scale the whole drawing context, but first, we need
|
||||
// to translate the context so its centered
|
||||
CGFloat tX = round(rect.size.width/2-(_viewBox.size.width/2)*_scale);
|
||||
CGFloat tY = round(rect.size.height/2-(_viewBox.size.height/2)*_scale);
|
||||
|
||||
// we also need to calculate the viewport so we can clip
|
||||
// the drawing if needed
|
||||
BOOL canDraw = NO;
|
||||
NSRect viewPort = [self computeRectDrawingInRect:rect
|
||||
isValid:&canDraw];
|
||||
NSRect viewPort = [self computeRectDrawingInRect:rect isValid:&canDraw];
|
||||
// check the viewport
|
||||
if( !canDraw ) {
|
||||
if( canDraw == NO ) {
|
||||
if( error != NULL ) {
|
||||
*error = [[[NSError alloc] initWithDomain:IJSVGErrorDomain
|
||||
code:IJSVGErrorDrawing
|
||||
userInfo:nil] autorelease];
|
||||
}
|
||||
CGContextRestoreGState(ref);
|
||||
return NO;
|
||||
} else {
|
||||
// clip to mask
|
||||
if(self.clipToViewport == YES) {
|
||||
CGContextClipToRect( ref, viewPort);
|
||||
}
|
||||
|
||||
// add the origin back onto the viewport
|
||||
viewPort.origin.x -= round((_viewBox.origin.x)*_scale);
|
||||
viewPort.origin.y -= round((_viewBox.origin.y)*_scale);
|
||||
viewPort = CGRectIntegral(viewPort);
|
||||
|
||||
// transforms
|
||||
CGContextTranslateCTM( ref, viewPort.origin.x, viewPort.origin.y);
|
||||
CGContextScaleCTM( ref, _scale, _scale );
|
||||
|
||||
// render the layer, its really important we lock
|
||||
// the transaction when drawing
|
||||
IJSVGBeginTransactionLock();
|
||||
// do we need to update the backing scales on the
|
||||
// layers?
|
||||
if(self.renderingBackingScaleHelper != nil) {
|
||||
[self _askHelperForBackingScale];
|
||||
}
|
||||
|
||||
CGInterpolationQuality quality;
|
||||
switch(self.renderQuality) {
|
||||
case IJSVGRenderQualityLow: {
|
||||
quality = kCGInterpolationLow;
|
||||
break;
|
||||
}
|
||||
case IJSVGRenderQualityOptimized: {
|
||||
quality = kCGInterpolationMedium;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
quality = kCGInterpolationHigh;
|
||||
}
|
||||
}
|
||||
CGContextSetInterpolationQuality(ref, quality);
|
||||
[self.layer renderInContext:ref];
|
||||
IJSVGEndTransactionLock();
|
||||
}
|
||||
|
||||
// clip to mask
|
||||
CGContextClipToRect( ref, viewPort);
|
||||
|
||||
tX -= (_viewBox.origin.x*_scale);
|
||||
tY -= (_viewBox.origin.y*_scale);
|
||||
|
||||
CGContextTranslateCTM( ref, tX, tY );
|
||||
CGContextScaleCTM( ref, _scale, _scale );
|
||||
|
||||
// render the layer, its really important we lock
|
||||
// the transaction when drawing
|
||||
__block IJSVG * weakSelf = self;
|
||||
IJSVGBeginTransactionLock();
|
||||
// do we need to update the backing scales on the
|
||||
// layers?
|
||||
if(weakSelf.renderingBackingScaleHelper != nil) {
|
||||
[weakSelf _askHelperForBackingScale];
|
||||
}
|
||||
|
||||
// render the layers
|
||||
[weakSelf.layer renderInContext:ref];
|
||||
IJSVGEndTransactionLock();
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
// just catch and give back a drawing error to the caller
|
||||
if( error != NULL )
|
||||
if( error != NULL ) {
|
||||
*error = [[[NSError alloc] initWithDomain:IJSVGErrorDomain
|
||||
code:IJSVGErrorDrawing
|
||||
userInfo:nil] autorelease];
|
||||
}
|
||||
}
|
||||
@finally {
|
||||
CGContextRestoreGState(ref);
|
||||
}
|
||||
CGContextRestoreGState(ref);
|
||||
}
|
||||
return (error == nil);
|
||||
}
|
||||
@@ -600,27 +701,27 @@
|
||||
|
||||
// dont do anything, nothing has changed, no point of iterating over
|
||||
// every layer for no reason!
|
||||
if(scale == _lastProposedBackingScale) {
|
||||
if(scale == _lastProposedBackingScale && renderQuality == _lastProposedRenderQuality) {
|
||||
return;
|
||||
}
|
||||
_lastProposedBackingScale = scale;
|
||||
[self _recursivelySetScale:scale
|
||||
forLayer:self.layer];
|
||||
}
|
||||
|
||||
- (void)_recursivelySetScale:(CGFloat)scale
|
||||
forLayer:(IJSVGLayer *)layer
|
||||
{
|
||||
// update the backing layer scale
|
||||
if(layer.requiresBackingScaleHelp == YES) {
|
||||
layer.backingScaleFactor = scale;
|
||||
}
|
||||
|
||||
// find the next!
|
||||
for(IJSVGLayer * aLayer in layer.sublayers) {
|
||||
[self _recursivelySetScale:scale
|
||||
forLayer:aLayer];
|
||||
}
|
||||
IJSVGRenderQuality quality = self.renderQuality;
|
||||
_lastProposedBackingScale = scale;
|
||||
_lastProposedRenderQuality = quality;
|
||||
|
||||
// walk the tree
|
||||
void (^block)(CALayer * layer, BOOL isMask) = ^void (CALayer * layer, BOOL isMask) {
|
||||
IJSVGLayer * propLayer = ((IJSVGLayer *)layer);
|
||||
propLayer.renderQuality = quality;
|
||||
if(propLayer.requiresBackingScaleHelp == YES) {
|
||||
propLayer.backingScaleFactor = scale;
|
||||
}
|
||||
};
|
||||
|
||||
// gogogo
|
||||
[IJSVGLayer recursivelyWalkLayer:self.layer
|
||||
withBlock:block];
|
||||
|
||||
}
|
||||
|
||||
- (void)setFillColor:(NSColor *)aColor
|
||||
@@ -641,6 +742,24 @@
|
||||
[_layerTree release], _layerTree = nil;
|
||||
}
|
||||
|
||||
- (void)setStrokeWidth:(CGFloat)aWidth
|
||||
{
|
||||
strokeWidth = aWidth;
|
||||
[_layerTree release], _layerTree = nil;
|
||||
}
|
||||
|
||||
- (void)setLineCapStyle:(IJSVGLineCapStyle)aLineCapStyle
|
||||
{
|
||||
lineCapStyle = aLineCapStyle;
|
||||
[_layerTree release], _layerTree = nil;
|
||||
}
|
||||
|
||||
- (void)setLineJoinStyle:(IJSVGLineJoinStyle)aLineJoinStyle
|
||||
{
|
||||
lineJoinStyle = aLineJoinStyle;
|
||||
[_layerTree release], _layerTree = nil;
|
||||
}
|
||||
|
||||
- (IJSVGLayer *)layerWithTree:(IJSVGLayerTree *)tree
|
||||
{
|
||||
// clear memory
|
||||
@@ -664,13 +783,89 @@
|
||||
// create the renderer and assign default values
|
||||
// from this SVG object
|
||||
IJSVGLayerTree * renderer = [[[IJSVGLayerTree alloc] init] autorelease];
|
||||
renderer.viewBox = self.viewBox;
|
||||
renderer.fillColor = self.fillColor;
|
||||
renderer.strokeColor = self.strokeColor;
|
||||
renderer.strokeWidth = self.strokeWidth;
|
||||
renderer.lineCapStyle = self.lineCapStyle;
|
||||
renderer.lineJoinStyle = self.lineJoinStyle;
|
||||
renderer.replacementColors = _replacementColors;
|
||||
|
||||
// return the rendered layer
|
||||
return [self layerWithTree:renderer];
|
||||
}
|
||||
|
||||
- (NSArray<NSColor *> *)visibleColors
|
||||
{
|
||||
// set for the colors
|
||||
NSMutableSet * colors = [[[NSMutableSet alloc] init] autorelease];
|
||||
|
||||
// block to find colors in stroke and fill
|
||||
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];
|
||||
color = [IJSVGColor computeColorSpace:color];
|
||||
if(color.alphaComponent != 0.f) {
|
||||
[colors addObject:color];
|
||||
}
|
||||
}
|
||||
if(sLayer.strokeColor != nil) {
|
||||
color = [NSColor colorWithCGColor:sLayer.strokeColor];
|
||||
color = [IJSVGColor computeColorSpace:color];
|
||||
if(color.alphaComponent != 0.f) {
|
||||
[colors addObject:color];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// walk
|
||||
[IJSVGLayer recursivelyWalkLayer:self.layer
|
||||
withBlock:block];
|
||||
|
||||
// return the colours!
|
||||
return colors.allObjects;
|
||||
}
|
||||
|
||||
- (void)removeAllReplacementColors
|
||||
{
|
||||
[_replacementColors release], _replacementColors = nil;
|
||||
}
|
||||
|
||||
- (void)removeReplacementColor:(NSColor *)color
|
||||
{
|
||||
if(_replacementColors == nil) {
|
||||
return;
|
||||
}
|
||||
[_replacementColors removeObjectForKey:[IJSVGColor computeColorSpace:color]];
|
||||
}
|
||||
|
||||
- (void)replaceColor:(NSColor *)color
|
||||
withColor:(NSColor *)newColor
|
||||
{
|
||||
if(_replacementColors == nil) {
|
||||
_replacementColors = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
color = [IJSVGColor computeColorSpace:color];
|
||||
newColor = [IJSVGColor computeColorSpace:newColor];
|
||||
_replacementColors[color] = newColor;
|
||||
[_layerTree release], _layerTree = nil;
|
||||
}
|
||||
|
||||
- (void)setReplacementColors:(NSDictionary<NSColor *, NSColor *> *)colors
|
||||
{
|
||||
if(_replacementColors != nil) {
|
||||
[_replacementColors release], _replacementColors = nil;
|
||||
}
|
||||
for(NSColor * oldColor in colors) {
|
||||
[self replaceColor:oldColor
|
||||
withColor:colors[oldColor]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_beginDraw:(NSRect)rect
|
||||
{
|
||||
// in order to correctly fit the the SVG into the
|
||||
@@ -696,8 +891,9 @@
|
||||
|
||||
- (id)pasteboardPropertyListForType:(NSString *)type
|
||||
{
|
||||
if( [type isEqualToString:NSPasteboardTypePDF] )
|
||||
if( [type isEqualToString:NSPasteboardTypePDF] ) {
|
||||
return [self PDFData];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -160,10 +160,16 @@ typedef NS_ENUM( NSInteger, IJSVGPredefinedColor ) {
|
||||
|
||||
@interface IJSVGColor : NSObject
|
||||
|
||||
CGFloat * IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightness);
|
||||
|
||||
+ (NSColor *)computeColorSpace:(NSColor *)color;
|
||||
+ (NSColorSpace *)defaultColorSpace;
|
||||
+ (BOOL)isColor:(NSString *)string;
|
||||
+ (NSString *)colorStringFromColor:(NSColor *)color
|
||||
forceHex:(BOOL)forceHex;
|
||||
forceHex:(BOOL)forceHex
|
||||
allowShorthand:(BOOL)allowShorthand;
|
||||
+ (NSString *)colorStringFromColor:(NSColor *)color;
|
||||
+ (NSColor *)colorFromHEXInteger:(NSInteger)hex;
|
||||
+ (NSColor *)computeColor:(id)colour;
|
||||
+ (NSColor *)colorFromString:(NSString *)string;
|
||||
+ (NSColor *)colorFromHEXString:(NSString *)string
|
||||
|
||||
@@ -11,168 +11,198 @@
|
||||
|
||||
@implementation IJSVGColor
|
||||
|
||||
static NSMutableDictionary * _colorTree = nil;
|
||||
static NSDictionary * _colorTree = nil;
|
||||
|
||||
CGFloat * IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightness)
|
||||
{
|
||||
hue *= (1.f/360.f);
|
||||
hue = (hue - floorf(hue));
|
||||
saturation *= 0.01;
|
||||
lightness *= 0.01;
|
||||
lightness *= 2.f;
|
||||
|
||||
CGFloat s = saturation * ((lightness < 1.f) ? lightness : (2.f - lightness));
|
||||
CGFloat brightness = (lightness + s) * .5f;
|
||||
if(s != 0.f) {
|
||||
s = (2.f * s) / (lightness + s);
|
||||
}
|
||||
CGFloat * floats = (CGFloat *)malloc(3*sizeof(CGFloat));
|
||||
floats[0] = hue;
|
||||
floats[1] = s;
|
||||
floats[2] = brightness;
|
||||
return floats;
|
||||
};
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[[self class] _generateTree];
|
||||
[self.class _generateTree];
|
||||
}
|
||||
|
||||
+ (NSColorSpace *)defaultColorSpace
|
||||
{
|
||||
return [NSColorSpace deviceRGBColorSpace];
|
||||
}
|
||||
|
||||
+ (NSColor *)computeColorSpace:(NSColor *)color
|
||||
{
|
||||
NSColorSpace * space = [self defaultColorSpace];
|
||||
if(color.colorSpace != space) {
|
||||
color = [color colorUsingColorSpace:space];
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
+ (void)_generateTree
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
||||
_colorTree = [[NSMutableDictionary alloc] init];
|
||||
// add the colours in
|
||||
[_colorTree setObject:@"f0f8ff" forKey:@"aliceblue"];
|
||||
[_colorTree setObject:@"faebd7" forKey:@"antiquewhite"];
|
||||
[_colorTree setObject:@"00ffff" forKey:@"aqua"];
|
||||
[_colorTree setObject:@"7fffd4" forKey:@"aquamarine"];
|
||||
[_colorTree setObject:@"f0ffff" forKey:@"azure"];
|
||||
[_colorTree setObject:@"f5f5dc" forKey:@"beige"];
|
||||
[_colorTree setObject:@"ffe4c4" forKey:@"bisque"];
|
||||
[_colorTree setObject:@"000000" forKey:@"black"];
|
||||
[_colorTree setObject:@"ffebcd" forKey:@"blanchedalmond"];
|
||||
[_colorTree setObject:@"0000ff" forKey:@"blue"];
|
||||
[_colorTree setObject:@"8a2be2" forKey:@"blueviolet"];
|
||||
[_colorTree setObject:@"a52a2a" forKey:@"brown"];
|
||||
[_colorTree setObject:@"deb887" forKey:@"burlywood"];
|
||||
[_colorTree setObject:@"5f9ea0" forKey:@"cadetblue"];
|
||||
[_colorTree setObject:@"7fff00" forKey:@"chartreuse"];
|
||||
[_colorTree setObject:@"d2691e" forKey:@"chocolate"];
|
||||
[_colorTree setObject:@"ff7f50" forKey:@"coral"];
|
||||
[_colorTree setObject:@"6495ed" forKey:@"cornflowerblue"];
|
||||
[_colorTree setObject:@"fff8dc" forKey:@"cornsilk"];
|
||||
[_colorTree setObject:@"dc143c" forKey:@"crimson"];
|
||||
[_colorTree setObject:@"00ffff" forKey:@"cyan"];
|
||||
[_colorTree setObject:@"00008b" forKey:@"darkblue"];
|
||||
[_colorTree setObject:@"008b8b" forKey:@"darkcyan"];
|
||||
[_colorTree setObject:@"b8860b" forKey:@"darkgoldenrod"];
|
||||
[_colorTree setObject:@"a9a9a9" forKey:@"darkgray"];
|
||||
[_colorTree setObject:@"006400" forKey:@"darkgreen"];
|
||||
[_colorTree setObject:@"a9a9a9" forKey:@"darkgrey"];
|
||||
[_colorTree setObject:@"bdb76b" forKey:@"darkkhaki"];
|
||||
[_colorTree setObject:@"8b008b" forKey:@"darkmagenta"];
|
||||
[_colorTree setObject:@"556b2f" forKey:@"darkolivegreen"];
|
||||
[_colorTree setObject:@"ff8c00" forKey:@"darkorange"];
|
||||
[_colorTree setObject:@"9932cc" forKey:@"darkorchid"];
|
||||
[_colorTree setObject:@"8b0000" forKey:@"darkred"];
|
||||
[_colorTree setObject:@"e9967a" forKey:@"darksalmon"];
|
||||
[_colorTree setObject:@"8fbc8f" forKey:@"darkseagreen"];
|
||||
[_colorTree setObject:@"483d8b" forKey:@"darkslateblue"];
|
||||
[_colorTree setObject:@"2f4f4f" forKey:@"darkslategray"];
|
||||
[_colorTree setObject:@"2f4f4f" forKey:@"darkslategrey"];
|
||||
[_colorTree setObject:@"00ced1" forKey:@"darkturquoise"];
|
||||
[_colorTree setObject:@"9400d3" forKey:@"darkviolet"];
|
||||
[_colorTree setObject:@"ff1493" forKey:@"deeppink"];
|
||||
[_colorTree setObject:@"00bfff" forKey:@"deepskyblue"];
|
||||
[_colorTree setObject:@"696969" forKey:@"dimgray"];
|
||||
[_colorTree setObject:@"696969" forKey:@"dimgrey"];
|
||||
[_colorTree setObject:@"1e90ff" forKey:@"dodgerblue"];
|
||||
[_colorTree setObject:@"b22222" forKey:@"firebrick"];
|
||||
[_colorTree setObject:@"fffaf0" forKey:@"floralwhite"];
|
||||
[_colorTree setObject:@"228b22" forKey:@"forestgreen"];
|
||||
[_colorTree setObject:@"ff00ff" forKey:@"fuchsia"];
|
||||
[_colorTree setObject:@"dcdcdc" forKey:@"gainsboro"];
|
||||
[_colorTree setObject:@"f8f8ff" forKey:@"ghostwhite"];
|
||||
[_colorTree setObject:@"ffd700" forKey:@"gold"];
|
||||
[_colorTree setObject:@"daa520" forKey:@"goldenrod"];
|
||||
[_colorTree setObject:@"808080" forKey:@"gray"];
|
||||
[_colorTree setObject:@"008000" forKey:@"green"];
|
||||
[_colorTree setObject:@"adff2f" forKey:@"greenyellow"];
|
||||
[_colorTree setObject:@"808080" forKey:@"grey"];
|
||||
[_colorTree setObject:@"f0fff0" forKey:@"honeydew"];
|
||||
[_colorTree setObject:@"ff69b4" forKey:@"hotpink"];
|
||||
[_colorTree setObject:@"cd5c5c" forKey:@"indianred"];
|
||||
[_colorTree setObject:@"4b0082" forKey:@"indigo"];
|
||||
[_colorTree setObject:@"fffff0" forKey:@"ivory"];
|
||||
[_colorTree setObject:@"f0e68c" forKey:@"khaki"];
|
||||
[_colorTree setObject:@"e6e6fa" forKey:@"lavender"];
|
||||
[_colorTree setObject:@"fff0f5" forKey:@"lavenderblush"];
|
||||
[_colorTree setObject:@"7cfc00" forKey:@"lawngreen"];
|
||||
[_colorTree setObject:@"fffacd" forKey:@"lemonchiffon"];
|
||||
[_colorTree setObject:@"add8e6" forKey:@"lightblue"];
|
||||
[_colorTree setObject:@"f08080" forKey:@"lightcoral"];
|
||||
[_colorTree setObject:@"e0ffff" forKey:@"lightcyan"];
|
||||
[_colorTree setObject:@"fafad2" forKey:@"lightgoldenrodyellow"];
|
||||
[_colorTree setObject:@"d3d3d3" forKey:@"lightgray"];
|
||||
[_colorTree setObject:@"90ee90" forKey:@"lightgreen"];
|
||||
[_colorTree setObject:@"d3d3d3" forKey:@"lightgrey"];
|
||||
[_colorTree setObject:@"ffb6c1" forKey:@"lightpink"];
|
||||
[_colorTree setObject:@"ffa07a" forKey:@"lightsalmon"];
|
||||
[_colorTree setObject:@"20b2aa" forKey:@"lightseagreen"];
|
||||
[_colorTree setObject:@"87cefa" forKey:@"lightskyblue"];
|
||||
[_colorTree setObject:@"778899" forKey:@"lightslategray"];
|
||||
[_colorTree setObject:@"778899" forKey:@"lightslategrey"];
|
||||
[_colorTree setObject:@"b0c4de" forKey:@"lightsteelblue"];
|
||||
[_colorTree setObject:@"ffffe0" forKey:@"lightyellow"];
|
||||
[_colorTree setObject:@"00ff00" forKey:@"lime"];
|
||||
[_colorTree setObject:@"32cd32" forKey:@"limegreen"];
|
||||
[_colorTree setObject:@"faf0e6" forKey:@"linen"];
|
||||
[_colorTree setObject:@"ff00ff" forKey:@"magenta"];
|
||||
[_colorTree setObject:@"800000" forKey:@"maroon"];
|
||||
[_colorTree setObject:@"66cdaa" forKey:@"mediumaquamarine"];
|
||||
[_colorTree setObject:@"0000cd" forKey:@"mediumblue"];
|
||||
[_colorTree setObject:@"ba55d3" forKey:@"mediumorchid"];
|
||||
[_colorTree setObject:@"9370db" forKey:@"mediumpurple"];
|
||||
[_colorTree setObject:@"3cb371" forKey:@"mediumseagreen"];
|
||||
[_colorTree setObject:@"7b68ee" forKey:@"mediumslateblue"];
|
||||
[_colorTree setObject:@"00fa9a" forKey:@"mediumspringgreen"];
|
||||
[_colorTree setObject:@"48d1cc" forKey:@"mediumturquoise"];
|
||||
[_colorTree setObject:@"c71585" forKey:@"mediumvioletred"];
|
||||
[_colorTree setObject:@"191970" forKey:@"midnightblue"];
|
||||
[_colorTree setObject:@"f5fffa" forKey:@"mintcream"];
|
||||
[_colorTree setObject:@"ffe4e1" forKey:@"mistyrose"];
|
||||
[_colorTree setObject:@"ffe4b5" forKey:@"moccasin"];
|
||||
[_colorTree setObject:@"ffdead" forKey:@"navajowhite"];
|
||||
[_colorTree setObject:@"000080" forKey:@"navy"];
|
||||
[_colorTree setObject:@"fdf5e6" forKey:@"oldlace"];
|
||||
[_colorTree setObject:@"808000" forKey:@"olive"];
|
||||
[_colorTree setObject:@"6b8e23" forKey:@"olivedrab"];
|
||||
[_colorTree setObject:@"ffa500" forKey:@"orange"];
|
||||
[_colorTree setObject:@"ff4500" forKey:@"orangered"];
|
||||
[_colorTree setObject:@"da70d6" forKey:@"orchid"];
|
||||
[_colorTree setObject:@"eee8aa" forKey:@"palegoldenrod"];
|
||||
[_colorTree setObject:@"98fb98" forKey:@"palegreen"];
|
||||
[_colorTree setObject:@"afeeee" forKey:@"paleturquoise"];
|
||||
[_colorTree setObject:@"db7093" forKey:@"palevioletred"];
|
||||
[_colorTree setObject:@"ffefd5" forKey:@"papayawhip"];
|
||||
[_colorTree setObject:@"ffdab9" forKey:@"peachpuff"];
|
||||
[_colorTree setObject:@"cd853f" forKey:@"peru"];
|
||||
[_colorTree setObject:@"ffc0cb" forKey:@"pink"];
|
||||
[_colorTree setObject:@"dda0dd" forKey:@"plum"];
|
||||
[_colorTree setObject:@"b0e0e6" forKey:@"powderblue"];
|
||||
[_colorTree setObject:@"800080" forKey:@"purple"];
|
||||
[_colorTree setObject:@"ff0000" forKey:@"red"];
|
||||
[_colorTree setObject:@"bc8f8f" forKey:@"rosybrown"];
|
||||
[_colorTree setObject:@"4169e1" forKey:@"royalblue"];
|
||||
[_colorTree setObject:@"8b4513" forKey:@"saddlebrown"];
|
||||
[_colorTree setObject:@"fa8072" forKey:@"salmon"];
|
||||
[_colorTree setObject:@"f4a460" forKey:@"sandybrown"];
|
||||
[_colorTree setObject:@"2e8b57" forKey:@"seagreen"];
|
||||
[_colorTree setObject:@"fff5ee" forKey:@"seashell"];
|
||||
[_colorTree setObject:@"a0522d" forKey:@"sienna"];
|
||||
[_colorTree setObject:@"c0c0c0" forKey:@"silver"];
|
||||
[_colorTree setObject:@"87ceeb" forKey:@"skyblue"];
|
||||
[_colorTree setObject:@"6a5acd" forKey:@"slateblue"];
|
||||
[_colorTree setObject:@"708090" forKey:@"slategray"];
|
||||
[_colorTree setObject:@"708090" forKey:@"slategrey"];
|
||||
[_colorTree setObject:@"fffafa" forKey:@"snow"];
|
||||
[_colorTree setObject:@"00ff7f" forKey:@"springgreen"];
|
||||
[_colorTree setObject:@"4682b4" forKey:@"steelblue"];
|
||||
[_colorTree setObject:@"d2b48c" forKey:@"tan"];
|
||||
[_colorTree setObject:@"008080" forKey:@"teal"];
|
||||
[_colorTree setObject:@"d8bfd8" forKey:@"thistle"];
|
||||
[_colorTree setObject:@"ff6347" forKey:@"tomato"];
|
||||
[_colorTree setObject:@"40e0d0" forKey:@"turquoise"];
|
||||
[_colorTree setObject:@"ee82ee" forKey:@"violet"];
|
||||
[_colorTree setObject:@"f5deb3" forKey:@"wheat"];
|
||||
[_colorTree setObject:@"ffffff" forKey:@"white"];
|
||||
[_colorTree setObject:@"f5f5f5" forKey:@"whitesmoke"];
|
||||
[_colorTree setObject:@"ffff00" forKey:@"yellow"];
|
||||
[_colorTree setObject:@"9acd32" forKey:@"yellowgreen"];
|
||||
_colorTree = [@{
|
||||
@"aliceblue":@(0xf0f8ff),
|
||||
@"antiquewhite":@(0xfaebd7),
|
||||
@"aqua":@(0x00ffff),
|
||||
@"aquamarine":@(0x7fffd4),
|
||||
@"azure":@(0xf0ffff),
|
||||
@"beige":@(0xf5f5dc),
|
||||
@"bisque":@(0xffe4c4),
|
||||
@"black":@(0x000000),
|
||||
@"blanchedalmond":@(0xffebcd),
|
||||
@"blue":@(0x0000ff),
|
||||
@"blueviolet":@(0x8a2be2),
|
||||
@"brown":@(0xa52a2a),
|
||||
@"burlywood":@(0xdeb887),
|
||||
@"cadetblue":@(0x5f9ea0),
|
||||
@"chartreuse":@(0x7fff00),
|
||||
@"chocolate":@(0xd2691e),
|
||||
@"coral":@(0xff7f50),
|
||||
@"cornflowerblue":@(0x6495ed),
|
||||
@"cornsilk":@(0xfff8dc),
|
||||
@"crimson":@(0xdc143c),
|
||||
@"currentcolor":@(0x000000),
|
||||
@"cyan":@(0x00ffff),
|
||||
@"darkblue":@(0x00008b),
|
||||
@"darkcyan":@(0x008b8b),
|
||||
@"darkgoldenrod":@(0xb8860b),
|
||||
@"darkgray":@(0xa9a9a9),
|
||||
@"darkgreen":@(0x006400),
|
||||
@"darkgrey":@(0xa9a9a9),
|
||||
@"darkkhaki":@(0xbdb76b),
|
||||
@"darkmagenta":@(0x8b008b),
|
||||
@"darkolivegreen":@(0x556b2f),
|
||||
@"darkorange":@(0xff8c00),
|
||||
@"darkorchid":@(0x9932cc),
|
||||
@"darkred":@(0x8b0000),
|
||||
@"darksalmon":@(0xe9967a),
|
||||
@"darkseagreen":@(0x8fbc8f),
|
||||
@"darkslateblue":@(0x483d8b),
|
||||
@"darkslategray":@(0x2f4f4f),
|
||||
@"darkturquoise":@(0x00ced1),
|
||||
@"darkviolet":@(0x9400d3),
|
||||
@"deeppink":@(0xff1493),
|
||||
@"deepskyblue":@(0x00bfff),
|
||||
@"dimgray":@(0x696969),
|
||||
@"dimgrey":@(0x696969),
|
||||
@"dodgerblue":@(0x1e90ff),
|
||||
@"firebrick":@(0xb22222),
|
||||
@"floralwhite":@(0xfffaf0),
|
||||
@"forestgreen":@(0x228b22),
|
||||
@"fuchsia":@(0xff00ff),
|
||||
@"gainsboro":@(0xdcdcdc),
|
||||
@"ghostwhite":@(0xf8f8ff),
|
||||
@"gold":@(0xffd700),
|
||||
@"goldenrod":@(0xdaa520),
|
||||
@"gray":@(0x808080),
|
||||
@"green":@(0x008000),
|
||||
@"greenyellow":@(0xadff2f),
|
||||
@"grey":@(0x808080),
|
||||
@"honeydew":@(0xf0fff0),
|
||||
@"hotpink":@(0xff69b4),
|
||||
@"indianred":@(0xcd5c5c),
|
||||
@"indigo":@(0x4b0082),
|
||||
@"ivory":@(0xfffff0),
|
||||
@"khaki":@(0xf0e68c),
|
||||
@"lavender":@(0xe6e6fa),
|
||||
@"lavenderblush":@(0xfff0f5),
|
||||
@"lawngreen":@(0x7cfc00),
|
||||
@"lemonchiffon":@(0xfffacd),
|
||||
@"lightblue":@(0xadd8e6),
|
||||
@"lightcoral":@(0xf08080),
|
||||
@"lightcyan":@(0xe0ffff),
|
||||
@"lightgoldenrodyellow":@(0xfafad2),
|
||||
@"lightgray":@(0xd3d3d3),
|
||||
@"lightgreen":@(0x90ee90),
|
||||
@"lightgrey":@(0xd3d3d3),
|
||||
@"lightpink":@(0xffb6c1),
|
||||
@"lightsalmon":@(0xffa07a),
|
||||
@"lightseagreen":@(0x20b2aa),
|
||||
@"lightskyblue":@(0x87cefa),
|
||||
@"lightslategray":@(0x778899),
|
||||
@"lightsteelblue":@(0xb0c4de),
|
||||
@"lightyellow":@(0xffffe0),
|
||||
@"lime":@(0x00ff00),
|
||||
@"limegreen":@(0x32cd32),
|
||||
@"linen":@(0xfaf0e6),
|
||||
@"magenta":@(0xff00ff),
|
||||
@"maroon":@(0x800000),
|
||||
@"mediumaquamarine":@(0x66cdaa),
|
||||
@"mediumblue":@(0x0000cd),
|
||||
@"mediumorchid":@(0xba55d3),
|
||||
@"mediumpurple":@(0x9370db),
|
||||
@"mediumseagreen":@(0x3cb371),
|
||||
@"mediumslateblue":@(0x7b68ee),
|
||||
@"mediumspringgreen":@(0x00fa9a),
|
||||
@"mediumturquoise":@(0x48d1cc),
|
||||
@"mediumvioletred":@(0xc71585),
|
||||
@"midnightblue":@(0x191970),
|
||||
@"mintcream":@(0xf5fffa),
|
||||
@"mistyrose":@(0xffe4e1),
|
||||
@"moccasin":@(0xffe4b5),
|
||||
@"navajowhite":@(0xffdead),
|
||||
@"navy":@(0x000080),
|
||||
@"oldlace":@(0xfdf5e6),
|
||||
@"olive":@(0x808000),
|
||||
@"olivedrab":@(0x6b8e23),
|
||||
@"orange":@(0xffa500),
|
||||
@"orangered":@(0xff4500),
|
||||
@"orchid":@(0xda70d6),
|
||||
@"palegoldenrod":@(0xeee8aa),
|
||||
@"palegreen":@(0x98fb98),
|
||||
@"paleturquoise":@(0xafeeee),
|
||||
@"palevioletred":@(0xdb7093),
|
||||
@"papayawhip":@(0xffefd5),
|
||||
@"peachpuff":@(0xffdab9),
|
||||
@"peru":@(0xcd853f),
|
||||
@"pink":@(0xffc0cb),
|
||||
@"plum":@(0xdda0dd),
|
||||
@"powderblue":@(0xb0e0e6),
|
||||
@"purple":@(0x800080),
|
||||
@"red":@(0xff0000),
|
||||
@"rosybrown":@(0xbc8f8f),
|
||||
@"royalblue":@(0x4169e1),
|
||||
@"saddlebrown":@(0x8b4513),
|
||||
@"salmon":@(0xfa8072),
|
||||
@"sandybrown":@(0xf4a460),
|
||||
@"seagreen":@(0x2e8b57),
|
||||
@"seashell":@(0xfff5ee),
|
||||
@"sienna":@(0xa0522d),
|
||||
@"silver":@(0xc0c0c0),
|
||||
@"skyblue":@(0x87ceeb),
|
||||
@"slateblue":@(0x6a5acd),
|
||||
@"slategrey":@(0x708090),
|
||||
@"snow":@(0xfffafa),
|
||||
@"springgreen":@(0x00ff7f),
|
||||
@"steelblue":@(0x4682b4),
|
||||
@"tan":@(0xd2b48c),
|
||||
@"teal":@(0x008080),
|
||||
@"thistle":@(0xd8bfd8),
|
||||
@"tomato":@(0xff6347),
|
||||
@"turquoise":@(0x40e0d0),
|
||||
@"violet":@(0xee82ee),
|
||||
@"wheat":@(0xf5deb3),
|
||||
@"white":@(0xffffff),
|
||||
@"whitesmoke":@(0xf5f5f5),
|
||||
@"yellow":@(0xffff00),
|
||||
@"yellowgreen":@(0x9acd32)
|
||||
} retain];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -185,60 +215,93 @@ static NSMutableDictionary * _colorTree = nil;
|
||||
|
||||
+ (NSColor *)colorFromString:(NSString *)string
|
||||
{
|
||||
if( [string length] < 3 )
|
||||
return nil;
|
||||
|
||||
string = [string lowercaseString];
|
||||
NSColor * color = [[self class] colorFromPredefinedColorName:string];
|
||||
if( color != nil )
|
||||
return color;
|
||||
NSCharacterSet * set = NSCharacterSet.whitespaceAndNewlineCharacterSet;
|
||||
string = [string stringByTrimmingCharactersInSet:set];
|
||||
|
||||
if( [[string lowercaseString] isEqualToString:@"none"] )
|
||||
if( [string length] < 3 ) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSColor * color = nil;
|
||||
string = [string lowercaseString];
|
||||
if([self.class isHex:string] == NO) {
|
||||
color = [self.class colorFromPredefinedColorName:string];
|
||||
if( color != nil ) {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
if( [[string lowercaseString] isEqualToString:@"none"] ) {
|
||||
return [NSColor clearColor];
|
||||
}
|
||||
|
||||
// is it RGB?
|
||||
if( [[string substringToIndex:3] isEqualToString:@"rgb"] )
|
||||
{
|
||||
if( [[string substringToIndex:3] isEqualToString:@"rgb"] ) {
|
||||
NSInteger count = 0;
|
||||
CGFloat * params = [IJSVGUtils commandParameters:string
|
||||
count:&count];
|
||||
CGFloat alpha = 1;
|
||||
if( count == 4 )
|
||||
if( count == 4 ) {
|
||||
alpha = params[3];
|
||||
color = [NSColor colorWithDeviceRed:params[0]/255
|
||||
green:params[1]/255
|
||||
blue:params[2]/255
|
||||
}
|
||||
color = [NSColor colorWithDeviceRed:params[0]/255.f
|
||||
green:params[1]/255.f
|
||||
blue:params[2]/255.f
|
||||
alpha:alpha];
|
||||
free(params);
|
||||
return color;
|
||||
}
|
||||
|
||||
color = [[self class] colorFromHEXString:string
|
||||
alpha:1.f];
|
||||
// is it HSL?
|
||||
if([[string substringToIndex:3] isEqualToString:@"hsl"]) {
|
||||
NSInteger count = 0;
|
||||
CGFloat * params = [IJSVGUtils commandParameters:string
|
||||
count:&count];
|
||||
CGFloat alpha = 1;
|
||||
if(count == 4) {
|
||||
alpha = params[3];
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
||||
// memory clean!
|
||||
free(hsb);
|
||||
free(params);
|
||||
return color;
|
||||
}
|
||||
|
||||
color = [self.class colorFromHEXString:string alpha:1.f];
|
||||
return color;
|
||||
}
|
||||
|
||||
+ (NSColor *)colorFromPredefinedColorName:(NSString *)name
|
||||
{
|
||||
NSString * hex = nil;
|
||||
NSNumber * hex = nil;
|
||||
name = [name.lowercaseString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
if( ( hex = [_colorTree objectForKey:name] ) == nil )
|
||||
if((hex = _colorTree[name]) == nil ) {
|
||||
return nil;
|
||||
return [[self class] colorFromHEXString:hex
|
||||
alpha:1.f];
|
||||
}
|
||||
return [self.class colorFromHEXInteger:hex.integerValue];
|
||||
}
|
||||
|
||||
+ (NSString *)colorStringFromColor:(NSColor *)color
|
||||
{
|
||||
return [self colorStringFromColor:color
|
||||
forceHex:NO];
|
||||
forceHex:NO
|
||||
allowShorthand:YES];
|
||||
}
|
||||
|
||||
+ (NSString *)colorStringFromColor:(NSColor *)color
|
||||
forceHex:(BOOL)forceHex
|
||||
allowShorthand:(BOOL)allowShorthand
|
||||
{
|
||||
// convert to RGB
|
||||
color = [color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
|
||||
color = [self computeColorSpace:color];
|
||||
|
||||
int red = color.redComponent * 0xFF;
|
||||
int green = color.greenComponent * 0xFF;
|
||||
@@ -254,10 +317,23 @@ static NSMutableDictionary * _colorTree = nil;
|
||||
if(forceHex || alpha == 100 ||
|
||||
(red == 0 && green == 0 && blue == 0 && alpha == 0) ||
|
||||
(red == 255 && green == 255 && blue == 255 && alpha == 100)) {
|
||||
// just return hex
|
||||
if(allowShorthand == YES) {
|
||||
NSString * r = [NSString stringWithFormat:@"%02X",red];
|
||||
NSString * g = [NSString stringWithFormat:@"%02X",green];
|
||||
NSString * b = [NSString stringWithFormat:@"%02X",blue];
|
||||
if([r characterAtIndex:0] == [r characterAtIndex:1] &&
|
||||
[g characterAtIndex:0] == [g characterAtIndex:1] &&
|
||||
[b characterAtIndex:0] == [b characterAtIndex:1]) {
|
||||
return [NSString stringWithFormat:@"#%c%c%c",[r characterAtIndex:0],
|
||||
[g characterAtIndex:0],[b characterAtIndex:0]];
|
||||
}
|
||||
}
|
||||
return [NSString stringWithFormat:@"#%02X%02X%02X",red,green,blue];
|
||||
}
|
||||
return [NSString stringWithFormat:@"rgba(%d, %d, %d, %d)",red, green, blue, alpha];
|
||||
|
||||
// note the %g, CSS alpha is 0 to 1, not 0 - 100, my bad!
|
||||
return [NSString stringWithFormat:@"rgba(%d,%d,%d,%g)",red, green, blue,
|
||||
((float)alpha/100.f)];
|
||||
}
|
||||
|
||||
+ (NSString *)colorNameFromPredefinedColor:(IJSVGPredefinedColor)color
|
||||
@@ -565,7 +641,7 @@ static NSMutableDictionary * _colorTree = nil;
|
||||
+ (NSColor *)changeAlphaOnColor:(NSColor *)color
|
||||
to:(CGFloat)alphaValue
|
||||
{
|
||||
color = [color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
|
||||
color = [self computeColorSpace:color];
|
||||
return [NSColor colorWithDeviceRed:[color redComponent]
|
||||
green:[color greenComponent]
|
||||
blue:[color blueComponent]
|
||||
@@ -574,20 +650,35 @@ static NSMutableDictionary * _colorTree = nil;
|
||||
|
||||
+ (BOOL)isColor:(NSString *)string
|
||||
{
|
||||
return [[string substringToIndex:1] isEqualToString:@"#"] || [[string substringToIndex:3] isEqualToString:@"rgb"];
|
||||
return [[string substringToIndex:1] isEqualToString:@"#"] ||
|
||||
[[string substringToIndex:3] isEqualToString:@"rgb"];
|
||||
}
|
||||
|
||||
+ (BOOL)isHex:(NSString *)string
|
||||
{
|
||||
NSCharacterSet *chars = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef#"] invertedSet];
|
||||
return [string rangeOfCharacterFromSet:chars].location == NSNotFound;
|
||||
const char * validList = "0123456789ABCDEFabcdef#";
|
||||
for(NSInteger i = 0; i < string.length; i++) {
|
||||
char c = [string characterAtIndex:i];
|
||||
if(strchr(validList, c) == NULL) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (NSColor *)colorFromHEXInteger:(NSInteger)hex
|
||||
{
|
||||
return [NSColor colorWithDeviceRed:((hex >> 16) & 0xFF) / 255.f
|
||||
green:((hex >> 6) & 0xFF) / 255.f
|
||||
blue:(hex & 0xFF) / 255.f
|
||||
alpha:1.f];
|
||||
}
|
||||
|
||||
+ (NSColor *)colorFromHEXString:(NSString *)string
|
||||
alpha:(CGFloat)alpha
|
||||
{
|
||||
// absolutely no string
|
||||
if( string == nil || string.length == 0 || ![[self class] isHex:string] )
|
||||
if( string == nil || string.length == 0 || ![self.class isHex:string] )
|
||||
return nil;
|
||||
|
||||
if( [[string substringToIndex:1] isEqualToString:@"#"] )
|
||||
@@ -605,19 +696,12 @@ static NSMutableDictionary * _colorTree = nil;
|
||||
string = str;
|
||||
}
|
||||
|
||||
NSScanner * scanner = [NSScanner scannerWithString:string];
|
||||
unsigned int hex;
|
||||
if( [scanner scanHexInt:&hex] )
|
||||
{
|
||||
NSInteger r = (hex>>16) & 0xFF;
|
||||
NSInteger g = (hex>>8) & 0xFF;
|
||||
NSInteger b = (hex) & 0xFF;
|
||||
return [NSColor colorWithDeviceRed:r/255.f
|
||||
green:g/255.f
|
||||
blue:b/255.f
|
||||
alpha:alpha];
|
||||
}
|
||||
return nil;
|
||||
const char * hexString = [string cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
unsigned long hex = strtoul(hexString, NULL, 16);
|
||||
return [NSColor colorWithDeviceRed:((hex>>16) & 0xFF)/255.f
|
||||
green:((hex>>8) & 0xFF)/255.f
|
||||
blue:(hex & 0xFF)/255.f
|
||||
alpha:alpha];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -41,6 +41,7 @@ typedef NS_ENUM( NSInteger, IJSVGCommandType ) {
|
||||
IJSVGCommand * previousCommand;
|
||||
NSInteger _currentIndex;
|
||||
Class<IJSVGCommandProtocol> commandClass;
|
||||
BOOL isSubCommand;
|
||||
}
|
||||
|
||||
@property ( nonatomic, copy ) NSString * commandString;
|
||||
@@ -52,17 +53,13 @@ typedef NS_ENUM( NSInteger, IJSVGCommandType ) {
|
||||
@property ( nonatomic, retain ) NSMutableArray * subCommands;
|
||||
@property ( nonatomic, assign ) Class<IJSVGCommandProtocol> commandClass;
|
||||
@property ( nonatomic, assign ) IJSVGCommand * previousCommand;
|
||||
@property ( nonatomic, assign ) BOOL isSubCommand;
|
||||
|
||||
- (id)initWithCommandString:(NSString *)commandString;
|
||||
|
||||
+ (NSPoint)readCoordinatePair:(CGFloat *)pairs
|
||||
index:(NSInteger)index;
|
||||
|
||||
+ (void)registerClass:(Class)aClass
|
||||
forCommand:(NSString *)command;
|
||||
+ (NSDictionary *)registeredCommandClasses;
|
||||
+ (Class<IJSVGCommandProtocol>)commandClassForCommandLetter:(NSString *)str;
|
||||
|
||||
- (CGFloat)readFloat;
|
||||
- (NSPoint)readPoint;
|
||||
- (BOOL)readBOOL;
|
||||
|
||||
@@ -9,6 +9,17 @@
|
||||
#import "IJSVGCommand.h"
|
||||
#import "IJSVGUtils.h"
|
||||
|
||||
#import "IJSVGCommandArc.h"
|
||||
#import "IJSVGCommandMove.h"
|
||||
#import "IJSVGCommandClose.h"
|
||||
#import "IJSVGCommandCurve.h"
|
||||
#import "IJSVGCommandLineTo.h"
|
||||
#import "IJSVGCommandVerticalLine.h"
|
||||
#import "IJSVGCommandHorizontalLine.h"
|
||||
#import "IJSVGCommandSmoothCurve.h"
|
||||
#import "IJSVGCommandQuadraticCurve.h"
|
||||
#import "IJSVGCommandCommandSmoothQuadraticCurve.h"
|
||||
|
||||
@implementation IJSVGCommand
|
||||
|
||||
@synthesize commandString;
|
||||
@@ -20,8 +31,7 @@
|
||||
@synthesize requiredParameters;
|
||||
@synthesize type;
|
||||
@synthesize previousCommand;
|
||||
|
||||
static NSMutableDictionary * _classes = nil;
|
||||
@synthesize isSubCommand;
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
@@ -38,10 +48,9 @@ static NSMutableDictionary * _classes = nil;
|
||||
{
|
||||
// work out the basics
|
||||
_currentIndex = 0;
|
||||
subCommands = [[NSMutableArray alloc] init];
|
||||
command = [[str substringToIndex:1] copy];
|
||||
type = [IJSVGUtils typeForCommandString:self.command];
|
||||
commandClass = [[self class] commandClassForCommandLetter:self.command];
|
||||
commandClass = [[self class] commandClassForCommandChar:[self.command characterAtIndex:0]];
|
||||
parameters = [IJSVGUtils commandParameters:str count:¶meterCount];
|
||||
requiredParameters = [self.commandClass requiredParameterCount];
|
||||
|
||||
@@ -54,6 +63,8 @@ static NSMutableDictionary * _classes = nil;
|
||||
sets = self.parameterCount/self.requiredParameters;
|
||||
}
|
||||
|
||||
subCommands = [[NSMutableArray alloc] initWithCapacity:sets];
|
||||
|
||||
// interate over the sets
|
||||
for( NSInteger i = 0; i < sets; i++ ) {
|
||||
// memory for this will be handled by the created subcommand
|
||||
@@ -71,8 +82,9 @@ static NSMutableDictionary * _classes = nil;
|
||||
c.parameters = subParams;
|
||||
c.type = self.type;
|
||||
c.command = self.command;
|
||||
c.previousCommand = [self.subCommands lastObject];
|
||||
c.previousCommand = self.subCommands.lastObject;
|
||||
c.commandClass = self.commandClass;
|
||||
c.isSubCommand = i == 0 ? NO : YES;
|
||||
|
||||
// add it to our tree
|
||||
[self.subCommands addObject:c];
|
||||
@@ -87,33 +99,27 @@ static NSMutableDictionary * _classes = nil;
|
||||
return NSMakePoint( pairs[index*2], pairs[index*2+1]);
|
||||
}
|
||||
|
||||
+ (void)registerClass:(Class)aClass
|
||||
forCommand:(NSString *)command
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_classes = [[NSMutableDictionary alloc] init];
|
||||
});
|
||||
[_classes setObject:NSStringFromClass(aClass)
|
||||
forKey:command];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)registeredCommandClasses
|
||||
{
|
||||
return _classes;
|
||||
}
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
// register here...
|
||||
}
|
||||
|
||||
+ (Class<IJSVGCommandProtocol>)commandClassForCommandLetter:(NSString *)str
|
||||
+ (Class<IJSVGCommandProtocol>)commandClassForCommandChar:(char)aChar
|
||||
{
|
||||
NSString * command = nil;
|
||||
if( ( command = [_classes objectForKey:[str lowercaseString]] ) == nil )
|
||||
return nil;
|
||||
return NSClassFromString(command);
|
||||
aChar = tolower(aChar);
|
||||
switch(aChar) {
|
||||
case 'a': return IJSVGCommandArc.class;
|
||||
case 'c': return IJSVGCommandCurve.class;
|
||||
case 'h': return IJSVGCommandHorizontalLine.class;
|
||||
case 'l': return IJSVGCommandLineTo.class;
|
||||
case 'm': return IJSVGCommandMove.class;
|
||||
case 'q': return IJSVGCommandQuadraticCurve.class;
|
||||
case 's': return IJSVGCommandSmoothCurve.class;
|
||||
case 't': return IJSVGCommandCommandSmoothQuadraticCurve.class;
|
||||
case 'v': return IJSVGCommandVerticalLine.class;
|
||||
case 'z': return IJSVGCommandClose.class;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CGFloat)readFloat
|
||||
|
||||
@@ -11,11 +11,6 @@
|
||||
|
||||
@implementation IJSVGCommandArc
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"a"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
@@ -29,10 +24,6 @@
|
||||
type:(IJSVGCommandType)type
|
||||
path:(IJSVGPath *)path
|
||||
{
|
||||
|
||||
// command was taken from: https://github.com/jmenter/JAMSVGImage/blob/89b375c0c3203355a0c693e3b805458415bf4e29/Classes/JAMSVGImage/Path%20and%20Parser/JAMStyledBezierPathFactory.m
|
||||
// and modified to purpose inside this to be converted into degrees and not radians
|
||||
|
||||
CGPoint radii = CGPointZero;
|
||||
CGPoint arcEndPoint = CGPointZero;
|
||||
CGPoint arcStartPoint = path.currentPoint;
|
||||
@@ -46,7 +37,7 @@
|
||||
sweepFlag = [currentCommand readBOOL];
|
||||
arcEndPoint = [currentCommand readPoint];
|
||||
|
||||
if ( type == IJSVGCommandTypeRelative ) {
|
||||
if (type == IJSVGCommandTypeRelative) {
|
||||
arcEndPoint.x += path.currentPoint.x;
|
||||
arcEndPoint.y += path.currentPoint.y;
|
||||
}
|
||||
@@ -75,15 +66,28 @@
|
||||
|
||||
angleDelta = (ratio(deltaU, deltaV) <= -1) ? M_PI : (ratio(deltaU, deltaV) >= 1) ? 0 : angleDelta;
|
||||
|
||||
// check for actually numbers, if this is not valid
|
||||
// kill it, blame WWDC 2017 SVG background for this...
|
||||
if(isnan(startAngle) || isnan(angleDelta)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
CGPoint scale = (radii.x > radii.y) ? CGPointMake(1, radii.y / radii.x) :
|
||||
CGPointMake(radii.x / radii.y, 1);
|
||||
|
||||
NSAffineTransform * trans = [NSAffineTransform transform];
|
||||
[trans translateXBy:-centerPoint.x yBy:-centerPoint.y];
|
||||
[trans rotateByRadians:-xAxisRotation];
|
||||
[trans scaleXBy:(1/scale.x) yBy:(1/scale.y)];
|
||||
|
||||
[path.currentSubpath transformUsingAffineTransform:trans];
|
||||
|
||||
trans = [NSAffineTransform transform];
|
||||
[trans rotateByRadians:-xAxisRotation];
|
||||
[path.currentSubpath transformUsingAffineTransform:trans];
|
||||
|
||||
trans = [NSAffineTransform transform];
|
||||
[trans scaleXBy:(1/scale.x) yBy:(1/scale.y)];
|
||||
[path.currentSubpath transformUsingAffineTransform:trans];
|
||||
|
||||
[path.currentSubpath appendBezierPathWithArcWithCenter:NSZeroPoint
|
||||
radius:radius
|
||||
startAngle:radians_to_degrees(startAngle)
|
||||
@@ -91,12 +95,16 @@
|
||||
clockwise:!sweepFlag];
|
||||
|
||||
trans = [NSAffineTransform transform];
|
||||
[trans translateXBy:centerPoint.x yBy:centerPoint.y];
|
||||
[trans rotateByRadians:xAxisRotation];
|
||||
[trans scaleXBy:scale.x yBy:scale.y];
|
||||
[path.currentSubpath transformUsingAffineTransform:trans];
|
||||
|
||||
trans = [NSAffineTransform transform];
|
||||
[trans rotateByRadians:xAxisRotation];
|
||||
[path.currentSubpath transformUsingAffineTransform:trans];
|
||||
|
||||
trans = [NSAffineTransform transform];
|
||||
[trans translateXBy:centerPoint.x yBy:centerPoint.y];
|
||||
[path.currentSubpath transformUsingAffineTransform:trans];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,12 +10,6 @@
|
||||
|
||||
@implementation IJSVGCommandClose
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"z"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 0;
|
||||
|
||||
@@ -12,12 +12,6 @@
|
||||
|
||||
@implementation IJSVGCommandCommandSmoothQuadraticCurve
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"t"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 2;
|
||||
|
||||
@@ -10,12 +10,6 @@
|
||||
|
||||
@implementation IJSVGCommandCurve
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"c"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 6;
|
||||
@@ -28,17 +22,15 @@
|
||||
type:(IJSVGCommandType)type
|
||||
path:(IJSVGPath *)path
|
||||
{
|
||||
if( type == IJSVGCommandTypeAbsolute )
|
||||
{
|
||||
if( type == IJSVGCommandTypeAbsolute ) {
|
||||
[[path currentSubpath] curveToPoint:NSMakePoint( params[4], params[5])
|
||||
controlPoint1:NSMakePoint( params[0], params[1])
|
||||
controlPoint2:NSMakePoint( params[2], params[3])];
|
||||
return;
|
||||
}
|
||||
NSPoint point = [[path currentSubpath] currentPoint];
|
||||
[[path currentSubpath] curveToPoint:NSMakePoint( point.x + params[4], point.y + params[5])
|
||||
controlPoint1:NSMakePoint( point.x + params[0], point.y + params[1])
|
||||
controlPoint2:NSMakePoint( point.x + params[2], point.y + params[3])];
|
||||
[[path currentSubpath] relativeCurveToPoint:NSMakePoint( params[4], params[5])
|
||||
controlPoint1:NSMakePoint( params[0], params[1])
|
||||
controlPoint2:NSMakePoint( params[2], params[3])];
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,6 @@
|
||||
|
||||
@implementation IJSVGCommandHorizontalLine
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"h"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 1;
|
||||
|
||||
@@ -10,12 +10,6 @@
|
||||
|
||||
@implementation IJSVGCommandLineTo
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"l"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 2;
|
||||
@@ -28,12 +22,11 @@
|
||||
type:(IJSVGCommandType)type
|
||||
path:(IJSVGPath *)path
|
||||
{
|
||||
if( type == IJSVGCommandTypeAbsolute )
|
||||
{
|
||||
if( type == IJSVGCommandTypeAbsolute ) {
|
||||
[[path currentSubpath] lineToPoint:NSMakePoint( params[0], params[1])];
|
||||
return;
|
||||
}
|
||||
NSPoint point = NSMakePoint( [path currentSubpath].currentPoint.x + params[0],
|
||||
NSPoint point = NSMakePoint([path currentSubpath].currentPoint.x + params[0],
|
||||
[path currentSubpath].currentPoint.y + params[1]);
|
||||
[[path currentSubpath] lineToPoint:point];
|
||||
}
|
||||
|
||||
@@ -12,12 +12,6 @@
|
||||
|
||||
@implementation IJSVGCommandMove
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"m"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 2;
|
||||
@@ -32,8 +26,7 @@
|
||||
{
|
||||
// move to's allow more then one move to, but if there are more then one,
|
||||
// we need to run the line to instead...who knew!
|
||||
if( command.commandClass == [self class])
|
||||
{
|
||||
if( command.commandClass == [self class] && currentCommand.isSubCommand == YES) {
|
||||
[IJSVGCommandLineTo runWithParams:params
|
||||
paramCount:count
|
||||
command:currentCommand
|
||||
@@ -44,8 +37,7 @@
|
||||
}
|
||||
|
||||
// actual move to command
|
||||
if( type == IJSVGCommandTypeAbsolute )
|
||||
{
|
||||
if( type == IJSVGCommandTypeAbsolute ) {
|
||||
[[path currentSubpath] moveToPoint:NSMakePoint( params[0], params[1])];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -11,12 +11,6 @@
|
||||
|
||||
@implementation IJSVGCommandQuadraticCurve
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"q"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 4;
|
||||
|
||||
@@ -12,12 +12,6 @@
|
||||
|
||||
@implementation IJSVGCommandSmoothCurve
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"s"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 4;
|
||||
|
||||
@@ -10,12 +10,6 @@
|
||||
|
||||
@implementation IJSVGCommandVerticalLine
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"v"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 1;
|
||||
|
||||
@@ -15,41 +15,52 @@ typedef void (^IJSVGCGPathHandler)(const CGPathElement * pathElement);
|
||||
void IJSVGExporterPathCaller(void * info, const CGPathElement * pathElement);
|
||||
|
||||
typedef NS_OPTIONS( NSInteger, IJSVGExporterOptions) {
|
||||
IJSVGExporterOptionNone = 1 << 0,
|
||||
IJSVGExporterOptionRemoveUselessGroups = 1 << 1,
|
||||
IJSVGExporterOptionRemoveUselessDef = 1 << 2,
|
||||
IJSVGExporterOptionMoveAttributesToGroup = 1 << 3,
|
||||
IJSVGExporterOptionCreateUseForPaths = 1 << 4,
|
||||
IJSVGExporterOptionSortAttributes = 1 << 5,
|
||||
IJSVGExporterOptionCollapseGroups = 1 << 6,
|
||||
IJSVGExporteroptionCleanupPaths = 1 << 7,
|
||||
IJSVGExporterOptionCleanupPaths = 1 << 7,
|
||||
IJSVGExporterOptionRemoveHiddenElements = 1 << 8,
|
||||
IJSVGExporterOptionScaleToSizeIfNecessary = 1 << 9,
|
||||
IJSVGExporterOptionCompressOutput = 1 << 10,
|
||||
IJSVGExporterOptionCollapseGradients = 1 << 11,
|
||||
IJSVGExporterOptionCreateClasses = 1 << 12,
|
||||
IJSVGExporterOptionRemoveWidthHeightAttributes = 1 << 13,
|
||||
IJSVGExporterOptionAll = IJSVGExporterOptionRemoveUselessDef|
|
||||
IJSVGExporterOptionRemoveUselessGroups|
|
||||
IJSVGExporterOptionCreateUseForPaths|
|
||||
IJSVGExporterOptionMoveAttributesToGroup|
|
||||
IJSVGExporterOptionSortAttributes|
|
||||
IJSVGExporterOptionCollapseGroups|
|
||||
IJSVGExporteroptionCleanupPaths
|
||||
IJSVGExporterOptionCleanupPaths|
|
||||
IJSVGExporterOptionRemoveHiddenElements|
|
||||
IJSVGExporterOptionScaleToSizeIfNecessary|
|
||||
IJSVGExporterOptionCompressOutput|
|
||||
IJSVGExporterOptionCollapseGradients|
|
||||
IJSVGExporterOptionRemoveWidthHeightAttributes
|
||||
};
|
||||
|
||||
@interface IJSVGExporter : NSObject {
|
||||
|
||||
@private
|
||||
IJSVG * _svg;
|
||||
CGSize _size;
|
||||
IJSVGExporterOptions _options;
|
||||
NSXMLDocument * _dom;
|
||||
NSXMLElement * _defElement;
|
||||
NSInteger _gradCount;
|
||||
NSInteger _patternCount;
|
||||
NSInteger _imageCount;
|
||||
NSInteger _maskCount;
|
||||
NSInteger _pathCount;
|
||||
|
||||
NSXMLElement * _scaledRootNode;
|
||||
NSInteger _idCount;
|
||||
NSInteger _shortIdCount;
|
||||
}
|
||||
|
||||
@property (nonatomic, copy) NSString * title;
|
||||
@property (nonatomic, copy) NSString * description;
|
||||
|
||||
- (id)initWithSVG:(IJSVG *)svg
|
||||
size:(CGSize)size
|
||||
options:(IJSVGExporterOptions)options;
|
||||
- (NSString *)SVGString;
|
||||
- (NSData *)SVGData;
|
||||
|
||||
@@ -64,8 +64,11 @@
|
||||
// move
|
||||
case 'M':
|
||||
case 'm': {
|
||||
str = [NSString stringWithFormat:@"%c%g,%g",
|
||||
instruction.instruction,data[0],data[1]];
|
||||
char * buffer;
|
||||
asprintf(&buffer, "%c%g,%g", instruction.instruction, data[0], data[1]);
|
||||
str = [NSString stringWithCString:buffer
|
||||
encoding:NSUTF8StringEncoding];
|
||||
free(buffer);
|
||||
[pathData addObject:str];
|
||||
break;
|
||||
}
|
||||
@@ -75,8 +78,11 @@
|
||||
case 'v':
|
||||
case 'H':
|
||||
case 'h': {
|
||||
str = [NSString stringWithFormat:@"%c%g",
|
||||
instruction.instruction,data[0]];
|
||||
char * buffer;
|
||||
asprintf(&buffer, "%c%g", instruction.instruction, data[0]);
|
||||
str = [NSString stringWithCString:buffer
|
||||
encoding:NSUTF8StringEncoding];
|
||||
free(buffer);
|
||||
[pathData addObject:str];
|
||||
break;
|
||||
}
|
||||
@@ -84,8 +90,11 @@
|
||||
// line
|
||||
case 'L':
|
||||
case 'l': {
|
||||
str = [NSString stringWithFormat:@"%c%g,%g",
|
||||
instruction.instruction, data[0], data[1]];
|
||||
char * buffer;
|
||||
asprintf(&buffer, "%c%g,%g",instruction.instruction, data[0], data[1]);
|
||||
str = [NSString stringWithCString:buffer
|
||||
encoding:NSUTF8StringEncoding];
|
||||
free(buffer);
|
||||
[pathData addObject:str];
|
||||
break;
|
||||
}
|
||||
@@ -93,9 +102,12 @@
|
||||
// curve
|
||||
case 'C':
|
||||
case 'c': {
|
||||
str = [NSString stringWithFormat:@"%c%g,%g %g,%g %g,%g",
|
||||
instruction.instruction, data[0], data[1],
|
||||
data[2], data[3], data[4], data[5]];
|
||||
char * buffer;
|
||||
asprintf(&buffer, "%c%g,%g %g,%g %g,%g", instruction.instruction,
|
||||
data[0], data[1], data[2], data[3], data[4], data[5]);
|
||||
str = [NSString stringWithCString:buffer
|
||||
encoding:NSUTF8StringEncoding];
|
||||
free(buffer);
|
||||
[pathData addObject:str];
|
||||
break;
|
||||
}
|
||||
@@ -103,9 +115,12 @@
|
||||
// quadratic curve
|
||||
case 'Q':
|
||||
case 'q': {
|
||||
str = [NSString stringWithFormat:@"%c%g,%g %g,%g",
|
||||
instruction.instruction, data[0], data[1],
|
||||
data[2], data[3]];
|
||||
char * buffer;
|
||||
asprintf(&buffer, "%c%g,%g %g,%g", instruction.instruction,
|
||||
data[0], data[1], data[2], data[3]);
|
||||
str = [NSString stringWithCString:buffer
|
||||
encoding:NSUTF8StringEncoding];
|
||||
free(buffer);
|
||||
[pathData addObject:str];
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -7,18 +7,23 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "IJSVG.h"
|
||||
|
||||
typedef void (^IJSVGFontConverterEnumerateBlock)(NSString * unicode, IJSVG * svg);
|
||||
|
||||
@interface IJSVGFontConverter : NSObject {
|
||||
|
||||
@private
|
||||
NSURL * _url;
|
||||
NSFont * _font;
|
||||
NSMutableDictionary * _paths;
|
||||
NSMutableDictionary<NSString *, id> * _transformedPaths;
|
||||
}
|
||||
|
||||
- (id)initWithFontAtFileURL:(NSURL *)url;
|
||||
- (NSDictionary *)paths;
|
||||
+ (NSBezierPath *)bezierpathFromCGPath:(CGPathRef)path;
|
||||
- (NSFont *)font;
|
||||
- (void)enumerateUsingBlock:(IJSVGFontConverterEnumerateBlock)block;
|
||||
|
||||
+ (IJSVG *)convertIJSVGPathToSVG:(IJSVGPath *)path;
|
||||
+ (IJSVG *)convertPathToSVG:(CGPathRef)path;
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
|
||||
#import "IJSVGFontConverter.h"
|
||||
#import "IJSVGBezierPathAdditions.h"
|
||||
#import "IJSVGShapeLayer.h"
|
||||
|
||||
@implementation IJSVGFontConverter
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_paths release], _paths = nil;
|
||||
[_transformedPaths release], _transformedPaths = nil;
|
||||
[_url release], _url = nil;
|
||||
[_font release], _font = nil;
|
||||
[super dealloc];
|
||||
@@ -21,8 +22,7 @@
|
||||
|
||||
- (id)initWithFontAtFileURL:(NSURL *)url
|
||||
{
|
||||
if( ( self = [super init] ) != nil )
|
||||
{
|
||||
if( ( self = [super init] ) != nil ) {
|
||||
_url = [url copy];
|
||||
|
||||
// load the font
|
||||
@@ -48,20 +48,16 @@
|
||||
{
|
||||
NSCharacterSet * charSet = _font.coveredCharacterSet;
|
||||
NSMutableArray * array = [[[NSMutableArray alloc] init] autorelease];
|
||||
for( int plane = 0; plane <= 16; plane++ )
|
||||
{
|
||||
if( [charSet hasMemberInPlane:plane] )
|
||||
{
|
||||
NSStringEncoding encoding = NSUTF32LittleEndianStringEncoding;
|
||||
for( int plane = 0; plane <= 16; plane++ ) {
|
||||
if( [charSet hasMemberInPlane:plane] ) {
|
||||
UTF32Char c;
|
||||
for( c = plane << 16; c < (plane+1) << 16; c++ )
|
||||
{
|
||||
if( [charSet longCharacterIsMember:c] )
|
||||
{
|
||||
for( c = plane << 16; c < (plane+1) << 16; c++ ) {
|
||||
if( [charSet longCharacterIsMember:c] ) {
|
||||
UTF32Char c1 = NSSwapHostIntToLittle(c);
|
||||
// add it...
|
||||
[array addObject:[[[NSString alloc] initWithBytes:&c1
|
||||
length:4
|
||||
encoding:NSUTF32LittleEndianStringEncoding] autorelease]];
|
||||
encoding:encoding] autorelease]];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,14 +67,8 @@
|
||||
|
||||
- (void)generateMap
|
||||
{
|
||||
// we have already been made!
|
||||
if( _paths.count != 0 )
|
||||
return;
|
||||
|
||||
_paths = [[NSMutableDictionary alloc] init];
|
||||
CTFontRef font = (CTFontRef)_font;
|
||||
for( NSString * charString in [self allCharacters] )
|
||||
{
|
||||
for( NSString * charString in [self allCharacters] ) {
|
||||
// get the characters in each char
|
||||
NSUInteger count = charString.length;
|
||||
unichar characters[count];
|
||||
@@ -89,93 +79,52 @@
|
||||
CGGlyph glyphs[count];
|
||||
CTFontGetGlyphsForCharacters( font, characters, glyphs, count);
|
||||
CGPathRef path = CTFontCreatePathForGlyph( font, glyphs[0], NULL );
|
||||
if( path != NULL )
|
||||
{
|
||||
NSString * k = [NSString stringWithFormat:@"%04x",[charString characterAtIndex:0]];
|
||||
[self parseCGPath:path
|
||||
forCharacterString:k];
|
||||
if(path != NULL) {
|
||||
// add SVG to the dictionary
|
||||
NSString * key = [NSString stringWithFormat:@"%04x",[charString characterAtIndex:0]];
|
||||
CGPathRef flippedPath = [IJSVGUtils newFlippedCGPath:path];
|
||||
_transformedPaths[key] = (id)flippedPath;
|
||||
CGPathRelease(flippedPath);
|
||||
}
|
||||
CGPathRelease(path);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)parseCGPath:(CGPathRef)path
|
||||
forCharacterString:(NSString *)string
|
||||
- (void)enumerateUsingBlock:(IJSVGFontConverterEnumerateBlock)block
|
||||
{
|
||||
[_paths setObject:[[self class] bezierpathFromCGPath:path]
|
||||
forKey:string];
|
||||
}
|
||||
|
||||
- (NSDictionary *)paths
|
||||
{
|
||||
[self generateMap];
|
||||
return _paths;
|
||||
}
|
||||
|
||||
static void IJSVGCGPathCallback(void * info, const CGPathElement * element)
|
||||
{
|
||||
NSBezierPath * path = (NSBezierPath *)info;
|
||||
CGPoint * points = element->points;
|
||||
switch( element->type )
|
||||
{
|
||||
// move to
|
||||
case kCGPathElementMoveToPoint:
|
||||
{
|
||||
[path moveToPoint:NSMakePoint( points[0].x, points[0].y)];
|
||||
break;
|
||||
}
|
||||
|
||||
// line to
|
||||
case kCGPathElementAddLineToPoint:
|
||||
{
|
||||
[path lineToPoint:NSMakePoint( points[0].x, points[0].y)];;
|
||||
break;
|
||||
}
|
||||
|
||||
// quad curve
|
||||
case kCGPathElementAddQuadCurveToPoint:
|
||||
{
|
||||
[path addQuadCurveToPoint:points[1] controlPoint:points[0]];
|
||||
break;
|
||||
}
|
||||
|
||||
// curve to
|
||||
case kCGPathElementAddCurveToPoint:
|
||||
{
|
||||
[path curveToPoint:NSMakePoint(points[2].x, points[2].y)
|
||||
controlPoint1:NSMakePoint( points[0].x, points[0].y)
|
||||
controlPoint2:NSMakePoint( points[1].x, points[1].y)];
|
||||
break;
|
||||
}
|
||||
|
||||
// close
|
||||
case kCGPathElementCloseSubpath: {
|
||||
[path closePath];
|
||||
break;
|
||||
}
|
||||
if(_transformedPaths == nil) {
|
||||
_transformedPaths = [[NSMutableDictionary alloc] init];
|
||||
[self generateMap];
|
||||
}
|
||||
|
||||
for(NSString * key in _transformedPaths.allKeys) {
|
||||
block(key, [self.class convertPathToSVG:(CGPathRef)_transformedPaths[key]]);
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSBezierPath *)bezierpathFromCGPath:(CGPathRef)path
|
||||
+ (IJSVG *)convertIJSVGPathToSVG:(IJSVGPath *)path
|
||||
{
|
||||
NSBezierPath * bezPath = [NSBezierPath bezierPath];
|
||||
|
||||
// pass the path
|
||||
CGPathApply( path, bezPath, IJSVGCGPathCallback );
|
||||
|
||||
// the glyph will be upside down, so we need to turn it up the right way!
|
||||
NSAffineTransform * trans = [NSAffineTransform transform];
|
||||
|
||||
// scale -1 on the Y axis so its now correct but to far up
|
||||
[trans scaleXBy:1.f
|
||||
yBy:-1.f];
|
||||
|
||||
// move it back down by its height
|
||||
[trans translateXBy:0.f
|
||||
yBy:bezPath.controlPointBounds.size.height];
|
||||
|
||||
[bezPath transformUsingAffineTransform:trans];
|
||||
return bezPath;
|
||||
CGPathRef cgPath = [IJSVGUtils newCGPathFromBezierPath:path.path];
|
||||
CGPathRef flippedPath = [IJSVGUtils newFlippedCGPath:cgPath];
|
||||
IJSVG * svg = [self convertPathToSVG:flippedPath];
|
||||
CGPathRelease(flippedPath);
|
||||
CGPathRelease(cgPath);
|
||||
return svg;
|
||||
}
|
||||
|
||||
+ (IJSVG *)convertPathToSVG:(CGPathRef)path
|
||||
{
|
||||
__block IJSVG * svg = nil;
|
||||
IJSVGObtainTransactionLock(^{
|
||||
IJSVGGroupLayer * layer = [[[IJSVGGroupLayer alloc] init] autorelease];
|
||||
IJSVGShapeLayer * shape = [[[IJSVGShapeLayer alloc] init] autorelease];
|
||||
[layer addSublayer:shape];
|
||||
shape.path = path;
|
||||
CGRect box = CGPathGetPathBoundingBox(path);
|
||||
svg = [[IJSVG alloc] initWithSVGLayer:layer
|
||||
viewBox:box];
|
||||
}, NO);
|
||||
return [svg autorelease];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
@interface IJSVGGradient : IJSVGNode
|
||||
|
||||
@property ( nonatomic, retain ) NSGradient * gradient;
|
||||
@property ( nonatomic, assign ) CGFloat angle;
|
||||
@property ( nonatomic, assign ) CGPoint startPoint;
|
||||
@property ( nonatomic, assign ) CGPoint endPoint;
|
||||
@property ( nonatomic, assign ) CGGradientRef CGGradient;
|
||||
@property ( nonatomic, retain ) IJSVGUnitLength * x1;
|
||||
@property ( nonatomic, retain ) IJSVGUnitLength * x2;
|
||||
@@ -26,6 +23,8 @@
|
||||
colors:(NSArray **)someColors;
|
||||
- (CGGradientRef)CGGradient;
|
||||
- (void)drawInContextRef:(CGContextRef)ctx
|
||||
rect:(NSRect)rect;
|
||||
objectRect:(NSRect)objectRect
|
||||
absoluteTransform:(CGAffineTransform)absoluteTransform
|
||||
viewPort:(CGRect)viewBox;
|
||||
|
||||
@end
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
@implementation IJSVGGradient
|
||||
|
||||
@synthesize gradient, CGGradient;
|
||||
@synthesize angle, startPoint, endPoint;
|
||||
@synthesize x1, x2, y1, y2;
|
||||
|
||||
- (void)dealloc
|
||||
@@ -31,8 +30,6 @@
|
||||
{
|
||||
IJSVGGradient * clone = [super copyWithZone:zone];
|
||||
clone.gradient = [[self.gradient copy] autorelease];
|
||||
clone.startPoint = self.startPoint;
|
||||
clone.endPoint = self.endPoint;
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -40,16 +37,13 @@
|
||||
colors:(NSArray **)someColors
|
||||
{
|
||||
// find each stop element
|
||||
NSArray * stops = [element nodesForXPath:@"stop"
|
||||
error:nil];
|
||||
|
||||
NSMutableArray * colors = [[[NSMutableArray alloc] init] autorelease];
|
||||
NSArray * stops = [element children];
|
||||
NSMutableArray * colors = [[[NSMutableArray alloc] initWithCapacity:stops.count] autorelease];
|
||||
CGFloat * stopsParams = (CGFloat *)malloc(stops.count*sizeof(CGFloat));
|
||||
NSInteger i = 0;
|
||||
for( NSXMLElement * stop in stops )
|
||||
{
|
||||
for( NSXMLElement * stop in stops ) {
|
||||
// find the offset
|
||||
CGFloat offset = [[[stop attributeForName:@"offset"] stringValue] floatValue];
|
||||
CGFloat offset = [stop attributeForName:@"offset"].stringValue.floatValue;
|
||||
if( offset > 1.f ) {
|
||||
offset /= 100.f;
|
||||
}
|
||||
@@ -60,48 +54,50 @@
|
||||
CGFloat stopOpacity = 1.f;
|
||||
NSXMLNode * stopOpacityAttribute = [stop attributeForName:@"stop-opacity"];
|
||||
if( stopOpacityAttribute != nil ) {
|
||||
stopOpacity = [[stopOpacityAttribute stringValue] floatValue];
|
||||
stopOpacity = stopOpacityAttribute.stringValue.floatValue;
|
||||
}
|
||||
|
||||
// find the stop color
|
||||
NSColor * stopColor = [IJSVGColor colorFromHEXString:[[stop attributeForName:@"stop-color"] stringValue]
|
||||
alpha:stopOpacity];
|
||||
|
||||
// no hex, grab it from predefined
|
||||
if( stopColor == nil )
|
||||
{
|
||||
stopColor = [IJSVGColor colorFromPredefinedColorName:[[stop attributeForName:@"stop-color"] stringValue]];
|
||||
if( stopColor != nil && stopOpacity != 1.f ) {
|
||||
stopColor = [IJSVGColor changeAlphaOnColor:stopColor
|
||||
to:stopOpacity];
|
||||
}
|
||||
}
|
||||
|
||||
// add it into the array
|
||||
if( stopColor != nil ) {
|
||||
[(NSMutableArray *)colors addObject:stopColor];
|
||||
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]];
|
||||
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?
|
||||
NSNumber * number = nil;
|
||||
if( (number = [style property:@"stop-opacity"] ) != nil ) {
|
||||
NSString * numberString = nil;
|
||||
if( (numberString = [style property:@"stop-opacity"] ) != nil ) {
|
||||
color = [IJSVGColor changeAlphaOnColor:color
|
||||
to:[number floatValue]];
|
||||
to:numberString.floatValue];
|
||||
} else {
|
||||
color = [IJSVGColor changeAlphaOnColor:color
|
||||
to:stopOpacity];
|
||||
}
|
||||
[(NSMutableArray *)colors addObject:color];
|
||||
stopColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
// default is black
|
||||
if(stopColor == nil) {
|
||||
stopColor = [IJSVGColor colorFromString:@"black"];
|
||||
if(stopOpacity != 1.f) {
|
||||
stopColor = [IJSVGColor changeAlphaOnColor:stopColor
|
||||
to:stopOpacity];
|
||||
}
|
||||
}
|
||||
|
||||
// add the stop color
|
||||
[(NSMutableArray *)colors addObject:stopColor];
|
||||
}
|
||||
*someColors = colors;
|
||||
return stopsParams;
|
||||
@@ -132,7 +128,9 @@
|
||||
}
|
||||
|
||||
- (void)drawInContextRef:(CGContextRef)ctx
|
||||
rect:(NSRect)rect
|
||||
objectRect:(NSRect)objectRect
|
||||
absoluteTransform:(CGAffineTransform)absoluteTransform
|
||||
viewPort:(CGRect)viewBox
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) CGRect viewBox;
|
||||
@property (nonatomic, retain) IJSVGGradient * gradient;
|
||||
@property (nonatomic, assign) CGAffineTransform absoluteTransform;
|
||||
@property (nonatomic, assign) CGRect objectRect;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
|
||||
@implementation IJSVGGradientLayer
|
||||
|
||||
@synthesize viewBox;
|
||||
@synthesize gradient;
|
||||
@synthesize absoluteTransform;
|
||||
@synthesize objectRect;
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
@@ -22,10 +25,60 @@
|
||||
{
|
||||
if((self = [super init]) != nil) {
|
||||
self.requiresBackingScaleHelp = YES;
|
||||
self.shouldRasterize = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setGradient:(IJSVGGradient *)newGradient
|
||||
{
|
||||
if(gradient != nil) {
|
||||
[gradient release], gradient = nil;
|
||||
}
|
||||
gradient = [newGradient retain];
|
||||
|
||||
// lets check its alpha properties on the colors
|
||||
BOOL hasAlphaChannel = NO;
|
||||
NSInteger stops = gradient.gradient.numberOfColorStops;
|
||||
for(NSInteger i = 0; i < stops; i++) {
|
||||
NSColor * color = nil;
|
||||
[gradient.gradient getColor:&color
|
||||
location:NULL
|
||||
atIndex:i];
|
||||
if(color.alphaComponent != 1.f) {
|
||||
hasAlphaChannel = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.opaque = hasAlphaChannel == NO;
|
||||
}
|
||||
|
||||
- (void)setOpacity:(float)opacity
|
||||
{
|
||||
if(opacity != 1.f) {
|
||||
self.opaque = NO;
|
||||
}
|
||||
[super setOpacity:opacity];
|
||||
}
|
||||
|
||||
- (void)setBackingScaleFactor:(CGFloat)backingScaleFactor
|
||||
{
|
||||
switch (self.renderQuality) {
|
||||
case IJSVGRenderQualityOptimized: {
|
||||
backingScaleFactor = .35f;
|
||||
break;
|
||||
}
|
||||
case IJSVGRenderQualityLow: {
|
||||
backingScaleFactor = .05f;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
[super setBackingScaleFactor:backingScaleFactor];
|
||||
}
|
||||
|
||||
- (void)drawInContext:(CGContextRef)ctx
|
||||
{
|
||||
[super drawInContext:ctx];
|
||||
@@ -36,7 +89,15 @@
|
||||
}
|
||||
|
||||
// draw the gradient
|
||||
[self.gradient drawInContextRef:ctx rect:self.bounds];
|
||||
CGAffineTransform trans = CGAffineTransformMakeTranslation(-CGRectGetMinX(objectRect),
|
||||
-CGRectGetMinY(objectRect));
|
||||
CGAffineTransform transform = CGAffineTransformConcat(absoluteTransform,trans);
|
||||
CGContextSaveGState(ctx);
|
||||
[self.gradient drawInContextRef:ctx
|
||||
objectRect:objectRect
|
||||
absoluteTransform:transform
|
||||
viewPort:self.viewBox];
|
||||
CGContextRestoreGState(ctx);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// IJSVGGradientUnitLength.h
|
||||
// IconJar
|
||||
//
|
||||
// Created by Curtis Hard on 29/03/2017.
|
||||
// Copyright © 2017 Curtis Hard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "IJSVGUnitLength.h"
|
||||
|
||||
@interface IJSVGGradientUnitLength : IJSVGUnitLength
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// IJSVGGradientUnitLength.m
|
||||
// IconJar
|
||||
//
|
||||
// Created by Curtis Hard on 29/03/2017.
|
||||
// Copyright © 2017 Curtis Hard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "IJSVGGradientUnitLength.h"
|
||||
|
||||
@implementation IJSVGGradientUnitLength
|
||||
|
||||
- (NSString *)stringValue
|
||||
{
|
||||
if(self.type == IJSVGUnitLengthTypePercentage) {
|
||||
return [NSString stringWithFormat:@"%g",self.value];
|
||||
}
|
||||
return [super stringValue];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"%@ - %ld",[super description],self.children.count];
|
||||
return [NSString stringWithFormat:@"%@ - %@",[super description],self.children];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -22,10 +22,16 @@
|
||||
|
||||
- (void)loadFromBase64EncodedString:(NSString *)encodedString
|
||||
{
|
||||
if([encodedString hasPrefix:@"data:"]) {
|
||||
encodedString = [encodedString stringByReplacingOccurrencesOfString:@"\\s+"
|
||||
withString:@""
|
||||
options:NSRegularExpressionSearch
|
||||
range:NSMakeRange(0, encodedString.length)];
|
||||
}
|
||||
NSURL * URL = [NSURL URLWithString:encodedString];
|
||||
NSData * data = [NSData dataWithContentsOfURL:URL];
|
||||
|
||||
// no data, jsut ignore...invalid probably
|
||||
// no data, just ignore...invalid probably
|
||||
if(data == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,14 +28,28 @@
|
||||
// set the contents
|
||||
self.contents = (id)imageRef;
|
||||
|
||||
// make sure we say we need help
|
||||
self.requiresBackingScaleHelp = YES;
|
||||
self.shouldRasterize = YES;
|
||||
|
||||
// set the frame, simple stuff
|
||||
self.frame = (CGRect){
|
||||
.origin = CGPointZero,
|
||||
.size = CGSizeMake( CGImageGetWidth(imageRef),
|
||||
CGImageGetHeight(imageRef))
|
||||
.size = CGSizeMake(CGImageGetWidth(imageRef),
|
||||
CGImageGetHeight(imageRef))
|
||||
};
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setNeedsDisplay
|
||||
{
|
||||
// swap the content around on call
|
||||
// because set needs display discards previous
|
||||
// content - yolo!
|
||||
id oldContent = self.contents;
|
||||
[super setNeedsDisplay];
|
||||
self.contents = oldContent;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// IJSVGImageRep.h
|
||||
// IJSVGExample
|
||||
//
|
||||
// Created by Curtis Hard on 15/03/2019.
|
||||
// Copyright © 2019 Curtis Hard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "IJSVGParser.h"
|
||||
|
||||
@class IJSVG;
|
||||
|
||||
@interface IJSVGImageRep : NSImageRep {
|
||||
|
||||
@private
|
||||
IJSVG * _svg;
|
||||
}
|
||||
|
||||
@property (nonatomic, readonly) CGRect viewBox;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// IJSVGImageRep.m
|
||||
// IJSVGExample
|
||||
//
|
||||
// Created by Curtis Hard on 15/03/2019.
|
||||
// Copyright © 2019 Curtis Hard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "IJSVGImageRep.h"
|
||||
#import "IJSVG.h"
|
||||
|
||||
@implementation IJSVGImageRep
|
||||
|
||||
@synthesize viewBox = _viewBox;
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[NSBitmapImageRep registerImageRepClass:self];
|
||||
}
|
||||
|
||||
+ (BOOL)canInitWithData:(NSData *)data
|
||||
{
|
||||
return [IJSVGParser isDataSVG:data];
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)imageTypes
|
||||
{
|
||||
return @[(NSString *)kUTTypeScalableVectorGraphics, @"svg"];
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)imageUnfilteredTypes
|
||||
{
|
||||
return @[(NSString *)kUTTypeScalableVectorGraphics, @"svg"];
|
||||
}
|
||||
|
||||
+ (NSArray<NSImageRep *> *)imageRepsWithData:(NSData *)data
|
||||
{
|
||||
IJSVGImageRep * instance = [self imageRepWithData:data];
|
||||
if(instance == nil) {
|
||||
return @[];
|
||||
}
|
||||
return @[instance];
|
||||
}
|
||||
|
||||
+ (instancetype)imageRepWithData:(NSData *)data
|
||||
{
|
||||
return [[self alloc] initWithData:data];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_svg release], _svg = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (instancetype)initWithData:(NSData *)data
|
||||
{
|
||||
if((self = [super init]) != nil) {
|
||||
// grab the string from the data
|
||||
// its more then likely UTF-8...
|
||||
NSString * string = [[[NSString alloc] initWithData:data
|
||||
encoding:NSUTF8StringEncoding] autorelease];
|
||||
|
||||
_svg = [[IJSVG alloc] initWithSVGString:string];
|
||||
|
||||
// no valid SVG, just return nil;
|
||||
if(_svg == nil) {
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
|
||||
// set default properties
|
||||
self.pixelsWide = _svg.viewBox.size.width;
|
||||
self.pixelsHigh = _svg.viewBox.size.height;
|
||||
self.size = _svg.viewBox.size;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)draw
|
||||
{
|
||||
[_svg drawInRect:self.viewBox];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)drawAtPoint:(NSPoint)point
|
||||
{
|
||||
[_svg drawAtPoint:point
|
||||
size:_svg.viewBox.size];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)drawInRect:(NSRect)rect
|
||||
{
|
||||
[_svg drawInRect:rect];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CGRect)viewBox
|
||||
{
|
||||
return _svg.viewBox;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -8,60 +8,38 @@
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import "IJSVGTransaction.h"
|
||||
#import "IJSVGRendering.h"
|
||||
|
||||
@class IJSVGShapeLayer;
|
||||
@class IJSVGGradientLayer;
|
||||
@class IJSVGPatternLayer;
|
||||
@class IJSVGStrokeLayer;
|
||||
|
||||
#define IJSVG_LAYER_ADD_SUBVIEW_DEFAULT_IMPLEMENTATION \
|
||||
- (void)addSublayer:(CALayer *)layer { \
|
||||
if([layer isKindOfClass:[IJSVGLayer class]] == NO && \
|
||||
[layer isKindOfClass:[IJSVGShapeLayer class]] == NO) { \
|
||||
NSString * r = [NSString stringWithFormat:@"The layer must be an instance of IJSVGLayer, %@ given.", \
|
||||
[layer class]]; \
|
||||
NSException * exception = [NSException exceptionWithName:@"IJSVGInvalidSublayerException"\
|
||||
reason:r \
|
||||
userInfo:nil];\
|
||||
@throw exception; \
|
||||
}\
|
||||
[super addSublayer:layer];\
|
||||
} \
|
||||
\
|
||||
- (void)setBackingScaleFactor:(CGFloat)newFactor \
|
||||
{ \
|
||||
if(self.backingScaleFactor == newFactor) { \
|
||||
return; \
|
||||
} \
|
||||
backingScaleFactor = newFactor; \
|
||||
self.contentsScale = newFactor; \
|
||||
self.rasterizationScale = newFactor; \
|
||||
[self setNeedsDisplay]; \
|
||||
};
|
||||
|
||||
#define IJSVG_LAYER_DEFAULT_PROPERTIES \
|
||||
@property (nonatomic, assign) IJSVGGradientLayer * gradientFillLayer; \
|
||||
@property (nonatomic, assign) IJSVGPatternLayer * patternFillLayer; \
|
||||
@property (nonatomic, assign) IJSVGStrokeLayer * strokeLayer; \
|
||||
@property (nonatomic, assign) BOOL requiresBackingScaleHelp; \
|
||||
@property (nonatomic, assign) CGFloat backingScaleFactor;
|
||||
|
||||
#define IJSVG_LAYER_DEFAULT_SYNTHESIZE \
|
||||
@synthesize gradientFillLayer; \
|
||||
@synthesize patternFillLayer; \
|
||||
@synthesize strokeLayer; \
|
||||
@synthesize requiresBackingScaleHelp; \
|
||||
@synthesize backingScaleFactor;
|
||||
|
||||
#define IJSVG_LAYER_DEFAULT_DEALLOC_INSTRUCTIONS \
|
||||
IJSVGBeginTransactionLock(); \
|
||||
[super dealloc]; \
|
||||
IJSVGEndTransactionLock();
|
||||
@class IJSVGGroupLayer;
|
||||
|
||||
@interface IJSVGLayer : CALayer {
|
||||
|
||||
@private
|
||||
IJSVGLayer * _maskingLayer;
|
||||
}
|
||||
|
||||
IJSVG_LAYER_DEFAULT_PROPERTIES
|
||||
@property (nonatomic, assign) IJSVGGradientLayer * gradientFillLayer;
|
||||
@property (nonatomic, assign) IJSVGPatternLayer * patternFillLayer;
|
||||
@property (nonatomic, assign) IJSVGStrokeLayer * strokeLayer;
|
||||
@property (nonatomic, assign) IJSVGGradientLayer * gradientStrokeLayer;
|
||||
@property (nonatomic, assign) IJSVGPatternLayer * patternStrokeLayer;
|
||||
@property (nonatomic, assign) BOOL requiresBackingScaleHelp;
|
||||
@property (nonatomic, assign) CGFloat backingScaleFactor;
|
||||
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
|
||||
@property (nonatomic, assign) CGBlendMode blendingMode;
|
||||
@property (nonatomic, assign) CGPoint absoluteOrigin;
|
||||
@property (nonatomic, assign) BOOL convertMasksToPaths;
|
||||
|
||||
+ (NSArray *)deepestSublayersOfLayer:(CALayer *)layer;
|
||||
+ (void)recursivelyWalkLayer:(CALayer *)layer
|
||||
withBlock:(void (^)(CALayer * layer, BOOL isMask))block;
|
||||
|
||||
- (void)applySublayerMaskToContext:(CGContextRef)context
|
||||
forSublayer:(IJSVGLayer *)sublayer
|
||||
withOffset:(CGPoint)offset;
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,19 +8,177 @@
|
||||
|
||||
#import "IJSVGLayer.h"
|
||||
#import "IJSVGShapeLayer.h"
|
||||
#import "IJSVGGroupLayer.h"
|
||||
#import "IJSVG.h"
|
||||
|
||||
|
||||
@implementation IJSVGLayer
|
||||
|
||||
IJSVG_LAYER_DEFAULT_SYNTHESIZE
|
||||
@synthesize gradientFillLayer;
|
||||
@synthesize patternFillLayer;
|
||||
@synthesize gradientStrokeLayer;
|
||||
@synthesize patternStrokeLayer;
|
||||
@synthesize strokeLayer;
|
||||
@synthesize requiresBackingScaleHelp;
|
||||
@synthesize backingScaleFactor;
|
||||
@synthesize blendingMode;
|
||||
@synthesize convertMasksToPaths;
|
||||
|
||||
#ifndef __clang_analyzer__
|
||||
- (void)dealloc
|
||||
{
|
||||
IJSVG_LAYER_DEFAULT_DEALLOC_INSTRUCTIONS
|
||||
[_maskingLayer release], _maskingLayer = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
#endif
|
||||
|
||||
IJSVG_LAYER_ADD_SUBVIEW_DEFAULT_IMPLEMENTATION
|
||||
+ (NSArray *)deepestSublayersOfLayer:(CALayer *)layer
|
||||
{
|
||||
NSMutableArray * arr = [[[NSMutableArray alloc] init] autorelease];
|
||||
for(CALayer * subLayer in layer.sublayers) {
|
||||
if(subLayer.sublayers.count != 0) {
|
||||
NSArray * moreLayers = [self deepestSublayersOfLayer:(IJSVGLayer *)subLayer];
|
||||
[arr addObjectsFromArray:moreLayers];
|
||||
} else {
|
||||
[arr addObject:subLayer];
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
+ (void)recursivelyWalkLayer:(CALayer *)layer
|
||||
withBlock:(void (^)(CALayer * layer, BOOL isMask))block
|
||||
{
|
||||
// call for layer and mask if there is one
|
||||
block(layer, NO);
|
||||
|
||||
// do the mask too!
|
||||
if(layer.mask != nil) {
|
||||
block(layer.mask, YES);
|
||||
}
|
||||
|
||||
// sublayers!!
|
||||
for(CALayer * aLayer in layer.sublayers) {
|
||||
[self recursivelyWalkLayer:aLayer
|
||||
withBlock:block];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addSublayer:(CALayer *)layer
|
||||
{
|
||||
if([layer isKindOfClass:[IJSVGLayer class]] == NO &&
|
||||
[layer isKindOfClass:[IJSVGShapeLayer class]] == NO) {
|
||||
NSString * r = [NSString stringWithFormat:@"The layer must be an instance of IJSVGLayer, %@ given.",
|
||||
[layer class]];
|
||||
NSException * exception = [NSException exceptionWithName:@"IJSVGInvalidSublayerException"
|
||||
reason:r
|
||||
userInfo:nil];
|
||||
@throw exception;
|
||||
}
|
||||
[super addSublayer:layer];
|
||||
}
|
||||
|
||||
- (void)setBackingScaleFactor:(CGFloat)newFactor
|
||||
{
|
||||
if(self.backingScaleFactor == newFactor) {
|
||||
return;
|
||||
}
|
||||
backingScaleFactor = newFactor;
|
||||
self.contentsScale = newFactor;
|
||||
self.rasterizationScale = newFactor;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)_customRenderInContext:(CGContextRef)ctx
|
||||
{
|
||||
if(self.convertMasksToPaths == YES && _maskingLayer != nil) {
|
||||
CGContextSaveGState(ctx);
|
||||
[self applySublayerMaskToContext:ctx
|
||||
forSublayer:(IJSVGLayer *)self
|
||||
withOffset:CGPointZero];
|
||||
[super renderInContext:ctx];
|
||||
CGContextRestoreGState(ctx);
|
||||
return;
|
||||
}
|
||||
[super renderInContext:ctx];
|
||||
}
|
||||
|
||||
- (void)setConvertMasksToPaths:(BOOL)flag
|
||||
{
|
||||
if(convertMasksToPaths == flag) {
|
||||
return;
|
||||
}
|
||||
convertMasksToPaths = flag;
|
||||
if(flag == YES) {
|
||||
if(_maskingLayer != nil){
|
||||
[_maskingLayer release], _maskingLayer = nil;
|
||||
}
|
||||
_maskingLayer = [(IJSVGLayer *)self.mask retain];
|
||||
self.mask = nil;
|
||||
} else {
|
||||
self.mask = _maskingLayer;
|
||||
[_maskingLayer release], _maskingLayer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applySublayerMaskToContext:(CGContextRef)context
|
||||
forSublayer:(IJSVGLayer *)sublayer
|
||||
withOffset:(CGPoint)offset
|
||||
{
|
||||
// apply any transforms needed
|
||||
CGPoint layerOffset = offset;
|
||||
CGAffineTransform sublayerTransform = CATransform3DGetAffineTransform(sublayer.transform);
|
||||
CGContextConcatCTM( context, CGAffineTransformInvert(sublayerTransform) );
|
||||
|
||||
// walk up the superlayer chain
|
||||
CALayer * superlayer = self.superlayer;
|
||||
if (IJSVGIsSVGLayer(superlayer) == YES) {
|
||||
[(IJSVGLayer *)superlayer applySublayerMaskToContext:context
|
||||
forSublayer:(IJSVGLayer *)self
|
||||
withOffset:layerOffset];
|
||||
}
|
||||
|
||||
// grab the masking layer
|
||||
IJSVGShapeLayer * maskingLayer = [self maskingLayer];
|
||||
|
||||
// if its a group we need to get the lowest level children
|
||||
// and walk up the chain again
|
||||
if([maskingLayer isKindOfClass:[IJSVGGroupLayer class]]) {
|
||||
NSArray * subs = [IJSVGLayer deepestSublayersOfLayer:maskingLayer];
|
||||
for(IJSVGLayer * subLayer in subs) {
|
||||
[subLayer applySublayerMaskToContext:context
|
||||
forSublayer:(IJSVGLayer *)self
|
||||
withOffset:layerOffset];
|
||||
}
|
||||
} else if ([maskingLayer isKindOfClass:[IJSVGShapeLayer class]]) {
|
||||
// is a shape, go for it!
|
||||
CGPathRef maskPath = maskingLayer.path;
|
||||
CGContextTranslateCTM(context, -layerOffset.x, -layerOffset.y);
|
||||
CGContextAddPath(context, maskPath);
|
||||
CGContextClip(context);
|
||||
CGContextTranslateCTM(context, layerOffset.x, layerOffset.y);
|
||||
}
|
||||
CGContextConcatCTM(context, sublayerTransform);
|
||||
}
|
||||
|
||||
- (IJSVGShapeLayer *)maskingLayer
|
||||
{
|
||||
return (IJSVGShapeLayer *)_maskingLayer ?: nil;
|
||||
}
|
||||
|
||||
- (void)renderInContext:(CGContextRef)ctx
|
||||
{
|
||||
if(self.blendingMode != kCGBlendModeNormal) {
|
||||
CGContextSaveGState(ctx);
|
||||
CGContextSetBlendMode(ctx, self.blendingMode);
|
||||
[self _customRenderInContext:ctx];
|
||||
CGContextRestoreGState(ctx);
|
||||
return;
|
||||
}
|
||||
[self _customRenderInContext:ctx];
|
||||
}
|
||||
|
||||
- (id<CAAction>)actionForKey:(NSString *)event
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -15,8 +15,13 @@
|
||||
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) CGRect viewBox;
|
||||
@property (nonatomic, retain) NSColor * fillColor;
|
||||
@property (nonatomic, retain) NSColor * strokeColor;
|
||||
@property (nonatomic, assign) CGFloat strokeWidth;
|
||||
@property (nonatomic, assign) IJSVGLineJoinStyle lineJoinStyle;
|
||||
@property (nonatomic, assign) IJSVGLineCapStyle lineCapStyle;
|
||||
@property (nonatomic, retain) NSDictionary<NSColor *, NSColor *> * replacementColors;
|
||||
|
||||
- (IJSVGLayer *)layerForNode:(IJSVGNode *)node;
|
||||
|
||||
|
||||
@@ -26,18 +26,31 @@
|
||||
|
||||
@implementation IJSVGLayerTree
|
||||
|
||||
#define DEFAULT_SHAPE_FILL_COLOR [NSColor blackColor].CGColor
|
||||
|
||||
@synthesize viewBox;
|
||||
@synthesize fillColor;
|
||||
@synthesize strokeColor;
|
||||
@synthesize strokeWidth;
|
||||
@synthesize lineJoinStyle;
|
||||
@synthesize lineCapStyle;
|
||||
@synthesize replacementColors;
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[fillColor release], fillColor = nil;
|
||||
[strokeColor release], strokeColor = nil;
|
||||
[replacementColors release], replacementColors = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if((self = [super init]) != nil) {
|
||||
self.lineJoinStyle = IJSVGLineJoinStyleNone;
|
||||
self.lineCapStyle = IJSVGLineCapStyleNone;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (IJSVGLayer *)layerForNode:(IJSVGNode *)node
|
||||
{
|
||||
IJSVGLayer * layer = nil;
|
||||
@@ -54,44 +67,67 @@
|
||||
layer = [self layerForImage:(IJSVGImage *)node];
|
||||
}
|
||||
|
||||
// apply any basic defaults
|
||||
[self applyDefaultsToLayer:layer
|
||||
fromNode:node];
|
||||
[self applyDefaultsToLayer:layer fromNode:node];
|
||||
|
||||
return [self proposedLayerAfterApplyingTransforms:layer
|
||||
transforms:node.transforms];
|
||||
// create the new layer
|
||||
layer = [self applyTransforms:node.transforms
|
||||
toLayer:layer
|
||||
fromNode:node];
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
- (IJSVGLayer *)proposedLayerAfterApplyingTransforms:(IJSVGLayer *)layer
|
||||
transforms:(NSArray<IJSVGTransform *> *)transforms
|
||||
- (IJSVGLayer *)applyTransforms:(NSArray<IJSVGTransform *> *)transforms
|
||||
toLayer:(IJSVGLayer *)layer
|
||||
fromNode:(IJSVGNode *)node
|
||||
|
||||
{
|
||||
// any x and y?
|
||||
CGFloat x = [node.x computeValue:layer.frame.size.width];
|
||||
CGFloat y = [node.y computeValue:layer.frame.size.height];
|
||||
|
||||
// do some magic transform
|
||||
if(transforms.count == 0 && x == 0.f && y == 0.f) {
|
||||
return layer;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// add any transforms
|
||||
if(transforms.count != 0) {
|
||||
IJSVGLayer * topLayer = nil;
|
||||
IJSVGLayer * parentLayer = nil;
|
||||
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;
|
||||
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// swap the layer around
|
||||
[parentLayer addSublayer:layer];
|
||||
layer = topLayer;
|
||||
// reset parent layer to the new child
|
||||
parentLayer = childLayer;
|
||||
}
|
||||
|
||||
// swap the layer around
|
||||
[parentLayer addSublayer:layer];
|
||||
layer = topLayer;
|
||||
return layer;
|
||||
}
|
||||
|
||||
@@ -99,37 +135,59 @@
|
||||
fromNode:(IJSVGNode *)node
|
||||
{
|
||||
CGFloat opacity = node.opacity.value;
|
||||
if(opacity == 0.f) {
|
||||
opacity = 1.f;
|
||||
}
|
||||
layer.opacity = opacity;
|
||||
|
||||
// setup the blending mode
|
||||
if(node.blendMode != IJSVGBlendModeNormal) {
|
||||
layer.blendingMode = (CGBlendMode)node.blendMode;
|
||||
}
|
||||
|
||||
// display?
|
||||
if(node.shouldRender == NO) {
|
||||
layer.hidden = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (IJSVGLayer *)layerForImage:(IJSVGImage *)image
|
||||
{
|
||||
IJSVGImageLayer * layer = [[[IJSVGImageLayer alloc] initWithCGImage:image.CGImage] autorelease];
|
||||
layer.frame = CGRectMake(image.x.value, image.y.value, image.width.value, image.height.value);
|
||||
layer.affineTransform = CGAffineTransformConcat(layer.affineTransform,
|
||||
CGAffineTransformMakeScale( 1.f, -1.f));
|
||||
|
||||
// make sure we set the width and height correctly,
|
||||
// as this may not be exactly the same as the size of the
|
||||
// given image
|
||||
CGRect frame = layer.frame;
|
||||
frame.size.width = image.width.value;
|
||||
frame.size.height = image.height.value;
|
||||
layer.frame = frame;
|
||||
return layer;
|
||||
}
|
||||
|
||||
- (IJSVGLayer *)layerForGroup:(IJSVGGroup *)group
|
||||
{
|
||||
|
||||
// grab the sub layer tree from the SVG
|
||||
if(group.svg != nil) {
|
||||
return [self layerForGroup:group.svg.rootNode];
|
||||
}
|
||||
|
||||
IJSVGGroupLayer * groupLayer = [[[IJSVGGroupLayer alloc] init] autorelease];
|
||||
for(IJSVGNode * node in group.children) {
|
||||
[groupLayer addSublayer:[self layerForNode:node]];
|
||||
}
|
||||
groupLayer.frame = (CGRect){
|
||||
.origin = (CGPoint){
|
||||
.x = group.x.value,
|
||||
.y = group.y.value
|
||||
},
|
||||
.origin = CGPointZero,
|
||||
.size = (CGSize){
|
||||
.width = group.width.value,
|
||||
.height = group.height.value
|
||||
}
|
||||
};
|
||||
|
||||
// mask it - forgot groups can have masks too, doh! simple
|
||||
// enough to apply though, recursion ftw!
|
||||
[self maskLayer:groupLayer
|
||||
fromNode:group];
|
||||
return groupLayer;
|
||||
}
|
||||
|
||||
@@ -151,16 +209,21 @@
|
||||
}
|
||||
|
||||
- (IJSVGShapeLayer *)basicLayerForPath:(IJSVGPath *)path
|
||||
originalBoundingBox:(CGRect *)originalBoundingBox
|
||||
{
|
||||
// setup path and layer
|
||||
IJSVGShapeLayer * layer = [[[IJSVGShapeLayer alloc] init] autorelease];
|
||||
CGPathRef introPath = [path newPathRefByAutoClosingPath:NO];
|
||||
|
||||
CGRect bounds = [self correctedBounds:CGPathGetBoundingBox(introPath)];
|
||||
*originalBoundingBox = CGRectIntegral(CGPathGetBoundingBox(introPath));
|
||||
layer.originalPathOrigin = (*originalBoundingBox).origin;
|
||||
|
||||
CGRect bounds = [self correctedBounds:*originalBoundingBox];
|
||||
|
||||
// zero back the path
|
||||
CGAffineTransform trans = CGAffineTransformMakeTranslation(-bounds.origin.x,
|
||||
-bounds.origin.y);
|
||||
|
||||
CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(introPath, &trans);
|
||||
layer.path = transformedPath;
|
||||
|
||||
@@ -169,7 +232,7 @@
|
||||
CGPathRelease(introPath);
|
||||
|
||||
// set the bounds
|
||||
layer.frame = bounds;
|
||||
layer.frame = CGRectIntegral(bounds);
|
||||
|
||||
// basic fill color and rule
|
||||
layer.fillColor = nil;
|
||||
@@ -178,7 +241,7 @@
|
||||
}
|
||||
|
||||
- (IJSVGShapeLayer *)layerMaskFromLayer:(CAShapeLayer *)layer
|
||||
fromNode:(IJSVGNode *)node
|
||||
fromNode:(IJSVGNode *)node
|
||||
{
|
||||
IJSVGShapeLayer * mask = [[[IJSVGShapeLayer alloc] init] autorelease];
|
||||
mask.fillColor = [NSColor blackColor].CGColor;
|
||||
@@ -186,73 +249,64 @@
|
||||
return mask;
|
||||
}
|
||||
|
||||
- (CGAffineTransform)absoluteTransform:(IJSVGNode *)node
|
||||
{
|
||||
CGAffineTransform parentAbsoluteTransform = CGAffineTransformIdentity;
|
||||
IJSVGNode * intermediateNode = node.intermediateParentNode;
|
||||
node = node.parentNode;
|
||||
while(node != nil) {
|
||||
// intermediateParent should be skipped as these are elements
|
||||
// created by use statements and are technically fake elements, but the
|
||||
// spec says use them for the transforms, yolo!
|
||||
if(node == intermediateNode) {
|
||||
node = node.parentNode;
|
||||
continue;
|
||||
}
|
||||
CGAffineTransform trans = IJSVGConcatTransforms(node.transforms);
|
||||
parentAbsoluteTransform = CGAffineTransformConcat(trans,parentAbsoluteTransform);
|
||||
node = node.parentNode;
|
||||
}
|
||||
return parentAbsoluteTransform;
|
||||
}
|
||||
|
||||
- (IJSVGLayer *)layerForPath:(IJSVGPath *)path
|
||||
{
|
||||
// is there a sub SVG?
|
||||
if(path.svg != nil) {
|
||||
// grab the sub layer tree from the SVG
|
||||
return [path.svg layerWithTree:self];
|
||||
}
|
||||
// grab the basic shape layer
|
||||
CGRect originalShapeBounds;
|
||||
IJSVGShapeLayer * layer = [self basicLayerForPath:path
|
||||
originalBoundingBox:&originalShapeBounds];
|
||||
|
||||
// garb the basic shape layer
|
||||
IJSVGShapeLayer * layer = [self basicLayerForPath:path];
|
||||
CGRect pathBoundingBox = CGPathGetBoundingBox(layer.path);
|
||||
BOOL hasStroke = (path.strokeColor != nil ||
|
||||
path.strokePattern != nil ||
|
||||
path.strokeGradient != nil);
|
||||
|
||||
// any gradient?
|
||||
if(self.fillColor == nil && path.fillGradient != nil) {
|
||||
|
||||
// add the mask
|
||||
IJSVGShapeLayer * mask = [self layerMaskFromLayer:layer
|
||||
fromNode:path];
|
||||
// create the gradient
|
||||
IJSVGGradientLayer * gradLayer = [self gradientLayerForLayer:layer
|
||||
gradient:path.fillGradient
|
||||
fromNode:path
|
||||
objectRect:originalShapeBounds
|
||||
shouldMask:YES];
|
||||
|
||||
// the gradient drawing layer
|
||||
IJSVGGradientLayer * gradLayer = [[[IJSVGGradientLayer alloc] init] autorelease];
|
||||
gradLayer.frame = pathBoundingBox;
|
||||
gradLayer.gradient = path.fillGradient;
|
||||
gradLayer.mask = mask;
|
||||
|
||||
// is there a fill opacity?
|
||||
if(path.fillOpacity.value != 0.f) {
|
||||
gradLayer.opacity = path.fillOpacity.value;
|
||||
}
|
||||
|
||||
// display it
|
||||
[gradLayer setNeedsDisplay];
|
||||
|
||||
// add the gradient
|
||||
// add the gradient and set it against the layer
|
||||
[layer addSublayer:gradLayer];
|
||||
|
||||
// assign it
|
||||
layer.gradientFillLayer = gradLayer;
|
||||
|
||||
if(path.fillGradient.units == IJSVGUnitUserSpaceOnUse) {
|
||||
// move back if needed
|
||||
gradLayer.frame = (CGRect){
|
||||
.size = gradLayer.frame.size,
|
||||
.origin = CGPointMake(-(gradLayer.frame.origin.x),
|
||||
-(gradLayer.frame.origin.y))
|
||||
};
|
||||
}
|
||||
|
||||
} else if(self.fillColor == nil && path.fillPattern != nil) {
|
||||
|
||||
// create the pattern, this is actually not as easy as it may seem
|
||||
IJSVGPatternLayer * patternLayer = [[[IJSVGPatternLayer alloc] init] autorelease];
|
||||
patternLayer.patternNode = path.fillPattern;
|
||||
patternLayer.pattern = [self layerForNode:path.fillPattern];
|
||||
patternLayer.frame = pathBoundingBox;
|
||||
|
||||
// add the mask
|
||||
patternLayer.mask = [self layerMaskFromLayer:layer
|
||||
fromNode:path];
|
||||
|
||||
// display it
|
||||
[patternLayer setNeedsDisplay];
|
||||
|
||||
IJSVGPatternLayer * patternLayer = [self patternLayerForLayer:layer
|
||||
pattern:path.fillPattern
|
||||
fromNode:path];
|
||||
// add it
|
||||
[layer addSublayer:patternLayer];
|
||||
|
||||
// assign it
|
||||
// apply offsets
|
||||
[self applyOffsetsToLayer:patternLayer
|
||||
fromNode:path.fillPattern];
|
||||
|
||||
layer.patternFillLayer = patternLayer;
|
||||
|
||||
} else {
|
||||
@@ -264,61 +318,334 @@
|
||||
BOOL hasFill = path.fillPattern != nil || path.fillGradient != nil;
|
||||
if(self.fillColor && (hasFill || hasColor || fColor == nil)) {
|
||||
fColor = self.fillColor;
|
||||
} else if(fColor != nil && path.fillOpacity.value != 1.f) {
|
||||
fColor = [IJSVGColor changeAlphaOnColor:fColor
|
||||
to:path.fillOpacity.value];
|
||||
}
|
||||
|
||||
// anything changed by user?
|
||||
fColor = [self proposedColorForColor:fColor];
|
||||
|
||||
// just set the color
|
||||
layer.fillColor = fColor.CGColor ?: DEFAULT_SHAPE_FILL_COLOR;
|
||||
if(fColor != nil) {
|
||||
layer.fillColor = fColor.CGColor;
|
||||
} else {
|
||||
// use default color
|
||||
NSColor * defColor = [NSColor blackColor];
|
||||
if(path.fillOpacity.value != 1.f) {
|
||||
defColor = [IJSVGColor changeAlphaOnColor:defColor
|
||||
to:path.fillOpacity.value];
|
||||
}
|
||||
|
||||
// work out if anything was changed by user
|
||||
NSColor * proposedColor = [self proposedColorForColor:defColor];
|
||||
layer.fillColor = proposedColor.CGColor;
|
||||
}
|
||||
}
|
||||
|
||||
// stroke it
|
||||
if(path.strokeColor != nil) {
|
||||
if(hasStroke == YES) {
|
||||
|
||||
// same as fill, dont use global if the alpha is 0.f
|
||||
NSColor * sColor = path.strokeColor;
|
||||
if(self.strokeColor != nil && (sColor != nil && sColor.alphaComponent != 0.f)) {
|
||||
sColor = self.strokeColor;
|
||||
// load the stroke layer
|
||||
IJSVGStrokeLayer * strokeLayer = [self strokeLayer:layer
|
||||
fromNode:path];
|
||||
|
||||
// reset the node
|
||||
BOOL moveStrokeLayer = NO;
|
||||
if(self.strokeColor == nil && path.strokeGradient != nil) {
|
||||
|
||||
// force reset of the mask colour as we need to use the stroke layer
|
||||
// as the mask for the stroke gradient
|
||||
strokeLayer.strokeColor = [NSColor blackColor].CGColor;
|
||||
|
||||
// create the gradient
|
||||
IJSVGGradientLayer * gradLayer = [self gradientStrokeLayerForLayer:layer
|
||||
gradient:path.strokeGradient
|
||||
fromNode:path
|
||||
objectRect:originalShapeBounds];
|
||||
|
||||
moveStrokeLayer = YES;
|
||||
gradLayer.mask = strokeLayer;
|
||||
gradLayer.opacity = strokeLayer.opacity;
|
||||
|
||||
// add it
|
||||
[layer addSublayer:gradLayer];
|
||||
layer.strokeLayer = strokeLayer;
|
||||
layer.gradientStrokeLayer = gradLayer;
|
||||
|
||||
} else if(self.strokeColor == nil && path.strokePattern != nil) {
|
||||
|
||||
// force reset of the mask
|
||||
strokeLayer.strokeColor = [NSColor blackColor].CGColor;
|
||||
|
||||
// create the pattern
|
||||
IJSVGPatternLayer * patternLayer = [self patternStrokeLayerForLayer:layer
|
||||
pattern:path.strokePattern
|
||||
fromNode:path];
|
||||
|
||||
// set the mask for it
|
||||
moveStrokeLayer = YES;
|
||||
patternLayer.mask = strokeLayer;
|
||||
patternLayer.opacity = strokeLayer.opacity;
|
||||
|
||||
// add it
|
||||
[layer addSublayer:patternLayer];
|
||||
layer.strokeLayer = strokeLayer;
|
||||
layer.patternStrokeLayer = (IJSVGPatternLayer *)patternLayer;
|
||||
|
||||
} else {
|
||||
// just add the coloured layer
|
||||
[layer addSublayer:strokeLayer];
|
||||
layer.strokeLayer = strokeLayer;
|
||||
}
|
||||
|
||||
// stroke layer
|
||||
IJSVGStrokeLayer * strokeLayer = [[[IJSVGStrokeLayer alloc] init] autorelease];
|
||||
strokeLayer.path = layer.path;
|
||||
strokeLayer.fillColor = nil;
|
||||
strokeLayer.strokeColor = sColor.CGColor;
|
||||
|
||||
CGFloat lineWidth = 1.f;
|
||||
if(path.strokeWidth.value > 0.f) {
|
||||
lineWidth = path.strokeWidth.value;
|
||||
// if we required to move the stroke layer
|
||||
// then move it in based on half of what the stroke
|
||||
// width is, as strokes are draw on the center
|
||||
if(moveStrokeLayer) {
|
||||
CGFloat layerStrokeWidth = strokeLayer.lineWidth;
|
||||
CGRect rect = strokeLayer.frame;
|
||||
rect.origin.x += (layerStrokeWidth*.5f);
|
||||
rect.origin.y += (layerStrokeWidth*.5f);
|
||||
strokeLayer.frame = rect;
|
||||
}
|
||||
|
||||
// line styles
|
||||
strokeLayer.lineWidth = lineWidth;
|
||||
strokeLayer.lineCap = [self lineCap:path.lineCapStyle];
|
||||
strokeLayer.lineJoin = [self lineJoin:path.lineJoinStyle];
|
||||
strokeLayer.miterLimit = lineWidth;
|
||||
|
||||
CGFloat strokeOpacity = 1.f;
|
||||
if(path.strokeOpacity.value != 0.f) {
|
||||
strokeOpacity = path.strokeOpacity.value;
|
||||
}
|
||||
strokeLayer.opacity = strokeOpacity;
|
||||
|
||||
// dashing
|
||||
strokeLayer.lineDashPhase = path.strokeDashOffset.value;
|
||||
strokeLayer.lineDashPattern = [self lineDashPattern:path];
|
||||
|
||||
// add the stroke layer
|
||||
[layer addSublayer:strokeLayer];
|
||||
|
||||
// add it
|
||||
layer.strokeLayer = strokeLayer;
|
||||
}
|
||||
|
||||
// apply masking
|
||||
[self maskLayer:(IJSVGLayer *)layer
|
||||
fromNode:path];
|
||||
|
||||
return (IJSVGLayer *)layer;
|
||||
}
|
||||
|
||||
- (CGRect)correctBounds:(CGRect)bounds
|
||||
forStrokedPath:(IJSVGNode *)path
|
||||
{
|
||||
// minus half the stroke width from x and y
|
||||
// plus the stroke width to width and height
|
||||
CGFloat val = path.strokeWidth.value;
|
||||
bounds.origin.x -= (val*.5f);
|
||||
bounds.origin.y -= (val*.5f);
|
||||
bounds.size.width += val;
|
||||
bounds.size.height += val;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
- (NSColor *)proposedColorForColor:(NSColor *)color
|
||||
{
|
||||
// nothing found, just return color
|
||||
if(replacementColors == nil || replacementColors.count == 0) {
|
||||
return color;
|
||||
}
|
||||
|
||||
// check the mappings
|
||||
NSColor * found = nil;
|
||||
color = [IJSVGColor computeColorSpace:color];
|
||||
if((found = replacementColors[color]) != nil) {
|
||||
return found;
|
||||
}
|
||||
|
||||
// nothing :(
|
||||
return color;
|
||||
}
|
||||
|
||||
- (IJSVGGradientLayer *)gradientStrokeLayerForLayer:(IJSVGShapeLayer *)layer
|
||||
gradient:(IJSVGGradient *)gradient
|
||||
fromNode:(IJSVGNode *)path
|
||||
objectRect:(CGRect)objectRect
|
||||
{
|
||||
// the gradient drawing layer
|
||||
IJSVGGradientLayer * gradLayer = [self gradientLayerForLayer:layer
|
||||
gradient:gradient
|
||||
fromNode:path
|
||||
objectRect:objectRect
|
||||
shouldMask:NO];
|
||||
|
||||
// set the bounds
|
||||
CGRect bounds = CGPathGetBoundingBox(layer.path);
|
||||
bounds = [self correctBounds:bounds forStrokedPath:path];
|
||||
gradLayer.frame = bounds;
|
||||
return gradLayer;
|
||||
}
|
||||
|
||||
|
||||
- (IJSVGGradientLayer *)gradientLayerForLayer:(IJSVGShapeLayer *)layer
|
||||
gradient:(IJSVGGradient *)gradient
|
||||
fromNode:(IJSVGNode *)path
|
||||
objectRect:(CGRect)objectRect
|
||||
shouldMask:(BOOL)shouldMask
|
||||
{
|
||||
// the gradient drawing layer
|
||||
IJSVGGradientLayer * gradLayer = [[[IJSVGGradientLayer alloc] init] autorelease];
|
||||
gradLayer.viewBox = self.viewBox;
|
||||
gradLayer.frame = layer.bounds;
|
||||
gradLayer.gradient = gradient;
|
||||
gradLayer.absoluteTransform = [self absoluteTransform:path];
|
||||
gradLayer.objectRect = CGRectApplyAffineTransform(objectRect, gradLayer.absoluteTransform);
|
||||
|
||||
if(shouldMask == YES) {
|
||||
// add the mask
|
||||
IJSVGShapeLayer * mask = [self layerMaskFromLayer:layer
|
||||
fromNode:path];
|
||||
gradLayer.mask = mask;
|
||||
}
|
||||
|
||||
// is there a fill opacity?
|
||||
if(path.fillOpacity.value != 0.f) {
|
||||
gradLayer.opacity = path.fillOpacity.value;
|
||||
}
|
||||
gradLayer.masksToBounds = YES;
|
||||
return gradLayer;
|
||||
}
|
||||
|
||||
- (IJSVGPatternLayer *)patternStrokeLayerForLayer:(IJSVGShapeLayer *)layer
|
||||
pattern:(IJSVGPattern *)pattern
|
||||
fromNode:(IJSVGNode *)path
|
||||
{
|
||||
// create the pattern, this is actually not as easy as it may seem
|
||||
IJSVGPatternLayer * patternLayer = [[[IJSVGPatternLayer alloc] init] autorelease];
|
||||
patternLayer.patternNode = pattern;
|
||||
patternLayer.pattern = [self layerForNode:pattern];
|
||||
|
||||
// is there a fill opacity?
|
||||
if(path.fillOpacity.value != 0.f) {
|
||||
patternLayer.opacity = path.fillOpacity.value;
|
||||
}
|
||||
|
||||
// set the bounds
|
||||
CGRect bounds = CGPathGetBoundingBox(layer.path);
|
||||
bounds = [self correctBounds:bounds forStrokedPath:path];
|
||||
patternLayer.frame = bounds;
|
||||
patternLayer.masksToBounds = YES;
|
||||
|
||||
// display
|
||||
[patternLayer setNeedsDisplay];
|
||||
return patternLayer;
|
||||
}
|
||||
|
||||
- (IJSVGPatternLayer *)patternLayerForLayer:(IJSVGShapeLayer *)layer
|
||||
pattern:(IJSVGPattern *)pattern
|
||||
fromNode:(IJSVGNode *)path
|
||||
{
|
||||
// create the pattern, this is actually not as easy as it may seem
|
||||
IJSVGPatternLayer * patternLayer = [[[IJSVGPatternLayer alloc] init] autorelease];
|
||||
patternLayer.patternNode = pattern;
|
||||
patternLayer.pattern = [self layerForNode:pattern];
|
||||
patternLayer.frame = CGPathGetBoundingBox(layer.path);
|
||||
|
||||
// is there a fill opacity?
|
||||
if(path.fillOpacity.value != 0.f) {
|
||||
patternLayer.opacity = path.fillOpacity.value;
|
||||
}
|
||||
|
||||
// add the mask
|
||||
patternLayer.mask = [self layerMaskFromLayer:layer
|
||||
fromNode:path];
|
||||
|
||||
// display
|
||||
[patternLayer setNeedsDisplay];
|
||||
patternLayer.masksToBounds = YES;
|
||||
|
||||
return patternLayer;
|
||||
}
|
||||
|
||||
- (void)applyOffsetsToLayer:(IJSVGLayer *)layer
|
||||
fromNode:(IJSVGNode *)node
|
||||
{
|
||||
// make sure it has a superlayer
|
||||
if(layer.superlayer == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
// grab the x and y
|
||||
IJSVGUnitLength * x = nil;
|
||||
IJSVGUnitLength * y = nil;
|
||||
|
||||
// sort out the rect
|
||||
CGRect rect = layer.superlayer.frame;
|
||||
CGRect frame = layer.frame;
|
||||
|
||||
// x
|
||||
if((x = node.x) != nil) {
|
||||
frame.origin.x = [x computeValue:rect.size.width];
|
||||
}
|
||||
|
||||
// y
|
||||
if((y = node.y) != nil) {
|
||||
frame.origin.y = [y computeValue:rect.size.height];
|
||||
}
|
||||
|
||||
// update the frame
|
||||
if(CGRectEqualToRect(frame, layer.frame) == NO) {
|
||||
layer.frame = frame;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (IJSVGStrokeLayer *)strokeLayer:(IJSVGShapeLayer *)layer
|
||||
fromNode:(IJSVGPath *)path
|
||||
{
|
||||
// same as fill, dont use global if the alpha is 0.f, but do use it
|
||||
// if there is a pattern or gradient
|
||||
NSColor * sColor = path.strokeColor;
|
||||
if(self.strokeColor != nil &&
|
||||
((sColor != nil && sColor.alphaComponent != 0.f) ||
|
||||
path.strokePattern != nil || path.strokeGradient != nil )) {
|
||||
sColor = self.strokeColor;
|
||||
}
|
||||
|
||||
sColor = [self proposedColorForColor:sColor];
|
||||
|
||||
// stroke layer
|
||||
IJSVGStrokeLayer * strokeLayer = [[[IJSVGStrokeLayer alloc] init] autorelease];
|
||||
strokeLayer.path = layer.path;
|
||||
strokeLayer.fillColor = nil;
|
||||
strokeLayer.strokeColor = sColor.CGColor;
|
||||
|
||||
CGFloat lineWidth = 1.f;
|
||||
if(self.strokeWidth > 0.f) {
|
||||
lineWidth = self.strokeWidth;
|
||||
} else if(path.strokeWidth.value > 0.f) {
|
||||
lineWidth = path.strokeWidth.value;
|
||||
}
|
||||
|
||||
// work out line styles
|
||||
IJSVGLineCapStyle lCapStyle;
|
||||
IJSVGLineJoinStyle lJoinStyle;
|
||||
|
||||
// forced cap style
|
||||
if(self.lineCapStyle != IJSVGLineCapStyleNone) {
|
||||
lCapStyle = self.lineCapStyle;
|
||||
} else {
|
||||
lCapStyle = path.lineCapStyle;
|
||||
}
|
||||
|
||||
// forced join style
|
||||
if(self.lineJoinStyle != IJSVGLineJoinStyleNone) {
|
||||
lJoinStyle = self.lineJoinStyle;
|
||||
} else {
|
||||
lJoinStyle = path.lineJoinStyle;
|
||||
}
|
||||
|
||||
// line styles
|
||||
strokeLayer.lineWidth = lineWidth;
|
||||
strokeLayer.lineCap = [self lineCap:lCapStyle];
|
||||
strokeLayer.lineJoin = [self lineJoin:lJoinStyle];
|
||||
|
||||
CGFloat strokeOpacity = 1.f;
|
||||
if(path.strokeOpacity.value != 0.f) {
|
||||
strokeOpacity = path.strokeOpacity.value;
|
||||
}
|
||||
strokeLayer.opacity = strokeOpacity;
|
||||
|
||||
// dashing
|
||||
strokeLayer.lineDashPhase = path.strokeDashOffset.value;
|
||||
strokeLayer.lineDashPattern = [self lineDashPattern:path];
|
||||
|
||||
return strokeLayer;
|
||||
}
|
||||
|
||||
- (void)maskLayer:(IJSVGLayer *)layer
|
||||
fromNode:(IJSVGNode *)node
|
||||
{
|
||||
@@ -448,4 +775,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)log:(IJSVGLayer *)layer
|
||||
depth:(NSInteger)depth {
|
||||
NSLog(@"%@%@: %@, Transforms: %@",[@"" stringByPaddingToLength:depth
|
||||
withString:@"\t"
|
||||
startingAtIndex:0],layer,
|
||||
NSStringFromRect(layer.frame),
|
||||
[IJSVGTransform affineTransformToSVGTransformAttributeString:layer.affineTransform]);
|
||||
for(IJSVGLayer * sublayer in layer.sublayers) {
|
||||
[self log:(IJSVGLayer *)sublayer
|
||||
depth:depth++];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
@interface IJSVGLinearGradient : IJSVGGradient
|
||||
|
||||
+ (NSGradient *)parseGradient:(NSXMLElement *)element
|
||||
gradient:(IJSVGLinearGradient *)aGradient
|
||||
startPoint:(CGPoint *)startPoint
|
||||
endPoint:(CGPoint *)endPoint;
|
||||
gradient:(IJSVGLinearGradient *)aGradient;
|
||||
|
||||
@end
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
|
||||
+ (NSGradient *)parseGradient:(NSXMLElement *)element
|
||||
gradient:(IJSVGLinearGradient *)aGradient
|
||||
startPoint:(CGPoint *)startPoint
|
||||
endPoint:(CGPoint *)endPoint
|
||||
{
|
||||
|
||||
CGFloat px1 = [[element attributeForName:@"x1"] stringValue].floatValue;
|
||||
@@ -25,26 +23,24 @@
|
||||
// work out each coord, and work out if its a % or not
|
||||
// annoyingly we need to check them all against each other -_-
|
||||
BOOL isPercent = NO;
|
||||
if(px1 <= 1.f && px2 <= 1.f && py1 <= 1.f && py2 <= 1.f) {
|
||||
isPercent = YES;
|
||||
} else if((px1 >= 0.f && px1 <= 1.f) && (px2 >= 0.f && px2 <= 1.f) &&
|
||||
(py1 >= 0.f && py1 <= 1.f) && (py2 >= 0.f && py2 <= 1.f)) {
|
||||
if((px1 >= 0.f && px1 <= 1.f) && (px2 >= 0.f && px2 <= 1.f) &&
|
||||
(py1 >= 0.f && py1 <= 1.f) && (py2 >= 0.f && py2 <= 1.f)) {
|
||||
isPercent = YES;
|
||||
}
|
||||
|
||||
// assume its a vertical / horizonal
|
||||
if(isPercent == NO) {
|
||||
// just ask unit for the value
|
||||
aGradient.x1 = [IJSVGUnitLength unitWithString:[[element attributeForName:@"x1"] stringValue] ?: @"0"];
|
||||
aGradient.x2 = [IJSVGUnitLength unitWithString:[[element attributeForName:@"x2"] stringValue] ?: @"100"];
|
||||
aGradient.y1 = [IJSVGUnitLength unitWithString:[[element attributeForName:@"y1"] stringValue] ?: @"0"];
|
||||
aGradient.y2 = [IJSVGUnitLength unitWithString:[[element attributeForName:@"y2"] stringValue] ?: @"0"];
|
||||
aGradient.x1 = [IJSVGGradientUnitLength unitWithString:[[element attributeForName:@"x1"] stringValue] ?: @"0"];
|
||||
aGradient.x2 = [IJSVGGradientUnitLength unitWithString:[[element attributeForName:@"x2"] stringValue] ?: @"100"];
|
||||
aGradient.y1 = [IJSVGGradientUnitLength unitWithString:[[element attributeForName:@"y1"] stringValue] ?: @"0"];
|
||||
aGradient.y2 = [IJSVGGradientUnitLength unitWithString:[[element attributeForName:@"y2"] stringValue] ?: @"0"];
|
||||
} else {
|
||||
// make sure its a percent!
|
||||
aGradient.x1 = [IJSVGUnitLength unitWithPercentageString:[[element attributeForName:@"x1"] stringValue] ?: @"0"];
|
||||
aGradient.x2 = [IJSVGUnitLength unitWithPercentageString:[[element attributeForName:@"x2"] stringValue] ?: @"100"];
|
||||
aGradient.y1 = [IJSVGUnitLength unitWithPercentageString:[[element attributeForName:@"y1"] stringValue] ?: @"0"];
|
||||
aGradient.y2 = [IJSVGUnitLength unitWithPercentageString:[[element attributeForName:@"y2"] stringValue] ?: @"0"];
|
||||
aGradient.x1 = [IJSVGGradientUnitLength unitWithPercentageString:[[element attributeForName:@"x1"] stringValue] ?: @"0"];
|
||||
aGradient.x2 = [IJSVGGradientUnitLength unitWithPercentageString:[[element attributeForName:@"x2"] stringValue] ?: @"1"];
|
||||
aGradient.y1 = [IJSVGGradientUnitLength unitWithPercentageString:[[element attributeForName:@"y1"] stringValue] ?: @"0"];
|
||||
aGradient.y2 = [IJSVGGradientUnitLength unitWithPercentageString:[[element attributeForName:@"y2"] stringValue] ?: @"0"];
|
||||
}
|
||||
|
||||
// compute the color stops and colours
|
||||
@@ -53,41 +49,58 @@
|
||||
colors:&colors];
|
||||
|
||||
// create the gradient with the colours
|
||||
NSGradient * grad = [[[NSGradient alloc] initWithColors:colors
|
||||
NSGradient * grad = [[NSGradient alloc] initWithColors:colors
|
||||
atLocations:stopsParams
|
||||
colorSpace:[NSColorSpace genericRGBColorSpace]] autorelease];
|
||||
colorSpace:IJSVGColor.defaultColorSpace];
|
||||
|
||||
free(stopsParams);
|
||||
return grad;
|
||||
return [grad autorelease];
|
||||
}
|
||||
|
||||
- (void)drawInContextRef:(CGContextRef)ctx
|
||||
rect:(NSRect)rect
|
||||
objectRect:(NSRect)objectRect
|
||||
absoluteTransform:(CGAffineTransform)absoluteTransform
|
||||
viewPort:(CGRect)viewBox
|
||||
{
|
||||
// grab the start and end point
|
||||
CGPoint aStartPoint = (CGPoint){
|
||||
.x = [self.x1 computeValue:rect.size.width],
|
||||
.y = [self.y1 computeValue:rect.size.height]
|
||||
};
|
||||
BOOL inUserSpace = self.units == IJSVGUnitUserSpaceOnUse;
|
||||
|
||||
CGPoint aEndPoint = (CGPoint){
|
||||
.x = [self.x2 computeValue:rect.size.width],
|
||||
.y = [self.y2 computeValue:rect.size.height]
|
||||
};
|
||||
CGPoint gradientStartPoint = CGPointZero;
|
||||
CGPoint gradientEndPoint = CGPointZero;
|
||||
CGAffineTransform absTransform = absoluteTransform;
|
||||
CGAffineTransform selfTransform = IJSVGConcatTransforms(self.transforms);
|
||||
|
||||
// convert the nsgradient to a CGGradient
|
||||
CGGradientRef gRef = [self CGGradient];
|
||||
|
||||
// apply transform for each point
|
||||
for( IJSVGTransform * transform in self.transforms ) {
|
||||
CGAffineTransform trans = transform.CGAffineTransform;
|
||||
aStartPoint = CGPointApplyAffineTransform(aStartPoint, trans);
|
||||
aEndPoint = CGPointApplyAffineTransform(aEndPoint, trans);
|
||||
#pragma mark User Space On Use
|
||||
if(inUserSpace == YES) {
|
||||
CGFloat width = CGRectGetWidth(viewBox);
|
||||
CGFloat height = CGRectGetHeight(viewBox);
|
||||
gradientStartPoint = CGPointMake([self.x1 computeValue:width],
|
||||
[self.y1 computeValue:height]);
|
||||
|
||||
gradientEndPoint = CGPointMake([self.x2 computeValue:width],
|
||||
[self.y2 computeValue:height]);
|
||||
|
||||
// transform absolute - due to user space
|
||||
CGContextConcatCTM(ctx, absTransform);
|
||||
} else {
|
||||
#pragma mark Object Bounding Box
|
||||
CGFloat width = CGRectGetWidth(objectRect);
|
||||
CGFloat height = CGRectGetHeight(objectRect);
|
||||
gradientStartPoint = CGPointMake([self.x1 computeValue:width],
|
||||
[self.y1 computeValue:height]);
|
||||
|
||||
gradientEndPoint = CGPointMake([self.x2 computeValue:width],
|
||||
[self.y2 computeValue:height]);
|
||||
}
|
||||
|
||||
// transform the context
|
||||
CGContextConcatCTM(ctx, selfTransform);
|
||||
|
||||
// draw the gradient
|
||||
CGGradientDrawingOptions opt = kCGGradientDrawsBeforeStartLocation|kCGGradientDrawsAfterEndLocation;
|
||||
CGContextDrawLinearGradient(ctx, gRef, aStartPoint, aEndPoint, opt);
|
||||
CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation|
|
||||
kCGGradientDrawsAfterEndLocation;
|
||||
|
||||
CGContextDrawLinearGradient(ctx, self.CGGradient, gradientStartPoint,
|
||||
gradientEndPoint, options);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
@class IJSVGGradient;
|
||||
@class IJSVGGroup;
|
||||
@class IJSVGPattern;
|
||||
@class IJSVGTransform;
|
||||
|
||||
typedef NS_ENUM( NSInteger, IJSVGNodeType ) {
|
||||
IJSVGNodeTypeGroup,
|
||||
@@ -39,6 +40,8 @@ typedef NS_ENUM( NSInteger, IJSVGNodeType ) {
|
||||
IJSVGNodeTypeSVG,
|
||||
IJSVGNodeTypeText,
|
||||
IJSVGNodeTypeTextSpan,
|
||||
IJSVGNodeTypeStyle,
|
||||
IJSVGNodeTypeSwitch,
|
||||
IJSVGNodeTypeNotFound,
|
||||
};
|
||||
|
||||
@@ -49,6 +52,7 @@ typedef NS_ENUM( NSInteger, IJSVGWindingRule ) {
|
||||
};
|
||||
|
||||
typedef NS_ENUM( NSInteger, IJSVGLineCapStyle ) {
|
||||
IJSVGLineCapStyleNone,
|
||||
IJSVGLineCapStyleButt,
|
||||
IJSVGLineCapStyleRound,
|
||||
IJSVGLineCapStyleSquare,
|
||||
@@ -56,6 +60,7 @@ typedef NS_ENUM( NSInteger, IJSVGLineCapStyle ) {
|
||||
};
|
||||
|
||||
typedef NS_ENUM( NSInteger, IJSVGLineJoinStyle ) {
|
||||
IJSVGLineJoinStyleNone,
|
||||
IJSVGLineJoinStyleMiter,
|
||||
IJSVGLineJoinStyleRound,
|
||||
IJSVGLineJoinStyleBevel,
|
||||
@@ -74,6 +79,25 @@ typedef NS_ENUM( NSInteger, IJSVGUnitType) {
|
||||
IJSVGUnitInherit
|
||||
};
|
||||
|
||||
typedef NS_ENUM( NSInteger, IJSVGBlendMode) {
|
||||
IJSVGBlendModeNormal = kCGBlendModeNormal,
|
||||
IJSVGBlendModeMultiply = kCGBlendModeMultiply,
|
||||
IJSVGBlendModeScreen = kCGBlendModeScreen,
|
||||
IJSVGBlendModeOverlay = kCGBlendModeOverlay,
|
||||
IJSVGBlendModeDarken = kCGBlendModeDarken,
|
||||
IJSVGBlendModeLighten = kCGBlendModeLighten,
|
||||
IJSVGBlendModeColorDodge = kCGBlendModeColorDodge,
|
||||
IJSVGBlendModeColorBurn = kCGBlendModeColorBurn,
|
||||
IJSVGBlendModeHardLight = kCGBlendModeHardLight,
|
||||
IJSVGBlendModeSoftLight = kCGBlendModeSoftLight,
|
||||
IJSVGBlendModeDifference = kCGBlendModeDifference,
|
||||
IJSVGBlendModeExclusion = kCGBlendModeExclusion,
|
||||
IJSVGBlendModeHue = kCGBlendModeHue,
|
||||
IJSVGBlendModeSaturation = kCGBlendModeSaturation,
|
||||
IJSVGBlendModeColor = kCGBlendModeColor,
|
||||
IJSVGBlendModeLuminosity = kCGBlendModeLuminosity
|
||||
};
|
||||
|
||||
static CGFloat IJSVGInheritedFloatValue = -99.9999991;
|
||||
|
||||
@interface IJSVGNode : NSObject <NSCopying>
|
||||
@@ -97,21 +121,25 @@ static CGFloat IJSVGInheritedFloatValue = -99.9999991;
|
||||
@property ( nonatomic, retain ) NSColor * strokeColor;
|
||||
@property ( nonatomic, copy ) NSString * identifier;
|
||||
@property ( nonatomic, assign ) IJSVGNode * parentNode;
|
||||
@property ( nonatomic, assign ) IJSVGNode * intermediateParentNode;
|
||||
@property ( nonatomic, retain ) IJSVGGroup * clipPath;
|
||||
@property ( nonatomic, retain ) IJSVGGroup * mask;
|
||||
@property ( nonatomic, assign ) IJSVGWindingRule windingRule;
|
||||
@property ( nonatomic, assign ) IJSVGLineCapStyle lineCapStyle;
|
||||
@property ( nonatomic, assign ) IJSVGLineJoinStyle lineJoinStyle;
|
||||
@property ( nonatomic, retain ) NSArray * transforms;
|
||||
@property ( nonatomic, retain ) NSArray<IJSVGTransform *> * transforms;
|
||||
@property ( nonatomic, retain ) IJSVGDef * def;
|
||||
@property ( nonatomic, retain ) IJSVGGradient * fillGradient;
|
||||
@property ( nonatomic, retain ) IJSVGPattern * fillPattern;
|
||||
@property ( nonatomic, retain ) IJSVGGradient * strokeGradient;
|
||||
@property ( nonatomic, retain ) IJSVGPattern * strokePattern;
|
||||
@property ( nonatomic, assign ) CGFloat * strokeDashArray;
|
||||
@property ( nonatomic, assign ) NSInteger strokeDashArrayCount;
|
||||
@property ( nonatomic, retain ) IJSVGUnitLength * strokeDashOffset;
|
||||
@property ( nonatomic, retain ) IJSVG * svg;
|
||||
@property ( nonatomic, assign ) IJSVGUnitType contentUnits;
|
||||
@property ( nonatomic, assign ) IJSVGUnitType units;
|
||||
@property ( nonatomic, assign ) IJSVGBlendMode blendMode;
|
||||
|
||||
+ (IJSVGNodeType)typeForString:(NSString *)string
|
||||
kind:(NSXMLNodeKind)kind;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#import "IJSVGNode.h"
|
||||
#import "IJSVGDef.h"
|
||||
#import "IJSVGUtils.h"
|
||||
|
||||
@implementation IJSVGNode
|
||||
|
||||
@@ -29,11 +30,14 @@
|
||||
@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;
|
||||
@@ -45,6 +49,7 @@
|
||||
@synthesize mask;
|
||||
@synthesize units;
|
||||
@synthesize contentUnits;
|
||||
@synthesize blendMode;
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
@@ -60,6 +65,8 @@
|
||||
[strokeDashOffset release], strokeDashOffset = nil;
|
||||
[unicode release], unicode = nil;
|
||||
[fillGradient release], fillGradient = nil;
|
||||
[strokeGradient release], strokeGradient = nil;
|
||||
[strokePattern release], strokePattern = nil;
|
||||
[transforms release], transforms = nil;
|
||||
[fillColor release], fillColor = nil;
|
||||
[strokeColor release], strokeColor = nil;
|
||||
@@ -79,6 +86,10 @@
|
||||
kind:(NSXMLNodeKind)kind
|
||||
{
|
||||
string = [string lowercaseString];
|
||||
if([string isEqualToString:@"style"])
|
||||
return IJSVGNodeTypeStyle;
|
||||
if([string isEqualToString:@"switch"])
|
||||
return IJSVGNodeTypeSwitch;
|
||||
if( [string isEqualToString:@"defs"] )
|
||||
return IJSVGNodeTypeDef;
|
||||
if( [string isEqualToString:@"g"] )
|
||||
@@ -122,6 +133,12 @@
|
||||
if([string isEqualToString:@"tspan"] || kind == NSXMLTextKind) {
|
||||
return IJSVGNodeTypeTextSpan;
|
||||
}
|
||||
|
||||
// are we commong HTML? - if so just treat as a group
|
||||
if(IJSVGIsCommonHTMLElementName(string) == YES) {
|
||||
return IJSVGNodeTypeGroup;
|
||||
}
|
||||
|
||||
return IJSVGNodeTypeNotFound;
|
||||
}
|
||||
|
||||
@@ -129,6 +146,7 @@
|
||||
{
|
||||
if( ( self = [self initWithDef:YES] ) != nil )
|
||||
{
|
||||
self.opacity = [IJSVGUnitLength unitWithFloat:1];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -148,6 +166,8 @@
|
||||
|
||||
self.fillGradient = node.fillGradient;
|
||||
self.fillPattern = node.fillPattern;
|
||||
self.strokeGradient = node.strokeGradient;
|
||||
self.strokePattern = node.strokePattern;
|
||||
|
||||
self.fillColor = node.fillColor;
|
||||
self.strokeColor = node.strokeColor;
|
||||
@@ -172,6 +192,7 @@
|
||||
self.parentNode = node.parentNode;
|
||||
|
||||
self.shouldRender = node.shouldRender;
|
||||
self.blendMode = node.blendMode;
|
||||
|
||||
// dash array needs physical memory copied
|
||||
CGFloat * nStrokeDashArray = (CGFloat *)malloc(node.strokeDashArrayCount*sizeof(CGFloat));
|
||||
@@ -194,15 +215,24 @@
|
||||
{
|
||||
self.opacity = [IJSVGUnitLength unitWithFloat:0.f];
|
||||
self.fillOpacity = [IJSVGUnitLength unitWithFloat:1.f];
|
||||
self.strokeOpacity = [IJSVGUnitLength unitWithFloat:1.f];
|
||||
self.fillOpacity.inherit = YES;
|
||||
|
||||
self.strokeDashOffset = [IJSVGUnitLength unitWithFloat:0.f];
|
||||
self.shouldRender = YES;
|
||||
|
||||
self.strokeOpacity = [IJSVGUnitLength unitWithFloat:1.f];
|
||||
self.strokeOpacity.inherit = YES;
|
||||
|
||||
self.strokeWidth = [IJSVGUnitLength unitWithFloat:0.f];
|
||||
self.strokeWidth.inherit = YES;
|
||||
|
||||
self.windingRule = IJSVGWindingRuleInherit;
|
||||
self.lineCapStyle = IJSVGLineCapStyleInherit;
|
||||
self.lineJoinStyle = IJSVGLineJoinStyleInherit;
|
||||
self.units = IJSVGUnitInherit;
|
||||
|
||||
self.blendMode = IJSVGBlendModeNormal;
|
||||
|
||||
if( flag ) {
|
||||
def = [[IJSVGDef alloc] init];
|
||||
}
|
||||
@@ -213,10 +243,12 @@
|
||||
- (IJSVGDef *)defForID:(NSString *)anID
|
||||
{
|
||||
IJSVGDef * aDef = nil;
|
||||
if( (aDef = [def defForID:anID]) != nil )
|
||||
if( (aDef = [def defForID:anID]) != nil ) {
|
||||
return aDef;
|
||||
if( parentNode != nil )
|
||||
}
|
||||
if( parentNode != nil ) {
|
||||
return [parentNode defForID:anID];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -228,7 +260,7 @@
|
||||
// winding rule can inherit..
|
||||
- (IJSVGWindingRule)windingRule
|
||||
{
|
||||
if( windingRule == IJSVGWindingRuleInherit && parentNode != nil ) {
|
||||
if(windingRule == IJSVGWindingRuleInherit && parentNode != nil) {
|
||||
return parentNode.windingRule;
|
||||
}
|
||||
return windingRule;
|
||||
@@ -236,20 +268,20 @@
|
||||
|
||||
- (IJSVGLineCapStyle)lineCapStyle
|
||||
{
|
||||
if( lineCapStyle == IJSVGLineCapStyleInherit )
|
||||
{
|
||||
if( parentNode != nil )
|
||||
if( lineCapStyle == IJSVGLineCapStyleInherit ) {
|
||||
if( parentNode != nil ) {
|
||||
return parentNode.lineCapStyle;
|
||||
}
|
||||
}
|
||||
return lineCapStyle;
|
||||
}
|
||||
|
||||
- (IJSVGLineJoinStyle)lineJoinStyle
|
||||
{
|
||||
if( lineJoinStyle == IJSVGLineJoinStyleInherit )
|
||||
{
|
||||
if( parentNode != nil )
|
||||
if( lineJoinStyle == IJSVGLineJoinStyleInherit ) {
|
||||
if( parentNode != nil ) {
|
||||
return parentNode.lineJoinStyle;
|
||||
}
|
||||
}
|
||||
return lineJoinStyle;
|
||||
}
|
||||
@@ -307,8 +339,9 @@
|
||||
// must be on the path, it can also be on the
|
||||
- (NSColor *)fillColor
|
||||
{
|
||||
if( fillColor == nil && parentNode != nil )
|
||||
if( fillColor == nil && parentNode != nil ) {
|
||||
return parentNode.fillColor;
|
||||
}
|
||||
return fillColor;
|
||||
}
|
||||
|
||||
@@ -332,4 +365,24 @@
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
return strokePattern;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -49,6 +49,7 @@ 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";
|
||||
@@ -92,7 +93,9 @@ handleForeignObject:(IJSVGForeignObject *)foreignObject
|
||||
IJSVGStyleSheet * _styleSheet;
|
||||
NSMutableArray * _parsedNodes;
|
||||
NSMutableDictionary * _defNodes;
|
||||
NSMutableDictionary * _baseDefNodes;
|
||||
NSMutableArray<IJSVG *> * _svgs;
|
||||
NSMutableArray * _definedGroups;
|
||||
|
||||
struct {
|
||||
unsigned int shouldHandleForeignObject: 1;
|
||||
@@ -104,6 +107,8 @@ handleForeignObject:(IJSVGForeignObject *)foreignObject
|
||||
@property ( nonatomic, readonly ) NSRect viewBox;
|
||||
@property ( nonatomic, readonly ) NSSize proposedViewSize;
|
||||
|
||||
+ (BOOL)isDataSVG:(NSData *)data;
|
||||
|
||||
- (id)initWithSVGString:(NSString *)string
|
||||
error:(NSError **)error
|
||||
delegate:(id<IJSVGParserDelegate>)delegate;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
{
|
||||
if((self = [super init]) != nil) {
|
||||
self.requiresBackingScaleHelp = YES;
|
||||
self.shouldRasterize = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
@property ( nonatomic, retain ) IJSVGUnitLength * radius;
|
||||
|
||||
+ (NSGradient *)parseGradient:(NSXMLElement *)element
|
||||
gradient:(IJSVGRadialGradient *)gradient
|
||||
startPoint:(CGPoint *)startPoint
|
||||
endPoint:(CGPoint *)endPoint;
|
||||
gradient:(IJSVGRadialGradient *)gradient;
|
||||
|
||||
@end
|
||||
|
||||
@@ -34,123 +34,134 @@
|
||||
grad.cx = self.cx;
|
||||
grad.cy = self.cy;
|
||||
grad.radius = self.radius;
|
||||
grad.startPoint = self.startPoint;
|
||||
grad.endPoint = self.endPoint;
|
||||
return grad;
|
||||
}
|
||||
|
||||
|
||||
+ (NSGradient *)parseGradient:(NSXMLElement *)element
|
||||
gradient:(IJSVGRadialGradient *)gradient
|
||||
startPoint:(CGPoint *)startPoint
|
||||
endPoint:(CGPoint *)endPoint
|
||||
{
|
||||
// cx defaults to 50% if not specified
|
||||
NSDictionary * kv = @{@"cx":@"cx",
|
||||
@"cy":@"cy",
|
||||
@"r":@"radius",
|
||||
@"fx":@"fx"};
|
||||
@"r":@"radius"};
|
||||
|
||||
for(NSString * key in kv.allKeys) {
|
||||
NSString * str = [element attributeForName:key].stringValue;
|
||||
IJSVGUnitLength * unit = nil;
|
||||
if(str != nil) {
|
||||
unit = [IJSVGUnitLength unitWithPercentageString:str];
|
||||
unit = [IJSVGUnitLength unitWithString:str];
|
||||
} else {
|
||||
unit = [IJSVGUnitLength unitWithPercentageFloat:50];
|
||||
unit = [IJSVGUnitLength unitWithPercentageFloat:.5f];
|
||||
}
|
||||
[gradient setValue:unit
|
||||
forKey:kv[key]];
|
||||
}
|
||||
|
||||
// fy defaults to cy if not specified
|
||||
NSString * fy = [element attributeForName:@"fy"].stringValue;
|
||||
if(fy != nil) {
|
||||
gradient.fy = [IJSVGUnitLength unitWithPercentageString:fy];
|
||||
} else {
|
||||
gradient.fy = gradient.cy;
|
||||
gradient.fx = gradient.cx;
|
||||
gradient.fy = gradient.cy;
|
||||
|
||||
// needs fixing
|
||||
NSString * fx = [element attributeForName:@"fx"].stringValue;
|
||||
if(fx != nil) {
|
||||
if(fx.floatValue < 1.f) {
|
||||
gradient.fx = [IJSVGUnitLength unitWithPercentageString:fx];
|
||||
} else {
|
||||
gradient.fx = [IJSVGUnitLength unitWithString:fx];
|
||||
}
|
||||
}
|
||||
|
||||
if( gradient.gradient != nil )
|
||||
NSString * fy = [element attributeForName:@"fy"].stringValue;
|
||||
if(fx != nil) {
|
||||
if(fx.floatValue < 1.f) {
|
||||
gradient.fy = [IJSVGUnitLength unitWithPercentageString:fy];
|
||||
} else {
|
||||
gradient.fy = [IJSVGUnitLength unitWithString:fy];
|
||||
}
|
||||
}
|
||||
|
||||
if( gradient.gradient != nil ) {
|
||||
return nil;
|
||||
|
||||
*startPoint = CGPointMake(gradient.cx.valueAsPercentage, gradient.cy.valueAsPercentage);
|
||||
*endPoint = CGPointMake(gradient.fx.valueAsPercentage, gradient.fy.valueAsPercentage);
|
||||
}
|
||||
|
||||
NSArray * colors = nil;
|
||||
CGFloat * colorStops = [[self class] computeColorStopsFromString:element colors:&colors];
|
||||
NSGradient * ret = [[[NSGradient alloc] initWithColors:colors
|
||||
atLocations:colorStops
|
||||
colorSpace:[NSColorSpace genericRGBColorSpace]] autorelease];
|
||||
colorSpace:IJSVGColor.defaultColorSpace] autorelease];
|
||||
free(colorStops);
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (CGFloat)_handleTransform:(IJSVGTransform *)transform
|
||||
bounds:(CGRect)bounds
|
||||
index:(NSInteger)index
|
||||
value:(CGFloat)value
|
||||
- (void)drawInContextRef:(CGContextRef)ctx
|
||||
objectRect:(NSRect)objectRect
|
||||
absoluteTransform:(CGAffineTransform)absoluteTransform
|
||||
viewPort:(CGRect)viewBox
|
||||
{
|
||||
// rotate transform, assume its based on percentages
|
||||
// if lower then 0 is specified for 1 or 2
|
||||
CGFloat max = bounds.size.width>bounds.size.height?bounds.size.width:bounds.size.height;
|
||||
if( transform.command == IJSVGTransformCommandRotate ) {
|
||||
switch(index) {
|
||||
case 1:
|
||||
case 2: {
|
||||
if(value<1.f) {
|
||||
return (max*value);
|
||||
}
|
||||
break;
|
||||
BOOL inUserSpace = self.units == IJSVGUnitUserSpaceOnUse;
|
||||
CGFloat radius = self.radius.value;
|
||||
CGPoint startPoint = CGPointZero;
|
||||
CGPoint gradientStartPoint = CGPointZero;
|
||||
CGPoint gradientEndPoint = CGPointZero;
|
||||
|
||||
// transforms
|
||||
CGAffineTransform selfTransform = IJSVGConcatTransforms(self.transforms);
|
||||
|
||||
#pragma mark User Space On Use
|
||||
if(inUserSpace == YES) {
|
||||
CGFloat rad = radius*2.f;
|
||||
startPoint = CGPointMake(self.cx.value, self.cy.value);
|
||||
|
||||
// work out the new radius
|
||||
CGRect rect = CGRectMake(startPoint.x, startPoint.y, rad, rad);
|
||||
rect = CGRectApplyAffineTransform(rect, selfTransform);
|
||||
rect = CGRectApplyAffineTransform(rect, absoluteTransform);
|
||||
radius = CGRectGetHeight(rect)/2.f;
|
||||
|
||||
gradientStartPoint = startPoint;
|
||||
gradientEndPoint = CGPointMake(self.fx.value, self.fy.value);
|
||||
|
||||
// apply the absolute position
|
||||
CGContextConcatCTM(ctx, absoluteTransform);
|
||||
} else {
|
||||
#pragma mark Object Bounding Box
|
||||
// compute size based on percentages
|
||||
CGFloat x = [self.cx computeValue:CGRectGetWidth(objectRect)];
|
||||
CGFloat y = [self.cy computeValue:CGRectGetHeight(objectRect)];
|
||||
startPoint = CGPointMake(x, y);
|
||||
CGFloat val = MIN(CGRectGetWidth(objectRect), CGRectGetHeight(objectRect));
|
||||
radius = [self.radius computeValue:val];
|
||||
|
||||
CGFloat ex = [self.fx computeValue:CGRectGetWidth(objectRect)];
|
||||
CGFloat ey = [self.fy computeValue:CGRectGetHeight(objectRect)];
|
||||
|
||||
gradientEndPoint = CGPointMake(ex, ey);
|
||||
gradientStartPoint = startPoint;
|
||||
|
||||
// transform if width or height is not equal
|
||||
if(CGRectGetWidth(objectRect) != CGRectGetHeight(objectRect)) {
|
||||
CGAffineTransform tr = CGAffineTransformMakeTranslation(gradientStartPoint.x,
|
||||
gradientStartPoint.y);
|
||||
if(CGRectGetWidth(objectRect) > CGRectGetHeight(objectRect)) {
|
||||
tr = CGAffineTransformScale(tr, CGRectGetWidth(objectRect)/CGRectGetHeight(objectRect), 1);
|
||||
} else {
|
||||
tr = CGAffineTransformScale(tr, 1.f, CGRectGetHeight(objectRect)/CGRectGetWidth(objectRect));
|
||||
}
|
||||
tr = CGAffineTransformTranslate(tr, -gradientStartPoint.x, -gradientStartPoint.y);
|
||||
selfTransform = CGAffineTransformConcat(tr, selfTransform);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
||||
}
|
||||
#pragma mark Default drawing
|
||||
// transform the context
|
||||
CGContextConcatCTM(ctx, selfTransform);
|
||||
|
||||
- (void)drawInContextRef:(CGContextRef)ctx
|
||||
rect:(NSRect)rect
|
||||
{
|
||||
CGRect bounds = rect;
|
||||
for( IJSVGTransform * transform in self.transforms ) {
|
||||
IJSVGTransformParameterModifier modifier = ^CGFloat(NSInteger index, CGFloat value) {
|
||||
return [self _handleTransform:transform
|
||||
bounds:bounds
|
||||
index:index
|
||||
value:value];
|
||||
};
|
||||
CGContextConcatCTM(ctx, [transform CGAffineTransformWithModifier:modifier]);
|
||||
}
|
||||
|
||||
CGPoint sp = self.startPoint;
|
||||
CGPoint ep = self.endPoint;
|
||||
|
||||
if( self.startPoint.x == .5f ) {
|
||||
sp.x = bounds.size.width*self.startPoint.x;
|
||||
}
|
||||
|
||||
if(self.startPoint.y == .5f) {
|
||||
sp.y = bounds.size.height*self.startPoint.y;
|
||||
}
|
||||
|
||||
if(self.endPoint.x == .5f) {
|
||||
ep.x = bounds.size.width*self.endPoint.x;
|
||||
}
|
||||
|
||||
if(self.endPoint.y == .5f) {
|
||||
ep.y = bounds.size.height*self.endPoint.y;
|
||||
}
|
||||
|
||||
CGFloat r = self.radius.value;
|
||||
if(r == .5f) {
|
||||
r = (sp.x>sp.y?sp.x:sp.y);
|
||||
}
|
||||
|
||||
// actually perform the draw
|
||||
CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation|kCGGradientDrawsAfterEndLocation;
|
||||
CGGradientRef grad = self.CGGradient;
|
||||
CGContextDrawRadialGradient(ctx, grad, sp, 0.f, ep, r, options);
|
||||
// draw the gradient
|
||||
CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation|
|
||||
kCGGradientDrawsAfterEndLocation;
|
||||
CGContextDrawRadialGradient(ctx, self.CGGradient,
|
||||
gradientEndPoint, 0, gradientStartPoint,
|
||||
radius, options);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// IJSVGRendering.h
|
||||
// IJSVGExample
|
||||
//
|
||||
// Created by Curtis Hard on 14/03/2019.
|
||||
// Copyright © 2019 Curtis Hard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef CGFloat (^IJSVGRenderingBackingScaleFactorHelper)();
|
||||
|
||||
typedef NS_ENUM(NSInteger, IJSVGRenderQuality) {
|
||||
IJSVGRenderQualityFullResolution, // slowest to render
|
||||
IJSVGRenderQualityOptimized, // best of both worlds
|
||||
IJSVGRenderQualityLow // fast rendering
|
||||
};
|
||||
|
||||
@interface IJSVGRendering : NSObject
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// IJSVGRendering.m
|
||||
// IJSVGExample
|
||||
//
|
||||
// Created by Curtis Hard on 14/03/2019.
|
||||
// Copyright © 2019 Curtis Hard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "IJSVGRendering.h"
|
||||
|
||||
@implementation IJSVGRendering
|
||||
|
||||
@end
|
||||
@@ -8,9 +8,29 @@
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import "IJSVGLayer.h"
|
||||
#import "IJSVGUtils.h"
|
||||
|
||||
@interface IJSVGShapeLayer : CAShapeLayer
|
||||
@interface IJSVGShapeLayer : CAShapeLayer {
|
||||
|
||||
@private
|
||||
IJSVGLayer * _maskingLayer;
|
||||
}
|
||||
|
||||
IJSVG_LAYER_DEFAULT_PROPERTIES
|
||||
@property (nonatomic, assign) IJSVGGradientLayer * gradientFillLayer;
|
||||
@property (nonatomic, assign) IJSVGPatternLayer * patternFillLayer;
|
||||
@property (nonatomic, assign) IJSVGStrokeLayer * strokeLayer;
|
||||
@property (nonatomic, assign) IJSVGGradientLayer * gradientStrokeLayer;
|
||||
@property (nonatomic, assign) IJSVGPatternLayer * patternStrokeLayer;
|
||||
@property (nonatomic, assign) BOOL requiresBackingScaleHelp;
|
||||
@property (nonatomic, assign) CGFloat backingScaleFactor;
|
||||
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
|
||||
@property (nonatomic, assign) CGBlendMode blendingMode;
|
||||
@property (nonatomic, assign) CGPoint absoluteOrigin;
|
||||
@property (nonatomic, assign) CGPoint originalPathOrigin;
|
||||
@property (nonatomic, assign) BOOL convertMasksToPaths;
|
||||
|
||||
- (void)applySublayerMaskToContext:(CGContextRef)context
|
||||
forSublayer:(IJSVGLayer *)sublayer
|
||||
withOffset:(CGPoint)offset;
|
||||
|
||||
@end
|
||||
|
||||
@@ -7,16 +7,156 @@
|
||||
//
|
||||
|
||||
#import "IJSVGShapeLayer.h"
|
||||
#import "IJSVGGroupLayer.h"
|
||||
|
||||
@implementation IJSVGShapeLayer
|
||||
|
||||
IJSVG_LAYER_DEFAULT_SYNTHESIZE
|
||||
@synthesize gradientFillLayer;
|
||||
@synthesize patternFillLayer;
|
||||
@synthesize gradientStrokeLayer;
|
||||
@synthesize patternStrokeLayer;
|
||||
@synthesize strokeLayer;
|
||||
@synthesize requiresBackingScaleHelp;
|
||||
@synthesize backingScaleFactor;
|
||||
@synthesize blendingMode;
|
||||
@synthesize convertMasksToPaths;
|
||||
@synthesize originalPathOrigin;
|
||||
@synthesize renderQuality;
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
IJSVG_LAYER_DEFAULT_DEALLOC_INSTRUCTIONS
|
||||
[_maskingLayer release], _maskingLayer = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
IJSVG_LAYER_ADD_SUBVIEW_DEFAULT_IMPLEMENTATION
|
||||
- (void)addSublayer:(CALayer *)layer {
|
||||
if([layer isKindOfClass:[IJSVGLayer class]] == NO &&
|
||||
[layer isKindOfClass:[IJSVGShapeLayer class]] == NO) {
|
||||
NSString * r = [NSString stringWithFormat:@"The layer must be an instance of IJSVGLayer, %@ given.",
|
||||
[layer class]];
|
||||
NSException * exception = [NSException exceptionWithName:@"IJSVGInvalidSublayerException"
|
||||
reason:r
|
||||
userInfo:nil];
|
||||
@throw exception;
|
||||
}
|
||||
[super addSublayer:layer];
|
||||
}
|
||||
|
||||
- (void)setBackingScaleFactor:(CGFloat)newFactor
|
||||
{
|
||||
if(self.backingScaleFactor == newFactor) {
|
||||
return;
|
||||
}
|
||||
backingScaleFactor = newFactor;
|
||||
self.contentsScale = newFactor;
|
||||
self.rasterizationScale = newFactor;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)_customRenderInContext:(CGContextRef)ctx
|
||||
{
|
||||
if(self.convertMasksToPaths == YES && _maskingLayer != nil) {
|
||||
CGContextSaveGState(ctx);
|
||||
[self applySublayerMaskToContext:ctx
|
||||
forSublayer:(IJSVGLayer *)self
|
||||
withOffset:CGPointZero];
|
||||
[super renderInContext:ctx];
|
||||
CGContextRestoreGState(ctx);
|
||||
return;
|
||||
}
|
||||
[super renderInContext:ctx];
|
||||
}
|
||||
|
||||
- (void)setConvertMasksToPaths:(BOOL)flag
|
||||
{
|
||||
if(convertMasksToPaths == flag) {
|
||||
return;
|
||||
}
|
||||
convertMasksToPaths = flag;
|
||||
if(flag == YES) {
|
||||
if(_maskingLayer != nil) {
|
||||
[_maskingLayer release], _maskingLayer = nil;
|
||||
}
|
||||
_maskingLayer = [(IJSVGLayer *)self.mask retain];
|
||||
self.mask = nil;
|
||||
} else {
|
||||
self.mask = _maskingLayer;
|
||||
[_maskingLayer release], _maskingLayer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applySublayerMaskToContext:(CGContextRef)context
|
||||
forSublayer:(IJSVGLayer *)sublayer
|
||||
withOffset:(CGPoint)offset
|
||||
{
|
||||
// apply any transforms needed
|
||||
CGPoint layerOffset = offset;
|
||||
CGAffineTransform sublayerTransform = CATransform3DGetAffineTransform(sublayer.transform);
|
||||
CGContextConcatCTM( context, CGAffineTransformInvert(sublayerTransform) );
|
||||
|
||||
// walk up the superlayer chain
|
||||
CALayer * superlayer = self.superlayer;
|
||||
if (IJSVGIsSVGLayer(superlayer) == YES) {
|
||||
[(IJSVGLayer *)superlayer applySublayerMaskToContext:context
|
||||
forSublayer:(IJSVGLayer *)self
|
||||
withOffset:layerOffset];
|
||||
}
|
||||
|
||||
// grab the masking layer
|
||||
IJSVGShapeLayer * maskingLayer = [self maskingLayer];
|
||||
|
||||
// if its a group we need to get the lowest level children
|
||||
// and walk up the chain again
|
||||
if([maskingLayer isKindOfClass:[IJSVGGroupLayer class]]) {
|
||||
NSArray * subs = [IJSVGLayer deepestSublayersOfLayer:maskingLayer];
|
||||
for(IJSVGLayer * subLayer in subs) {
|
||||
[subLayer applySublayerMaskToContext:context
|
||||
forSublayer:(IJSVGLayer *)self
|
||||
withOffset:layerOffset];
|
||||
}
|
||||
} else if ([maskingLayer isKindOfClass:[IJSVGShapeLayer class]]) {
|
||||
// is a shape, go for it!
|
||||
CGPathRef maskPath = maskingLayer.path;
|
||||
CGContextTranslateCTM(context, -layerOffset.x, -layerOffset.y);
|
||||
CGContextAddPath(context, maskPath);
|
||||
CGContextClip(context);
|
||||
CGContextTranslateCTM(context, layerOffset.x, layerOffset.y);
|
||||
}
|
||||
CGContextConcatCTM(context, sublayerTransform);
|
||||
}
|
||||
|
||||
- (IJSVGShapeLayer *)maskingLayer
|
||||
{
|
||||
return (IJSVGShapeLayer *)_maskingLayer ?: nil;
|
||||
}
|
||||
|
||||
- (void)renderInContext:(CGContextRef)ctx
|
||||
{
|
||||
if(self.blendingMode != kCGBlendModeNormal) {
|
||||
CGContextSaveGState(ctx);
|
||||
CGContextSetBlendMode(ctx, self.blendingMode);
|
||||
[self _customRenderInContext:ctx];
|
||||
CGContextRestoreGState(ctx);
|
||||
return;
|
||||
}
|
||||
[self _customRenderInContext:ctx];
|
||||
}
|
||||
|
||||
- (CGPoint)absoluteOrigin
|
||||
{
|
||||
CGPoint point = CGPointZero;
|
||||
CALayer * pLayer = self;
|
||||
while(pLayer != nil) {
|
||||
point.x += pLayer.frame.origin.x;
|
||||
point.y += pLayer.frame.origin.y;
|
||||
pLayer = pLayer.superlayer;
|
||||
}
|
||||
return point;
|
||||
}
|
||||
|
||||
- (id<CAAction>)actionForKey:(NSString *)event
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -44,25 +44,44 @@
|
||||
|
||||
+ (IJSVGStyle *)parseStyleString:(NSString *)string
|
||||
{
|
||||
static NSRegularExpression * _reg = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
IJSVGStyle * style = [[[self class] alloc] init];
|
||||
dispatch_once(&onceToken, ^{
|
||||
_reg = [[NSRegularExpression alloc] initWithPattern:@"([a-zA-Z\\-]+)\\:([^;]+)\\;?"
|
||||
options:0
|
||||
error:nil];
|
||||
});
|
||||
[_reg enumerateMatchesInString:string
|
||||
options:0
|
||||
range:NSMakeRange( 0, string.length )
|
||||
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||
NSString * key = [string substringWithRange:[result rangeAtIndex:1]];
|
||||
NSString * value = [string substringWithRange:[result rangeAtIndex:2]];
|
||||
[[self class] computeStyleProperty:key
|
||||
value:value
|
||||
style:style];
|
||||
}];
|
||||
return [style autorelease];
|
||||
IJSVGStyle * style = [[[[self class] alloc] init] autorelease];
|
||||
NSInteger length = string.length;
|
||||
NSInteger index = 0;
|
||||
NSString * key = nil;
|
||||
NSString * value = nil;
|
||||
|
||||
// iterate over the string - its actually really simple what we need
|
||||
// to do
|
||||
for(NSInteger i = 0; i < length; i++) {
|
||||
unichar c = [string characterAtIndex:i];
|
||||
|
||||
// find the key
|
||||
if(c == ':') {
|
||||
key = [string substringWithRange:NSMakeRange(index, (i-index))];
|
||||
index = i+1;
|
||||
}
|
||||
|
||||
// find the value
|
||||
else if(c == ';' || i == (length-1)) {
|
||||
NSInteger chomp;
|
||||
if(i == (length-1) && c != ';') {
|
||||
chomp = (i-(index-1));
|
||||
} else {
|
||||
chomp = (i-index);
|
||||
}
|
||||
value = [string substringWithRange:NSMakeRange(index, chomp)];
|
||||
index = i+1;
|
||||
}
|
||||
|
||||
// set the propery if it actually exists
|
||||
if(key != nil && value != nil) {
|
||||
[style setPropertyValue:[[self class] trimString:value]
|
||||
forProperty:[[self class] trimString:key]];
|
||||
key = nil;
|
||||
value = nil;
|
||||
}
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
+ (NSString *)trimString:(NSString *)string
|
||||
@@ -75,17 +94,6 @@
|
||||
return @[@"fill",@"stroke-colour",@"stop-color",@"stroke"];
|
||||
}
|
||||
|
||||
+ (void)computeStyleProperty:(NSString *)key
|
||||
value:(NSString *)value
|
||||
style:(IJSVGStyle *)style
|
||||
{
|
||||
key = [[self class] trimString:key];
|
||||
value = [[self class] trimString:value];
|
||||
[style setPropertyValue:value
|
||||
forProperty:key];
|
||||
|
||||
}
|
||||
|
||||
+ (BOOL)isNumeric:(NSString *)string
|
||||
{
|
||||
return [[NSScanner scannerWithString:string] scanFloat:NULL];
|
||||
|
||||
@@ -71,12 +71,16 @@
|
||||
// create a new selector if not found
|
||||
if((selector = [_selectors objectForKey:selectorName]) == nil) {
|
||||
selector = [[[IJSVGStyleSheetSelector alloc] initWithSelectorString:selectorName] autorelease];
|
||||
[_selectors setObject:selector
|
||||
forKey:selectorName];
|
||||
if(selector != nil) {
|
||||
[_selectors setObject:selector
|
||||
forKey:selectorName];
|
||||
}
|
||||
}
|
||||
|
||||
// add it to our list
|
||||
[array addObject:selector];
|
||||
if(selector != nil) {
|
||||
[array addObject:selector];
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@@ -257,7 +257,12 @@ BOOL IJSVGStyleSheetMatchSelector(IJSVGNode * node, IJSVGStyleSheetSelectorRaw *
|
||||
{
|
||||
selector = [string copy];
|
||||
_rawSelectors = [[NSMutableArray alloc] init];
|
||||
[self _compile];
|
||||
|
||||
// failed to compile
|
||||
if([self _compile] == NO) {
|
||||
[self release], self = nil;
|
||||
return nil;
|
||||
}
|
||||
[self _calculate];
|
||||
}
|
||||
return self;
|
||||
@@ -283,23 +288,55 @@ BOOL IJSVGStyleSheetMatchSelector(IJSVGNode * node, IJSVGStyleSheetSelectorRaw *
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_compile
|
||||
- (BOOL)validateSelector:(NSString *)string
|
||||
{
|
||||
char * invalidChars = "@:;*()[]";
|
||||
NSUInteger length = strlen(invalidChars);
|
||||
NSUInteger sLength = string.length;
|
||||
for(NSUInteger i = 0; i < length; i++) {
|
||||
for(NSUInteger s = 0; s < sLength; s++) {
|
||||
// if found invalid char, just return NO instantly
|
||||
if(invalidChars[i] == [string characterAtIndex:s]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)_compile
|
||||
{
|
||||
|
||||
// completely unsupported
|
||||
if([self validateSelector:selector] == NO) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// keychar lookup
|
||||
char * keychars = "#+.>~ ";
|
||||
NSUInteger aLength = strlen(keychars);
|
||||
BOOL (^isKeyChar)(char anotherChar) = ^(char anotherChar) {
|
||||
for(NSInteger i = 0; i < aLength; i++) {
|
||||
if(keychars[i] == anotherChar) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
};
|
||||
|
||||
|
||||
NSUInteger length = selector.length;
|
||||
NSMutableArray * sels = [[[NSMutableArray alloc] init] autorelease];
|
||||
NSCharacterSet * alphaNumeric = [NSCharacterSet characterSetWithCharactersInString:@"_-abcdefghijklmnopqrstuvwxyz0123456789"];
|
||||
IJSVGStyleSheetSelectorRaw * rawSelector = [[[IJSVGStyleSheetSelectorRaw alloc] init] autorelease];
|
||||
|
||||
for(NSUInteger i = 0; i < length; i++)
|
||||
{
|
||||
for(NSUInteger i = 0; i < length; i++) {
|
||||
unichar c = [selector characterAtIndex:i];
|
||||
|
||||
// beginning of class
|
||||
if( c == '.' ) {
|
||||
i++;
|
||||
for(NSUInteger a = i; a < length; a++ ) {
|
||||
unichar ca = [selector characterAtIndex:a];
|
||||
if([alphaNumeric characterIsMember:ca] == NO || a == length-1) {
|
||||
if(isKeyChar(ca) == YES || a == length-1) {
|
||||
// if at end, add 1 to a so it gets the last character
|
||||
if( a == length-1 ) {
|
||||
a++;
|
||||
@@ -316,7 +353,7 @@ BOOL IJSVGStyleSheetMatchSelector(IJSVGNode * node, IJSVGStyleSheetSelectorRaw *
|
||||
i++;
|
||||
for(NSUInteger a = i; a < length; a++ ) {
|
||||
unichar ca = [selector characterAtIndex:a];
|
||||
if([alphaNumeric characterIsMember:ca] == NO || a == length-1) {
|
||||
if(isKeyChar(ca) == YES || a == length-1) {
|
||||
// if at end, add 1 to a so it gets the last character
|
||||
if( a == length-1 ) {
|
||||
a++;
|
||||
@@ -375,7 +412,7 @@ BOOL IJSVGStyleSheetMatchSelector(IJSVGNode * node, IJSVGStyleSheetSelectorRaw *
|
||||
else {
|
||||
for( NSUInteger a = i; a < length; a++ ) {
|
||||
unichar ca = [selector characterAtIndex:a];
|
||||
if([alphaNumeric characterIsMember:ca] == NO || a == length-1) {
|
||||
if(isKeyChar(ca) == YES || a == length-1) {
|
||||
if( a == length-1 ) {
|
||||
a++;
|
||||
}
|
||||
@@ -397,6 +434,7 @@ BOOL IJSVGStyleSheetMatchSelector(IJSVGNode * node, IJSVGStyleSheetSelectorRaw *
|
||||
}
|
||||
// now its compiled, we need to reverse the selectors
|
||||
[_rawSelectors addObjectsFromArray:[sels reverseObjectEnumerator].allObjects];
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,13 +9,24 @@
|
||||
#import "IJSVGTransaction.h"
|
||||
|
||||
void IJSVGBeginTransactionLock() {
|
||||
if(NSThread.isMainThread == YES) {
|
||||
return;
|
||||
}
|
||||
[CATransaction begin];
|
||||
[CATransaction lock];
|
||||
if(@available(macOS 10.14, *)) {} else {
|
||||
[CATransaction lock];
|
||||
}
|
||||
[CATransaction setDisableActions:YES];
|
||||
};
|
||||
|
||||
void IJSVGEndTransactionLock() {
|
||||
[CATransaction unlock];
|
||||
if(NSThread.isMainThread == YES) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(@available(macOS 10.14, *)) {} else {
|
||||
[CATransaction unlock];
|
||||
}
|
||||
[CATransaction commit];
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "IJSVGUtils.h"
|
||||
|
||||
@class IJSVGTransform;
|
||||
|
||||
typedef CGFloat (^IJSVGTransformParameterModifier)(NSInteger index, CGFloat value);
|
||||
typedef void (^IJSVGTransformApplyBlock)(IJSVGTransform * transform);
|
||||
|
||||
typedef NS_OPTIONS( NSInteger, IJSVGTransformCommand ) {
|
||||
IJSVGTransformCommandMatrix,
|
||||
@@ -35,12 +38,21 @@ typedef NS_OPTIONS( NSInteger, IJSVGTransformCommand ) {
|
||||
@property ( nonatomic, assign ) NSInteger parameterCount;
|
||||
@property ( nonatomic, assign ) NSInteger sort;
|
||||
|
||||
NSString * IJSVGDebugAffineTransform(CGAffineTransform transform);
|
||||
NSString * IJSVGDebugTransforms(NSArray<IJSVGTransform *> * transforms);
|
||||
void IJSVGApplyTransform(NSArray<IJSVGTransform *> * transforms, IJSVGTransformApplyBlock block);
|
||||
CGAffineTransform IJSVGConcatTransforms(NSArray<IJSVGTransform *> * transforms);
|
||||
|
||||
+ (NSArray<IJSVGTransform *> *)transformsFromAffineTransform:(CGAffineTransform)affineTransform;
|
||||
+ (NSArray *)transformsForString:(NSString *)string;
|
||||
+ (NSBezierPath *)transformedPath:(IJSVGPath *)path;
|
||||
+ (NSArray<NSString *> *)affineTransformToSVGTransformAttributeString:(CGAffineTransform)affineTransform;
|
||||
+ (NSString *)affineTransformToSVGMatrixString:(CGAffineTransform)affineTransform;
|
||||
- (CGAffineTransform)CGAffineTransform;
|
||||
- (CGAffineTransform)CGAffineTransformWithModifier:(IJSVGTransformParameterModifier)modifier;
|
||||
- (CGAffineTransform)stackIdentity:(CGAffineTransform)identity;
|
||||
- (void)recalculateWithBounds:(CGRect)bounds;
|
||||
+ (IJSVGTransform *)transformByTranslatingX:(CGFloat)x
|
||||
y:(CGFloat)y;
|
||||
|
||||
@end
|
||||
|
||||
@@ -33,6 +33,51 @@
|
||||
return trans;
|
||||
}
|
||||
|
||||
NSString * IJSVGDebugAffineTransform(CGAffineTransform transform)
|
||||
{
|
||||
NSMutableArray * strings = [[[NSMutableArray alloc] init] autorelease];
|
||||
[strings addObjectsFromArray:[IJSVGTransform affineTransformToSVGTransformAttributeString:transform]];
|
||||
return [strings componentsJoinedByString:@" "];
|
||||
}
|
||||
|
||||
NSString * IJSVGDebugTransforms(NSArray<IJSVGTransform *> * transforms)
|
||||
{
|
||||
NSMutableArray * strings = [[[NSMutableArray alloc] init] autorelease];
|
||||
IJSVGApplyTransform(transforms, ^(IJSVGTransform *transform) {
|
||||
[strings addObjectsFromArray:[IJSVGTransform affineTransformToSVGTransformAttributeString:transform.CGAffineTransform]];
|
||||
});
|
||||
return [strings componentsJoinedByString:@" "];
|
||||
}
|
||||
|
||||
CGAffineTransform IJSVGConcatTransforms(NSArray<IJSVGTransform *> * transforms)
|
||||
{
|
||||
__block CGAffineTransform trans = CGAffineTransformIdentity;
|
||||
IJSVGApplyTransform(transforms, ^(IJSVGTransform *transform) {
|
||||
trans = CGAffineTransformConcat(trans, transform.CGAffineTransform);
|
||||
});
|
||||
return trans;
|
||||
}
|
||||
|
||||
void IJSVGApplyTransform(NSArray<IJSVGTransform *> * transforms, IJSVGTransformApplyBlock block)
|
||||
{
|
||||
for(IJSVGTransform * transform in transforms) {
|
||||
block(transform);
|
||||
}
|
||||
};
|
||||
|
||||
+ (IJSVGTransform *)transformByTranslatingX:(CGFloat)x
|
||||
y:(CGFloat)y
|
||||
{
|
||||
IJSVGTransform * transform = [[[self alloc] init] autorelease];
|
||||
transform.command = IJSVGTransformCommandTranslate;
|
||||
transform.parameterCount = 2;
|
||||
CGFloat * params = (CGFloat *)malloc(sizeof(CGFloat)*2);
|
||||
params[0] = x;
|
||||
params[1] = y;
|
||||
transform.parameters = params;
|
||||
return transform;
|
||||
}
|
||||
|
||||
- (void)recalculateWithBounds:(CGRect)bounds
|
||||
{
|
||||
CGFloat max = bounds.size.width>bounds.size.height?bounds.size.width:bounds.size.height;
|
||||
@@ -101,8 +146,9 @@
|
||||
{
|
||||
NSString * command = [string substringWithRange:[result rangeAtIndex:1]];
|
||||
IJSVGTransformCommand commandType = [[self class] commandForCommandString:command];
|
||||
if( commandType == IJSVGTransformCommandNotImplemented )
|
||||
if( commandType == IJSVGTransformCommandNotImplemented ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create the transform
|
||||
NSString * params = [string substringWithRange:[result rangeAtIndex:2]];
|
||||
@@ -400,6 +446,19 @@
|
||||
return CGAffineTransformIdentity;
|
||||
}
|
||||
|
||||
+ (NSArray<IJSVGTransform *> *)transformsFromAffineTransform:(CGAffineTransform)affineTransform
|
||||
{
|
||||
NSArray * strings = [self affineTransformToSVGTransformAttributeString:affineTransform];
|
||||
return [self transformsForString:[strings componentsJoinedByString:@" "]];
|
||||
}
|
||||
|
||||
+ (NSString *)affineTransformToSVGMatrixString:(CGAffineTransform)transform
|
||||
{
|
||||
return [NSString stringWithFormat:@"matrix(%g,%g,%g,%g,%g,%g)",
|
||||
transform.a, transform.b, transform.c, transform.d,
|
||||
transform.tx, transform.ty];
|
||||
}
|
||||
|
||||
// this is an Object-C version of the matrixToTransform method from SVGO
|
||||
+ (NSArray<NSString *> *)affineTransformToSVGTransformAttributeString:(CGAffineTransform)affineTransform
|
||||
{
|
||||
@@ -414,6 +473,7 @@
|
||||
|
||||
CGFloat sx = sqrtf(data[0]*data[0] + data[1] * data[1]);
|
||||
CGFloat sy = (data[0]*data[3] - data[1]*data[2])/sx;
|
||||
|
||||
CGFloat colSum = data[0]*data[2] + data[1]*data[3];
|
||||
CGFloat rowSum = data[0]*data[1] + data[2]*data[3];
|
||||
BOOL scaleBefore = rowSum != 0.f || (sx == sy);
|
||||
@@ -439,8 +499,8 @@
|
||||
sy = data[3];
|
||||
} else if(colSum == 0.f || (sx == 1.f && sy == 1.f) || scaleBefore == NO) {
|
||||
if(scaleBefore == NO) {
|
||||
sx = (data[0] < 0.f ? -1.f : 1.f * sqrtf(data[0] * data[0] + data[2] * data[2]));
|
||||
sy = (data[3] < 0.f ? -1.f : 1.f * sqrtf(data[1] * data[1] + data[3] * data[3]));
|
||||
sx = (data[0] < 0.f ? -1.f : 1.f) * sqrtf(data[0] * data[0] + data[2] * data[2]);
|
||||
sy = (data[3] < 0.f ? -1.f : 1.f) * sqrtf(data[1] * data[1] + data[3] * data[3]);
|
||||
NSString * str = nil;
|
||||
if(sx == sy) {
|
||||
str = [NSString stringWithFormat:@"scale(%g)",sx];
|
||||
@@ -499,5 +559,11 @@
|
||||
return trans;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"%@ %@",[super description],
|
||||
[self.class affineTransformToSVGTransformAttributeString:self.CGAffineTransform]];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -46,10 +46,17 @@
|
||||
|
||||
+ (IJSVGUnitLength *)unitWithString:(NSString *)string
|
||||
{
|
||||
// just return noting for inherit, node will deal
|
||||
// with the rest
|
||||
if([string isEqualToString:@"inherit"]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
IJSVGUnitLength * unit = [[[self alloc] init] autorelease];
|
||||
unit.value = [string floatValue];
|
||||
unit.type = IJSVGUnitLengthTypeNumber;
|
||||
if([string hasSuffix:@"%"] == YES) {
|
||||
unit.value /= 100.f;
|
||||
unit.type = IJSVGUnitLengthTypePercentage;
|
||||
}
|
||||
return unit;
|
||||
@@ -58,7 +65,7 @@
|
||||
- (CGFloat)computeValue:(CGFloat)anotherValue
|
||||
{
|
||||
if(self.type == IJSVGUnitLengthTypePercentage) {
|
||||
return ((anotherValue/100)*self.value);
|
||||
return ((anotherValue/100.f)*(self.value*100.f));
|
||||
}
|
||||
return self.value;
|
||||
}
|
||||
@@ -71,9 +78,14 @@
|
||||
- (NSString *)stringValue
|
||||
{
|
||||
if(self.type == IJSVGUnitLengthTypePercentage) {
|
||||
return [NSString stringWithFormat:@"%g%%",self.value];
|
||||
return [NSString stringWithFormat:@"%g%%",(self.value*100.f)];
|
||||
}
|
||||
return [NSString stringWithFormat:@"%g",self.value];
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"%f%@",self.value,(self.type == IJSVGUnitLengthTypePercentage ? @"%" : @"")];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <xlocale.h>
|
||||
#import "IJSVGCommand.h"
|
||||
#import "IJSVGGradientUnitLength.h"
|
||||
|
||||
@interface IJSVGUtils : NSObject
|
||||
|
||||
@@ -17,6 +19,14 @@ CGFloat angle( CGPoint a, CGPoint b );
|
||||
CGFloat radians_to_degrees( CGFloat radians);
|
||||
CGFloat degrees_to_radians( CGFloat degrees );
|
||||
|
||||
BOOL IJSVGIsCommonHTMLElementName(NSString * str);
|
||||
NSArray * IJSVGCommonHTMLElementNames();
|
||||
|
||||
NSString * IJSVGShortFloatString(CGFloat f);
|
||||
NSString * IJSVGShortFloatStringWithPrecision(CGFloat f, NSInteger precision);
|
||||
|
||||
BOOL IJSVGIsLegalCommandCharacter(unichar aChar);
|
||||
BOOL IJSVGIsSVGLayer(CALayer * layer);
|
||||
+ (IJSVGCommandType)typeForCommandString:(NSString *)string;
|
||||
+ (CGFloat *)commandParameters:(NSString *)command
|
||||
count:(NSInteger *)count;
|
||||
@@ -25,6 +35,9 @@ CGFloat degrees_to_radians( CGFloat degrees );
|
||||
+ (IJSVGLineJoinStyle)lineJoinStyleForString:(NSString *)string;
|
||||
+ (IJSVGLineCapStyle)lineCapStyleForString:(NSString *)string;
|
||||
+ (IJSVGUnitType)unitTypeForString:(NSString *)string;
|
||||
+ (IJSVGBlendMode)blendModeForString:(NSString *)string;
|
||||
+ (NSString *)mixBlendingModeForBlendMode:(IJSVGBlendMode)blendMode;
|
||||
|
||||
+ (void)logParameters:(CGFloat *)param
|
||||
count:(NSInteger)count;
|
||||
+ (CGFloat)floatValue:(NSString *)string;
|
||||
@@ -39,4 +52,6 @@ CGFloat degrees_to_radians( CGFloat degrees );
|
||||
+ (IJSVGFontTraits)fontWeightTraitForString:(NSString *)string
|
||||
weight:(CGFloat *)weight;
|
||||
|
||||
+ (CGPathRef)newFlippedCGPath:(CGPathRef)path;
|
||||
+ (CGPathRef)newCGPathFromBezierPath:(NSBezierPath *)bezPath;
|
||||
@end
|
||||
|
||||
@@ -7,9 +7,187 @@
|
||||
//
|
||||
|
||||
#import "IJSVGUtils.h"
|
||||
#import "IJSVGLayer.h"
|
||||
#import "IJSVGShapeLayer.h"
|
||||
|
||||
@implementation IJSVGUtils
|
||||
|
||||
BOOL IJSVGIsCommonHTMLElementName(NSString * str)
|
||||
{
|
||||
str = str.lowercaseString;
|
||||
return [IJSVGCommonHTMLElementNames() containsObject:str];
|
||||
};
|
||||
|
||||
NSArray * IJSVGCommonHTMLElementNames()
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
NSString * IJSVGShortFloatString(CGFloat f)
|
||||
{
|
||||
return [NSString stringWithFormat:@"%g",f];
|
||||
};
|
||||
|
||||
NSString * IJSVGShortFloatStringWithPrecision(CGFloat f, NSInteger precision)
|
||||
{
|
||||
NSString * format = [NSString stringWithFormat:@"%@.%ld%@",@"%",precision,@"f"];
|
||||
NSString * ret = [NSString stringWithFormat:format,f];
|
||||
// can it be reduced even more?
|
||||
if(ret.floatValue == (float)ret.integerValue) {
|
||||
ret = [NSString stringWithFormat:@"%ld",ret.integerValue];
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
BOOL IJSVGIsLegalCommandCharacter(unichar aChar)
|
||||
{
|
||||
const char * validChars = "MmZzLlHhVvCcSsQqTtAa";
|
||||
return strchr(validChars, aChar) != NULL;
|
||||
}
|
||||
|
||||
BOOL IJSVGIsSVGLayer(CALayer * layer)
|
||||
{
|
||||
return [layer isKindOfClass:IJSVGLayer.class] ||
|
||||
[layer isKindOfClass:IJSVGShapeLayer.class];
|
||||
}
|
||||
|
||||
CGFloat angle( CGPoint a, CGPoint b ) {
|
||||
return [IJSVGUtils angleBetweenPointA:a
|
||||
pointb:b];
|
||||
@@ -36,7 +214,7 @@ CGFloat degrees_to_radians( CGFloat degrees )
|
||||
|
||||
+ (IJSVGCommandType)typeForCommandString:(NSString *)string
|
||||
{
|
||||
return [string isEqualToString:[string uppercaseString]] ? IJSVGCommandTypeAbsolute : IJSVGCommandTypeRelative;
|
||||
return isupper([string characterAtIndex:0]) ? IJSVGCommandTypeAbsolute : IJSVGCommandTypeRelative;
|
||||
}
|
||||
|
||||
+ (NSString *)defURL:(NSString *)string
|
||||
@@ -119,11 +297,103 @@ CGFloat degrees_to_radians( CGFloat degrees )
|
||||
return IJSVGUnitObjectBoundingBox;
|
||||
}
|
||||
|
||||
+ (IJSVGBlendMode)blendModeForString:(NSString *)string
|
||||
{
|
||||
string = string.lowercaseString;
|
||||
if([string isEqualToString:@"normal"])
|
||||
return IJSVGBlendModeNormal;
|
||||
if([string isEqualToString:@"multiply"])
|
||||
return IJSVGBlendModeMultiply;
|
||||
if([string isEqualToString:@"screen"])
|
||||
return IJSVGBlendModeScreen;
|
||||
if([string isEqualToString:@"overlay"])
|
||||
return IJSVGBlendModeOverlay;
|
||||
if([string isEqualToString:@"darken"])
|
||||
return IJSVGBlendModeDarken;
|
||||
if([string isEqualToString:@"lighten"])
|
||||
return IJSVGBlendModeLighten;
|
||||
if([string isEqualToString:@"color-dodge"])
|
||||
return IJSVGBlendModeColorDodge;
|
||||
if([string isEqualToString:@"color-burn"])
|
||||
return IJSVGBlendModeColorBurn;
|
||||
if([string isEqualToString:@"hard-light"])
|
||||
return IJSVGBlendModeHardLight;
|
||||
if([string isEqualToString:@"soft-light"])
|
||||
return IJSVGBlendModeSoftLight;
|
||||
if([string isEqualToString:@"difference"])
|
||||
return IJSVGBlendModeDifference;
|
||||
if([string isEqualToString:@"exclusion"])
|
||||
return IJSVGBlendModeExclusion;
|
||||
if([string isEqualToString:@"hue"])
|
||||
return IJSVGBlendModeHue;
|
||||
if([string isEqualToString:@"saturation"])
|
||||
return IJSVGBlendModeSaturation;
|
||||
if([string isEqualToString:@"color"])
|
||||
return IJSVGBlendModeColor;
|
||||
if([string isEqualToString:@"luminosity"])
|
||||
return IJSVGBlendModeLuminosity;
|
||||
return IJSVGBlendModeNormal;
|
||||
}
|
||||
|
||||
+ (NSString *)mixBlendingModeForBlendMode:(IJSVGBlendMode)blendMode
|
||||
{
|
||||
switch (blendMode) {
|
||||
case IJSVGBlendModeMultiply: {
|
||||
return @"multiple";
|
||||
}
|
||||
case IJSVGBlendModeScreen: {
|
||||
return @"screen";
|
||||
}
|
||||
case IJSVGBlendModeOverlay: {
|
||||
return @"overlay";
|
||||
}
|
||||
case IJSVGBlendModeDarken: {
|
||||
return @"darken";
|
||||
}
|
||||
case IJSVGBlendModeLighten: {
|
||||
return @"lighten";
|
||||
}
|
||||
case IJSVGBlendModeColorDodge: {
|
||||
return @"color-dodge";
|
||||
}
|
||||
case IJSVGBlendModeColorBurn: {
|
||||
return @"color-burn";
|
||||
}
|
||||
case IJSVGBlendModeHardLight: {
|
||||
return @"hard-light";
|
||||
}
|
||||
case IJSVGBlendModeSoftLight: {
|
||||
return @"soft-light";
|
||||
}
|
||||
case IJSVGBlendModeDifference: {
|
||||
return @"difference";
|
||||
}
|
||||
case IJSVGBlendModeExclusion: {
|
||||
return @"exclusion";
|
||||
}
|
||||
case IJSVGBlendModeHue: {
|
||||
return @"hue";
|
||||
}
|
||||
case IJSVGBlendModeSaturation: {
|
||||
return @"saturation";
|
||||
}
|
||||
case IJSVGBlendModeColor: {
|
||||
return @"color";
|
||||
}
|
||||
case IJSVGBlendModeLuminosity: {
|
||||
return @"luminosity";
|
||||
}
|
||||
case IJSVGBlendModeNormal:
|
||||
default: {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (CGFloat *)commandParameters:(NSString *)command
|
||||
count:(NSInteger *)count
|
||||
{
|
||||
if( [command isKindOfClass:[NSNumber class]] )
|
||||
{
|
||||
if( [command isKindOfClass:[NSNumber class]] ) {
|
||||
CGFloat * ret = (CGFloat *)malloc(1*sizeof(CGFloat));
|
||||
ret[0] = [(NSNumber *)command floatValue];
|
||||
*count = 1;
|
||||
@@ -138,12 +408,11 @@ CGFloat degrees_to_radians( CGFloat degrees )
|
||||
{
|
||||
// default sizes and memory
|
||||
// sizes for the string buffer
|
||||
NSInteger defSize = 50;
|
||||
NSInteger size = defSize;
|
||||
NSInteger sLength = string.length;
|
||||
const NSInteger defFloatSize = 30;
|
||||
const NSInteger defSize = 15;
|
||||
|
||||
// default memory size for the floats
|
||||
NSInteger defFloatSize = 100;
|
||||
// default memory size for the float
|
||||
NSInteger size = defSize;
|
||||
NSInteger floatSize = defFloatSize;
|
||||
|
||||
NSInteger i = 0;
|
||||
@@ -152,6 +421,8 @@ CGFloat degrees_to_radians( CGFloat degrees )
|
||||
const char * cString = [string cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
const char * validChars = "0123456789eE+-.";
|
||||
|
||||
NSInteger sLength = strlen(cString);
|
||||
|
||||
// buffer for the returned floats
|
||||
CGFloat * floats = (CGFloat *)malloc(sizeof(CGFloat)*defFloatSize);
|
||||
|
||||
@@ -168,7 +439,7 @@ CGFloat degrees_to_radians( CGFloat degrees )
|
||||
nextChar = cString[i+1];
|
||||
}
|
||||
|
||||
bool isValid = strchr(validChars, currentChar);
|
||||
bool isValid = strchr(validChars, currentChar) != NULL;
|
||||
|
||||
// in order to work out the split, its either because the next char is
|
||||
// a hyphen or a plus, or next char is a decimal and the current number is a decimal
|
||||
@@ -205,7 +476,7 @@ CGFloat degrees_to_radians( CGFloat degrees )
|
||||
// is at end of string, or wants to be stopped
|
||||
// buffer has to actually exist or its completly
|
||||
// useless and will cause a crash
|
||||
if(buffer != NULL && (wantsEnd || i == sLength-1)) {
|
||||
if((buffer != NULL && bufferCount != 0) && (wantsEnd || i == sLength-1)) {
|
||||
// make sure there is enough room in the float pool
|
||||
if((counter+1) == floatSize) {
|
||||
floatSize += defFloatSize;
|
||||
@@ -213,17 +484,18 @@ CGFloat degrees_to_radians( CGFloat degrees )
|
||||
}
|
||||
|
||||
// add the float
|
||||
floats[counter++] = atof(buffer);
|
||||
floats[counter++] = strtod_l(buffer, NULL, NULL);
|
||||
|
||||
// memory clean and counter resets
|
||||
free(buffer);
|
||||
size = defSize;
|
||||
memset(buffer, '\0', sizeof(*buffer)*size);
|
||||
isDecimal = false;
|
||||
bufferCount = 0;
|
||||
buffer = NULL;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if(buffer != NULL) {
|
||||
free(buffer);
|
||||
}
|
||||
*length = counter;
|
||||
return floats;
|
||||
}
|
||||
@@ -268,4 +540,64 @@ CGFloat degrees_to_radians( CGFloat degrees )
|
||||
return (point1.x * point2.y < point1.y * point2.x ? -1 : 1) * acosf(ratio(point1, point2));
|
||||
}
|
||||
|
||||
+ (CGPathRef)newFlippedCGPath:(CGPathRef)path
|
||||
{
|
||||
CGRect boundingBox = CGPathGetPathBoundingBox(path);
|
||||
CGAffineTransform scale = CGAffineTransformMakeScale(1.f, -1.f);
|
||||
CGAffineTransform translate = CGAffineTransformTranslate(scale, 0.f, boundingBox.size.height);
|
||||
CGPathRef transformPath = CGPathCreateCopyByTransformingPath(path, &translate);
|
||||
return transformPath;
|
||||
}
|
||||
|
||||
+ (CGPathRef)newCGPathFromBezierPath:(NSBezierPath *)bezPath
|
||||
{
|
||||
CGPathRef immutablePath = NULL;
|
||||
// Then draw the path elements.
|
||||
NSInteger numElements = bezPath.elementCount;
|
||||
if (numElements > 0) {
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
NSPoint points[3];
|
||||
BOOL didClosePath = YES;
|
||||
|
||||
for (NSInteger i = 0; i < numElements; i++) {
|
||||
switch ([bezPath elementAtIndex:i associatedPoints:points]) {
|
||||
case NSMoveToBezierPathElement: {
|
||||
CGPathMoveToPoint(path, NULL, points[0].x, points[0].y);
|
||||
break;
|
||||
}
|
||||
|
||||
case NSLineToBezierPathElement: {
|
||||
CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y);
|
||||
didClosePath = NO;
|
||||
break;
|
||||
}
|
||||
|
||||
case NSCurveToBezierPathElement: {
|
||||
CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y,
|
||||
points[1].x, points[1].y,
|
||||
points[2].x, points[2].y);
|
||||
didClosePath = NO;
|
||||
break;
|
||||
}
|
||||
|
||||
case NSClosePathBezierPathElement: {
|
||||
CGPathCloseSubpath(path);
|
||||
didClosePath = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Be sure the path is closed or Quartz may not do valid hit detection.
|
||||
if (didClosePath == NO) {
|
||||
CGPathCloseSubpath(path);
|
||||
}
|
||||
|
||||
// memory clean
|
||||
immutablePath = CGPathCreateCopy(path);
|
||||
CGPathRelease(path);
|
||||
}
|
||||
return immutablePath;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// IJSVGView.h
|
||||
// IconJar
|
||||
//
|
||||
// Created by Curtis Hard on 04/04/2017.
|
||||
// Copyright © 2017 Curtis Hard. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "IJSVG.h"
|
||||
|
||||
IB_DESIGNABLE
|
||||
@interface IJSVGView : NSView {
|
||||
IBInspectable NSString * imageName;
|
||||
IBInspectable NSColor * tintColor;
|
||||
|
||||
IJSVG * SVG;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) IJSVG * SVG;
|
||||
|
||||
+ (IJSVGView *)viewWithSVGNamed:(NSString *)name;
|
||||
- (id)initWithSVG:(IJSVG *)anSvg;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// IJSVGView.m
|
||||
// IconJar
|
||||
//
|
||||
// Created by Curtis Hard on 04/04/2017.
|
||||
// Copyright © 2017 Curtis Hard. All rights reserved.
|
||||
//
|
||||
|
||||
#import "IJSVGView.h"
|
||||
|
||||
@implementation IJSVGView
|
||||
|
||||
@synthesize SVG;
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// make sure we call this, or block may get called for a view
|
||||
// that doesnt exist
|
||||
[SVG prepForDrawingInView:nil];
|
||||
[imageName release], imageName = nil;
|
||||
[SVG release], SVG = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
+ (IJSVGView *)viewWithSVGNamed:(NSString *)name
|
||||
{
|
||||
IJSVG * anSVG = [IJSVG svgNamed:name];
|
||||
return [[[self alloc] initWithSVG:anSVG] autorelease];
|
||||
}
|
||||
|
||||
- (id)initWithSVG:(IJSVG *)anSvg
|
||||
{
|
||||
if((self = [super init]) != nil) {
|
||||
self.SVG = anSvg;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
// image was set via IB
|
||||
if(imageName != nil) {
|
||||
IJSVG * anSVG = [IJSVG svgNamed:imageName];
|
||||
if(tintColor != nil) {
|
||||
anSVG.fillColor = tintColor;
|
||||
}
|
||||
self.SVG = anSVG;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSVG:(IJSVG *)anSVG
|
||||
{
|
||||
// memory clean
|
||||
if(SVG != nil) {
|
||||
[SVG release], SVG = nil;
|
||||
}
|
||||
SVG = [anSVG retain];
|
||||
|
||||
// redisplay ourself!
|
||||
[SVG prepForDrawingInView:self];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isFlipped
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect
|
||||
{
|
||||
// only draw if there is actually an SVG
|
||||
if(self.SVG == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
// draw the svg
|
||||
[self.SVG drawInRect:self.bounds];
|
||||
}
|
||||
|
||||
@end
|
||||