diff --git a/Macaw.xcodeproj/project.pbxproj b/Macaw.xcodeproj/project.pbxproj index 8eaf5d54..b251699c 100644 --- a/Macaw.xcodeproj/project.pbxproj +++ b/Macaw.xcodeproj/project.pbxproj @@ -670,6 +670,23 @@ A74C8326229EB77B0085A832 /* masking-path-13-f-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = A74C8325229EB77B0085A832 /* masking-path-13-f-manual.reference */; }; A74C832C229FB7690085A832 /* color-prop-04-t-manual-osx.svg in Resources */ = {isa = PBXBuildFile; fileRef = A74C832B229FB7690085A832 /* color-prop-04-t-manual-osx.svg */; }; A74C832E229FBA4C0085A832 /* color-prop-04-t-manual-osx.reference in Resources */ = {isa = PBXBuildFile; fileRef = A74C832D229FBA4C0085A832 /* color-prop-04-t-manual-osx.reference */; }; + A7632F6022B2500500803489 /* Direction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7632F5F22B2500500803489 /* Direction.swift */; }; + A7B4899322B3914D002D1D56 /* Direction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7632F5F22B2500500803489 /* Direction.swift */; }; + A7B4899922B3AAD7002D1D56 /* text-intro-09-b-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = A7B4899422B3AAD6002D1D56 /* text-intro-09-b-manual.svg */; }; + A7B4899A22B3AAD7002D1D56 /* text-intro-01-t-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = A7B4899522B3AAD6002D1D56 /* text-intro-01-t-manual.svg */; }; + A7B4899B22B3AAD7002D1D56 /* text-intro-04-t-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = A7B4899622B3AAD7002D1D56 /* text-intro-04-t-manual.svg */; }; + A7B4899C22B3AAD7002D1D56 /* text-intro-11-t-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = A7B4899722B3AAD7002D1D56 /* text-intro-11-t-manual.svg */; }; + A7B4899D22B3AAD7002D1D56 /* text-intro-02-b-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = A7B4899822B3AAD7002D1D56 /* text-intro-02-b-manual.svg */; }; + A7B489A322B3AAE1002D1D56 /* text-intro-11-t-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = A7B4899E22B3AAE0002D1D56 /* text-intro-11-t-manual.reference */; }; + A7B489A422B3AAE1002D1D56 /* text-intro-04-t-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = A7B4899F22B3AAE0002D1D56 /* text-intro-04-t-manual.reference */; }; + A7B489A522B3AAE1002D1D56 /* text-intro-01-t-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = A7B489A022B3AAE1002D1D56 /* text-intro-01-t-manual.reference */; }; + A7B489A622B3AAE1002D1D56 /* text-intro-09-b-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = A7B489A122B3AAE1002D1D56 /* text-intro-09-b-manual.reference */; }; + A7B489A722B3AAE1002D1D56 /* text-intro-02-b-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = A7B489A222B3AAE1002D1D56 /* text-intro-02-b-manual.reference */; }; + A7B489AD22B3AAF3002D1D56 /* text-intro-02-b-manual.png in Resources */ = {isa = PBXBuildFile; fileRef = A7B489A822B3AAF2002D1D56 /* text-intro-02-b-manual.png */; }; + A7B489AE22B3AAF3002D1D56 /* text-intro-09-b-manual.png in Resources */ = {isa = PBXBuildFile; fileRef = A7B489A922B3AAF3002D1D56 /* text-intro-09-b-manual.png */; }; + A7B489AF22B3AAF3002D1D56 /* text-intro-04-t-manual.png in Resources */ = {isa = PBXBuildFile; fileRef = A7B489AA22B3AAF3002D1D56 /* text-intro-04-t-manual.png */; }; + A7B489B022B3AAF3002D1D56 /* text-intro-11-t-manual.png in Resources */ = {isa = PBXBuildFile; fileRef = A7B489AB22B3AAF3002D1D56 /* text-intro-11-t-manual.png */; }; + A7B489B122B3AAF3002D1D56 /* text-intro-01-t-manual.png in Resources */ = {isa = PBXBuildFile; fileRef = A7B489AC22B3AAF3002D1D56 /* text-intro-01-t-manual.png */; }; A7E675561EC4213500BD9ECB /* NodeBoundsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7E675551EC4213500BD9ECB /* NodeBoundsTests.swift */; }; C410148E1F834D290022EE44 /* style.svg in Resources */ = {isa = PBXBuildFile; fileRef = C410148D1F834D280022EE44 /* style.svg */; }; C4153A8F1F8793DE001BA5EE /* small-logo.png in Resources */ = {isa = PBXBuildFile; fileRef = C4153A8E1F8793DD001BA5EE /* small-logo.png */; }; @@ -1248,6 +1265,22 @@ A74C8325229EB77B0085A832 /* masking-path-13-f-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "masking-path-13-f-manual.reference"; sourceTree = ""; }; A74C832B229FB7690085A832 /* color-prop-04-t-manual-osx.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "color-prop-04-t-manual-osx.svg"; sourceTree = ""; }; A74C832D229FBA4C0085A832 /* color-prop-04-t-manual-osx.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "color-prop-04-t-manual-osx.reference"; sourceTree = ""; }; + A7632F5F22B2500500803489 /* Direction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Direction.swift; sourceTree = ""; }; + A7B4899422B3AAD6002D1D56 /* text-intro-09-b-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "text-intro-09-b-manual.svg"; sourceTree = ""; }; + A7B4899522B3AAD6002D1D56 /* text-intro-01-t-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "text-intro-01-t-manual.svg"; sourceTree = ""; }; + A7B4899622B3AAD7002D1D56 /* text-intro-04-t-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "text-intro-04-t-manual.svg"; sourceTree = ""; }; + A7B4899722B3AAD7002D1D56 /* text-intro-11-t-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "text-intro-11-t-manual.svg"; sourceTree = ""; }; + A7B4899822B3AAD7002D1D56 /* text-intro-02-b-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "text-intro-02-b-manual.svg"; sourceTree = ""; }; + A7B4899E22B3AAE0002D1D56 /* text-intro-11-t-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "text-intro-11-t-manual.reference"; sourceTree = ""; }; + A7B4899F22B3AAE0002D1D56 /* text-intro-04-t-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "text-intro-04-t-manual.reference"; sourceTree = ""; }; + A7B489A022B3AAE1002D1D56 /* text-intro-01-t-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "text-intro-01-t-manual.reference"; sourceTree = ""; }; + A7B489A122B3AAE1002D1D56 /* text-intro-09-b-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "text-intro-09-b-manual.reference"; sourceTree = ""; }; + A7B489A222B3AAE1002D1D56 /* text-intro-02-b-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "text-intro-02-b-manual.reference"; sourceTree = ""; }; + A7B489A822B3AAF2002D1D56 /* text-intro-02-b-manual.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "text-intro-02-b-manual.png"; sourceTree = ""; }; + A7B489A922B3AAF3002D1D56 /* text-intro-09-b-manual.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "text-intro-09-b-manual.png"; sourceTree = ""; }; + A7B489AA22B3AAF3002D1D56 /* text-intro-04-t-manual.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "text-intro-04-t-manual.png"; sourceTree = ""; }; + A7B489AB22B3AAF3002D1D56 /* text-intro-11-t-manual.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "text-intro-11-t-manual.png"; sourceTree = ""; }; + A7B489AC22B3AAF3002D1D56 /* text-intro-01-t-manual.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "text-intro-01-t-manual.png"; sourceTree = ""; }; A7E675551EC4213500BD9ECB /* NodeBoundsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NodeBoundsTests.swift; path = Bounds/NodeBoundsTests.swift; sourceTree = ""; }; C410148D1F834D280022EE44 /* style.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = style.svg; sourceTree = ""; }; C4153A8E1F8793DD001BA5EE /* small-logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "small-logo.png"; sourceTree = ""; }; @@ -1410,6 +1443,11 @@ 4295954322A0F7CE00869079 /* shapes-rect-06-f-manual.png */, 4295954422A0F7CE00869079 /* shapes-polyline-02-t-manual.png */, 4295954522A0F7CE00869079 /* shapes-polygon-01-t-manual.png */, + A7B489AC22B3AAF3002D1D56 /* text-intro-01-t-manual.png */, + A7B489A822B3AAF2002D1D56 /* text-intro-02-b-manual.png */, + A7B489AA22B3AAF3002D1D56 /* text-intro-04-t-manual.png */, + A7B489A922B3AAF3002D1D56 /* text-intro-09-b-manual.png */, + A7B489AB22B3AAF3002D1D56 /* text-intro-11-t-manual.png */, ); path = png; sourceTree = ""; @@ -1642,6 +1680,7 @@ 5B6E191F20AC58F900454E7E /* Color.swift */, 5874CCB620DA8A860090DBD5 /* ColorMatrix.swift */, 5BFEF5CD20B80A83008DAC11 /* ColorMatrixEffect.swift */, + A7632F5F22B2500500803489 /* Direction.swift */, 5B6E191B20AC58F800454E7E /* Drawable.swift */, 5B6E191C20AC58F800454E7E /* Effect.swift */, 5B6E191D20AC58F900454E7E /* Fill.swift */, @@ -2049,6 +2088,16 @@ 5B1AE1F820B6A669007EECCB /* text-fonts-01-t-manual.svg */, 5B1AE21520B6A669007EECCB /* text-fonts-02-t-manual.reference */, 5B1AE1DA20B6A669007EECCB /* text-fonts-02-t-manual.svg */, + A7B489A022B3AAE1002D1D56 /* text-intro-01-t-manual.reference */, + A7B4899522B3AAD6002D1D56 /* text-intro-01-t-manual.svg */, + A7B489A222B3AAE1002D1D56 /* text-intro-02-b-manual.reference */, + A7B4899822B3AAD7002D1D56 /* text-intro-02-b-manual.svg */, + A7B4899F22B3AAE0002D1D56 /* text-intro-04-t-manual.reference */, + A7B4899622B3AAD7002D1D56 /* text-intro-04-t-manual.svg */, + A7B489A122B3AAE1002D1D56 /* text-intro-09-b-manual.reference */, + A7B4899422B3AAD6002D1D56 /* text-intro-09-b-manual.svg */, + A7B4899E22B3AAE0002D1D56 /* text-intro-11-t-manual.reference */, + A7B4899722B3AAD7002D1D56 /* text-intro-11-t-manual.svg */, 5B1AE1EF20B6A669007EECCB /* types-basic-01-f-manual.reference */, 5B1AE20420B6A669007EECCB /* types-basic-01-f-manual.svg */, ); @@ -2267,6 +2316,7 @@ 4295957B22A0F7CE00869079 /* struct-defs-01-t-manual.png in Resources */, A74C81ED229284790085A832 /* struct-use-01-t-manual.svg in Resources */, 5B1AE23620B6A669007EECCB /* paths-data-07-t-manual.svg in Resources */, + A7B4899B22B3AAD7002D1D56 /* text-intro-04-t-manual.svg in Resources */, 5B1AE27720B6A669007EECCB /* coords-trans-03-t-manual.reference in Resources */, 5B1AE26420B6A669007EECCB /* painting-fill-03-t-manual.svg in Resources */, 5BAEA9C9206CEAA20049AAAE /* viewBox.svg in Resources */, @@ -2291,6 +2341,7 @@ 5B1AE2B820B6A669007EECCB /* painting-stroke-03-t-manual.reference in Resources */, 4269F43E2257265800D91393 /* color-prop-04-t-manual.reference in Resources */, 4295957522A0F7CE00869079 /* color-prop-01-b-manual.png in Resources */, + A7B489A522B3AAE1002D1D56 /* text-intro-01-t-manual.reference in Resources */, 5B1AE2C720B6A669007EECCB /* paths-data-02-t-manual.svg in Resources */, A74C823C2297DB2C0085A832 /* shapes-rect-07-f-manual.reference in Resources */, 5B1AE27120B6A669007EECCB /* painting-stroke-04-t-manual.reference in Resources */, @@ -2298,6 +2349,7 @@ A74C82E4229E35D30085A832 /* pservers-grad-22-b-manual.svg in Resources */, 5B1AE2C220B6A669007EECCB /* coords-trans-05-t-manual.svg in Resources */, 5B1AE29D20B6A669007EECCB /* types-basic-01-f-manual.reference in Resources */, + A7B4899922B3AAD7002D1D56 /* text-intro-09-b-manual.svg in Resources */, 429595AD22A0F7CE00869079 /* coords-transformattr-01-f-manual.png in Resources */, 4295955E22A0F7CE00869079 /* painting-fill-04-t-manual.png in Resources */, 5B1AE25820B6A669007EECCB /* paths-data-08-t-manual.reference in Resources */, @@ -2319,6 +2371,7 @@ 5B1AE2CA20B6A669007EECCB /* shapes-ellipse-01-t-manual.reference in Resources */, 5B1AE25420B6A669007EECCB /* paths-data-06-t-manual.svg in Resources */, 4295959622A0F7CE00869079 /* paths-data-16-t-manual.png in Resources */, + A7B489AE22B3AAF3002D1D56 /* text-intro-09-b-manual.png in Resources */, 4295955522A0F7CE00869079 /* struct-frag-01-t-manual.png in Resources */, 42DAF9AC22A612CF0095B936 /* color-prop-01-b-manual.svg in Resources */, 5B1AE26B20B6A669007EECCB /* shapes-polygon-02-t-manual.svg in Resources */, @@ -2363,6 +2416,7 @@ 5B1AE24820B6A669007EECCB /* coords-coord-01-t-manual.svg in Resources */, 4295955622A0F7CE00869079 /* struct-frag-06-t-manual.png in Resources */, 5B1AE2B620B6A669007EECCB /* paths-data-04-t-manual.svg in Resources */, + A7B4899C22B3AAD7002D1D56 /* text-intro-11-t-manual.svg in Resources */, 5B1AE25620B6A669007EECCB /* paths-data-05-t-manual.reference in Resources */, 42DB451522A52A4200091DC6 /* pservers-grad-09-b-manual.png in Resources */, 4295954C22A0F7CE00869079 /* coords-trans-10-f-manual.png in Resources */, @@ -2402,11 +2456,14 @@ 4295954722A0F7CE00869079 /* render-elems-03-t-manual.png in Resources */, 4295954822A0F7CE00869079 /* paths-data-03-f-manual.png in Resources */, 5B7E79DE20D2781A00C50BCF /* masking-intro-01-f-manual.reference in Resources */, + A7B489B022B3AAF3002D1D56 /* text-intro-11-t-manual.png in Resources */, 42DB451322A52A4200091DC6 /* masking-path-13-f-manual.png in Resources */, + A7B489B122B3AAF3002D1D56 /* text-intro-01-t-manual.png in Resources */, 5B1AE28A20B6A669007EECCB /* shapes-polygon-01-t-manual.reference in Resources */, 4295955B22A0F7CE00869079 /* shapes-rect-05-f-manual.png in Resources */, 4295958D22A0F7CE00869079 /* coords-trans-02-t-manual.png in Resources */, 4295956C22A0F7CE00869079 /* pservers-grad-02-b-manual.png in Resources */, + A7B489A322B3AAE1002D1D56 /* text-intro-11-t-manual.reference in Resources */, 5B1AE27920B6A669007EECCB /* shapes-circle-01-t-manual.svg in Resources */, 5B1AE25E20B6A669007EECCB /* paths-data-17-f-manual.reference in Resources */, 5B1AE23F20B6A669007EECCB /* coords-trans-06-t-manual.svg in Resources */, @@ -2444,6 +2501,7 @@ 4295957F22A0F7CE00869079 /* metadata-example-01-t-manual.png in Resources */, 5B1AE2D320B6A669007EECCB /* shapes-rect-04-f-manual.reference in Resources */, 5B1AE23C20B6A669007EECCB /* paths-data-15-t-manual.svg in Resources */, + A7B489AF22B3AAF3002D1D56 /* text-intro-04-t-manual.png in Resources */, 5BAE2039208E163D006BF277 /* polygon.reference in Resources */, 5B1AE27220B6A669007EECCB /* struct-frag-06-t-manual.svg in Resources */, 421C66502225196900DD73F5 /* color-prop-04-t-manual.svg in Resources */, @@ -2470,6 +2528,7 @@ 5B7E79C420CA7F1B00C50BCF /* pservers-grad-03-b-manual.svg in Resources */, 5B1AE27F20B6A669007EECCB /* painting-stroke-07-t-manual.reference in Resources */, 4295956622A0F7CE00869079 /* coords-trans-07-t-manual.png in Resources */, + A7B4899D22B3AAD7002D1D56 /* text-intro-02-b-manual.svg in Resources */, 4295956A22A0F7CE00869079 /* paths-data-14-t-manual.png in Resources */, 5B1AE23320B6A669007EECCB /* paths-data-06-t-manual.reference in Resources */, 5B1AE2C820B6A669007EECCB /* paths-data-19-f-manual.svg in Resources */, @@ -2486,6 +2545,7 @@ A74C832E229FBA4C0085A832 /* color-prop-04-t-manual-osx.reference in Resources */, 4295956822A0F7CE00869079 /* paths-data-06-t-manual.png in Resources */, 5B1AE28720B6A669007EECCB /* render-elems-03-t-manual.svg in Resources */, + A7B489AD22B3AAF3002D1D56 /* text-intro-02-b-manual.png in Resources */, 4295956522A0F7CE00869079 /* painting-stroke-01-t-manual.png in Resources */, 5B1AE2AD20B6A669007EECCB /* painting-stroke-05-t-manual.reference in Resources */, 5B1AE25920B6A669007EECCB /* coords-trans-01-b-manual.svg in Resources */, @@ -2494,6 +2554,7 @@ 5BFEF5D720BC1C1F008DAC11 /* paths-data-18-f-manual.reference in Resources */, 5BAE203F208E163D006BF277 /* transform.reference in Resources */, 5B1AE24920B6A669007EECCB /* painting-control-06-f-manual.svg in Resources */, + A7B489A722B3AAE1002D1D56 /* text-intro-02-b-manual.reference in Resources */, 5B1AE2B320B6A669007EECCB /* coords-trans-04-t-manual.reference in Resources */, 5BAE2038208E163D006BF277 /* polyline.reference in Resources */, 5B37139C20BE95D7004BB6EE /* pservers-grad-01-b-manual.reference in Resources */, @@ -2514,6 +2575,7 @@ 5B1AE29F20B6A669007EECCB /* painting-stroke-09-t-manual.svg in Resources */, 429595A222A0F7CE00869079 /* coords-trans-08-t-manual.png in Resources */, 5B1AE27D20B6A669007EECCB /* shapes-polygon-03-t-manual.svg in Resources */, + A7B489A622B3AAE1002D1D56 /* text-intro-09-b-manual.reference in Resources */, 4295958322A0F7CE00869079 /* pservers-grad-01-b-manual.png in Resources */, 5B1AE26020B6A669007EECCB /* shapes-polygon-02-t-manual.reference in Resources */, 4295959022A0F7CE00869079 /* painting-control-02-f-manual.png in Resources */, @@ -2563,9 +2625,11 @@ 4295959722A0F7CE00869079 /* color-prop-03-t-manual.png in Resources */, 5B1AE24F20B6A669007EECCB /* painting-stroke-02-t-manual.reference in Resources */, 4295958C22A0F7CE00869079 /* shapes-rect-03-t-manual.png in Resources */, + A7B489A422B3AAE1002D1D56 /* text-intro-04-t-manual.reference in Resources */, 4295958A22A0F7CE00869079 /* paths-data-05-t-manual.png in Resources */, 4295958B22A0F7CE00869079 /* coords-trans-05-t-manual.png in Resources */, 4295956322A0F7CE00869079 /* painting-stroke-06-t-manual.png in Resources */, + A7B4899A22B3AAD7002D1D56 /* text-intro-01-t-manual.svg in Resources */, 5B1AE28420B6A669007EECCB /* shapes-polyline-01-t-manual.reference in Resources */, 4295957C22A0F7CE00869079 /* painting-control-03-f-manual.png in Resources */, 42DB450E22A52A4200091DC6 /* pservers-grad-24-f-manual.png in Resources */, @@ -2729,6 +2793,7 @@ 57614B2A1F83D15600875933 /* Polygon.swift in Sources */, 57614B2D1F83D15600875933 /* TransformAnimation.swift in Sources */, 57614B2E1F83D15600875933 /* CombineAnimation.swift in Sources */, + A7B4899322B3914D002D1D56 /* Direction.swift in Sources */, 57614B2F1F83D15600875933 /* TransformHashable.swift in Sources */, 57614B301F83D15600875933 /* MoveTo.swift in Sources */, 5B7E79C120CA7E9300C50BCF /* Pattern.swift in Sources */, @@ -2870,6 +2935,7 @@ 57E5E16C1E3B393900D1CB28 /* CombineAnimation.swift in Sources */, 57E5E1661E3B393900D1CB28 /* TransformHashable.swift in Sources */, 57E5E1921E3B393900D1CB28 /* MoveTo.swift in Sources */, + A7632F6022B2500500803489 /* Direction.swift in Sources */, 5B7E79C020CA7E9300C50BCF /* Pattern.swift in Sources */, 57E5E1A51E3B393900D1CB28 /* NodeRenderer.swift in Sources */, 5B6E193920AC58F900454E7E /* Drawable.swift in Sources */, diff --git a/MacawTests/MacawSVGTests.swift b/MacawTests/MacawSVGTests.swift index fb22ef30..cdeb0c70 100644 --- a/MacawTests/MacawSVGTests.swift +++ b/MacawTests/MacawSVGTests.swift @@ -19,7 +19,7 @@ class MacawSVGTests: XCTestCase { private let testFolderName = "MacawTestOutputData" private let shouldComparePNGImages = true - private let multipleTestsWillRun = false + private let multipleTestsWillRun = true private let shouldSaveFaildedTestImage = false override func setUp() { @@ -814,6 +814,26 @@ class MacawSVGTests: XCTestCase { validateJSON("masking-mask-02-f-manual") } + func testTextIntro01() { + validateJSON("text-intro-01-t-manual") + } + + func testTextIntro02() { + validateJSON("text-intro-02-b-manual") + } + + func testTextIntro04() { + validateJSON("text-intro-04-t-manual") + } + + func testTextIntro09() { + validateJSON("text-intro-09-b-manual") + } + + func testTextIntro11() { + validateJSON("text-intro-11-t-manual") + } + func getImage(from svgName: String) -> MImage { let bundle = Bundle(for: type(of: TestUtils())) do { diff --git a/MacawTests/SceneSerialization.swift b/MacawTests/SceneSerialization.swift index 1c914c58..1ed212c4 100644 --- a/MacawTests/SceneSerialization.swift +++ b/MacawTests/SceneSerialization.swift @@ -76,6 +76,9 @@ class NodeSerializer { if let baselineString = dictionary["baseline"] as? String { text.baseline = baselineForString(baselineString) } + if let directionString = dictionary["direction"] as? String{ + text.direction = directionForString(directionString) + } return text } @@ -174,6 +177,9 @@ extension Text: Serializable { } result["align"] = align.toString() result["baseline"] = "\(baseline)" + if direction != .lre { + result["direction"] = "\(direction)" + } return result } } @@ -629,3 +635,13 @@ fileprivate func baselineForString(_ string: String) -> Baseline { default: return .top } } + +fileprivate func directionForString(_ string: String) -> Direction { + switch string { + case "lre": return .lre + case "lro": return .lro + case "rle": return .rle + case "rlo": return .rlo + default: return .lre + } +} diff --git a/MacawTests/png/text-intro-01-t-manual.png b/MacawTests/png/text-intro-01-t-manual.png new file mode 100644 index 00000000..29f2c523 Binary files /dev/null and b/MacawTests/png/text-intro-01-t-manual.png differ diff --git a/MacawTests/png/text-intro-02-b-manual.png b/MacawTests/png/text-intro-02-b-manual.png new file mode 100644 index 00000000..7d3213b9 Binary files /dev/null and b/MacawTests/png/text-intro-02-b-manual.png differ diff --git a/MacawTests/png/text-intro-04-t-manual.png b/MacawTests/png/text-intro-04-t-manual.png new file mode 100644 index 00000000..5be3f4ce Binary files /dev/null and b/MacawTests/png/text-intro-04-t-manual.png differ diff --git a/MacawTests/png/text-intro-09-b-manual.png b/MacawTests/png/text-intro-09-b-manual.png new file mode 100644 index 00000000..60083c4f Binary files /dev/null and b/MacawTests/png/text-intro-09-b-manual.png differ diff --git a/MacawTests/png/text-intro-11-t-manual.png b/MacawTests/png/text-intro-11-t-manual.png new file mode 100644 index 00000000..e64e1f3e Binary files /dev/null and b/MacawTests/png/text-intro-11-t-manual.png differ diff --git a/MacawTests/w3c-test-suite.md b/MacawTests/w3c-test-suite.md index 55f40ea9..c97329a5 100644 --- a/MacawTests/w3c-test-suite.md +++ b/MacawTests/w3c-test-suite.md @@ -10,12 +10,12 @@ There are [521 SVG tests](https://github.com/web-platform-tests/wpt/tree/master/ * [switch/object](https://www.w3.org/TR/SVG11/backward.html) (7) The rest 306 tests can be split into following categories: -* 38.6% passed (118) +* 40.2% passed (123) * 14.1% filters (43) [#390](https://github.com/exyte/Macaw/issues/390) * 8.5% images (26) [wpt issue](https://github.com/web-platform-tests/wpt/issues/11178) * 2.6% markers (8) [#392](https://github.com/exyte/Macaw/issues/392) -* 20.6% text (63) [#391](https://github.com/exyte/Macaw/issues/391) -* 15.7% blocked by issues (48) +* 19.0% text (58) [#391](https://github.com/exyte/Macaw/issues/391) +* 14.1% blocked by issues (43) Status of each test: @@ -289,23 +289,23 @@ Status of each test: |[text-bidi-01-t-manual](w3cSVGTests/text-bidi-01-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | |[text-deco-01-b-manual](w3cSVGTests/text-deco-01-b-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | |[text-fonts-01-t-manual](w3cSVGTests/text-fonts-01-t-manual.svg) | ✅ | -|[text-fonts-02-t-manual](w3cSVGTests/text-fonts-02-t-manual.svg) | ✅ | +|[text-fonts-02-t-manual](w3cSVGTests/text-fonts-02-t-manual.svg) | ✅ | |[text-fonts-03-t-manual](w3cSVGTests/text-fonts-03-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | |[text-fonts-04-t-manual](w3cSVGTests/text-fonts-04-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | |[text-fonts-05-f-manual](w3cSVGTests/text-fonts-05-f-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | |[text-fonts-202-t-manual](w3cSVGTests/text-fonts-202-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | |[text-fonts-203-t-manual](w3cSVGTests/text-fonts-203-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | |[text-fonts-204-t-manual](w3cSVGTests/text-fonts-204-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | -|[text-intro-01-t-manual](w3cSVGTests/text-intro-01-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | -|[text-intro-02-b-manual](w3cSVGTests/text-intro-02-b-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | +|[text-intro-01-t-manual](w3cSVGTests/text-intro-01-t-manual.svg) | ✅ | +|[text-intro-02-b-manual](w3cSVGTests/text-intro-02-b-manual.svg) | ✅ | |[text-intro-03-b-manual](w3cSVGTests/text-intro-03-b-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | -|[text-intro-04-t-manual](w3cSVGTests/text-intro-04-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | +|[text-intro-04-t-manual](w3cSVGTests/text-intro-04-t-manual.svg) | ✅ | |[text-intro-05-t-manual](w3cSVGTests/text-intro-05-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | |[text-intro-06-t-manual](w3cSVGTests/text-intro-06-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | |[text-intro-07-t-manual](w3cSVGTests/text-intro-07-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | -|[text-intro-09-b-manual](w3cSVGTests/text-intro-09-b-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | +|[text-intro-09-b-manual](w3cSVGTests/text-intro-09-b-manual.svg) | ✅ | |[text-intro-10-f-manual](w3cSVGTests/text-intro-10-f-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | -|[text-intro-11-t-manual](w3cSVGTests/text-intro-11-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | +|[text-intro-11-t-manual](w3cSVGTests/text-intro-11-t-manual.svg) | ✅ | |[text-intro-12-t-manual](w3cSVGTests/text-intro-12-t-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | |[text-path-01-b-manual](w3cSVGTests/text-path-01-b-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | |[text-path-02-b-manual](w3cSVGTests/text-path-02-b-manual.svg) | [#391](https://github.com/exyte/Macaw/issues/391) | diff --git a/MacawTests/w3cSVGTests/text-intro-01-t-manual.reference b/MacawTests/w3cSVGTests/text-intro-01-t-manual.reference new file mode 100644 index 00000000..5cbaf2d6 --- /dev/null +++ b/MacawTests/w3cSVGTests/text-intro-01-t-manual.reference @@ -0,0 +1,212 @@ +{ + "contents" : [ + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 255 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 40, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 5, 40", + "text" : "Test left-to-right text." + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "MissingInAction, Georgia, 'Arial Hebrew', 'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 75", + "text" : "Polish: Mogę jeść szkło, i mi ..." + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "MissingInAction, Georgia, 'Arial Hebrew', 'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 110", + "text" : "Russian: Я могу есть стекло, ..." + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "MissingInAction, Georgia, 'Arial Hebrew', 'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 145", + "text" : "Greek: Μπορώ να φάω ..." + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "MissingInAction, Georgia, 'Arial Hebrew', 'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 180", + "text" : "Hebrew: אני יכול לאכול זכוכית ... " + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "MissingInAction, Georgia, 'Arial Hebrew', 'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 215", + "text" : "Yiddish: איך קען עסן גלאָז און ..." + } + ], + "node" : "Group" + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "MissingInAction, 'MS Song', SimHei,'LucidaSansUnicode', 'Arial Unicode MS'", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 250", + "text" : "Chinese:我能吞下玻璃而不伤身体。" + } + ], + "node" : "Group" + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "MissingInAction, 'MS-Gothic','LucidaSansUnicode', 'Arial Unicode MS'", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 285", + "text" : "Japanese: 私はガラスを食べます。" + } + ], + "node" : "Group" + } + ], + "node" : "Group" + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 32, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 340", + "text" : "$Revision: 1.9 $" + } + ], + "node" : "Group" + }, + { + "form" : { + "h" : 358, + "type" : "Rect", + "w" : 478, + "x" : 1, + "y" : 1 + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 0 + }, + "join" : "miter", + "width" : 1 + } + } + ], + "layout" : { + "scalingMode" : "meet", + "svgSize" : { + "height" : "100.0%", + "width" : "100.0%" + }, + "viewBox" : { + "h" : 360, + "type" : "Rect", + "w" : 480, + "x" : 0, + "y" : 0 + }, + "xAligningMode" : "mid", + "yAligningMode" : "mid" + }, + "node" : "Canvas" +} \ No newline at end of file diff --git a/MacawTests/w3cSVGTests/text-intro-01-t-manual.svg b/MacawTests/w3cSVGTests/text-intro-01-t-manual.svg new file mode 100644 index 00000000..6d28e0c5 --- /dev/null +++ b/MacawTests/w3cSVGTests/text-intro-01-t-manual.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + +

+ Test left-to-right aspect of internationalized text. +

+

+ Various text strings in various languages appear. The main + purpose of the test is to verify that the correct characters + appear and that they appear in the correct order and orientation, even + though the first choice font does not have the right glyphs. +

+ + +

+ Run the test. No interaction required. +

+
+ +

+ Correct rendering requires that each character is rendered. It may be rendered + with the 'missing glyph' if no + glyphs are found in the fonts listed in the content, or in any fallback font + that is available. The first choice font + is a special SVG font that only contains the 'missing glyph'. Missing glyph from + other fonts may conformantly be used, however. +

+

+ The test is passed if the lines of text appear as follows: +

+
    +
  • Polish: Mogę jeść szkło, i mi ...
  • +
  • Russian: Я могу есть стекло, ...
  • +
  • Greek: Μπορώ να φάω ...
  • +
  • Hebrew: אני יכול לאכול זכוכית ...
  • +
  • Yiddish: איך קען עסן גלאָז און ...
  • +
  • Chinese:我能吞下玻璃而不伤身体。
  • +
  • Japanese: 私はガラスを食べます。
  • +
+
+ + $RCSfile: text-intro-01-t.svg,v $ + + + + + + + + + + + + + + + Test left-to-right text. + + Polish: Mogę jeść szkło, i mi ... + Russian: Я могу есть стекло, ... + Greek: Μπορώ να φάω ... + Hebrew: אני יכול לאכול זכוכית ... + Yiddish: איך קען עסן גלאָז און ... + + + Chinese:我能吞下玻璃而不伤身体。 + + + Japanese: 私はガラスを食べます。 + + + + $Revision: 1.9 $ + + + + + diff --git a/MacawTests/w3cSVGTests/text-intro-02-b-manual.reference b/MacawTests/w3cSVGTests/text-intro-02-b-manual.reference new file mode 100644 index 00000000..d76f062c --- /dev/null +++ b/MacawTests/w3cSVGTests/text-intro-02-b-manual.reference @@ -0,0 +1,183 @@ +{ + "contents" : [ + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 255 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 40", + "text" : "unicode-bidi=\"bidi-override\" direction=\"ltr\"." + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "direction" : "lro", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 18, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 80", + "text" : "Text \"אני יכול לאכול זכוכית וזה לא מזיק לי\" is in Hebrew" + } + ], + "node" : "Group" + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 255 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 140", + "text" : "unicode-bidi=\"bidi-override\" direction=\"rtl\"." + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "direction" : "rlo", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 18, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 180", + "text" : "Text \"אני יכול לאכול זכוכית וזה לא מזיק לי\" is in Hebrew" + } + ], + "node" : "Group" + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 255 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 240", + "text" : "unicode-bidi=\"normal\" direction=\"rtl\"." + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "direction" : "rle", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 18, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 280", + "text" : "Text \"אני יכול לאכול זכוכית וזה לא מזיק לי\" is in Hebrew" + } + ], + "node" : "Group" + } + ], + "node" : "Group" + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 32, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 340", + "text" : "$Revision: 1.10 $" + } + ], + "node" : "Group" + }, + { + "form" : { + "h" : 358, + "type" : "Rect", + "w" : 478, + "x" : 1, + "y" : 1 + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 0 + }, + "join" : "miter", + "width" : 1 + } + } + ], + "layout" : { + "scalingMode" : "meet", + "svgSize" : { + "height" : "100.0%", + "width" : "100.0%" + }, + "viewBox" : { + "h" : 360, + "type" : "Rect", + "w" : 480, + "x" : 0, + "y" : 0 + }, + "xAligningMode" : "mid", + "yAligningMode" : "mid" + }, + "node" : "Canvas" +} \ No newline at end of file diff --git a/MacawTests/w3cSVGTests/text-intro-02-b-manual.svg b/MacawTests/w3cSVGTests/text-intro-02-b-manual.svg new file mode 100644 index 00000000..4e2946d5 --- /dev/null +++ b/MacawTests/w3cSVGTests/text-intro-02-b-manual.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + +

+ Test various aspects of internationalized text, including + left-to-right, right-to-left, and the + following properties: 'writing-mode', + 'direction' and 'unicode-bidi'. +

+

+ Various text strings in various languages appear. Ttest of bidi algorithms and support of 'unicode-bidi' and + 'direction' properties. +

+ + +

+ This test requires installation of a system font that supports + the various international characters used in this test case. A + suitable font should be used by the SVG renderer if none of the + specified font families are available (or if they are available but do + not have the required glyphs). +

+

+ Run the test. No interaction required. +

+
+ +

+ The test is passed if the correct characters + appear and they appear in the correct order and orientation. + Ensure that the three lines with Hebrew are ordered + correctly, as shown in the reference image. +

+
+ + $RCSfile: text-intro-02-b.svg,v $ + + + + + + + + + unicode-bidi="bidi-override" direction="ltr". + + Text "אני יכול לאכול זכוכית וזה לא מזיק לי" is in Hebrew + + unicode-bidi="bidi-override" direction="rtl". + + Text "אני יכול לאכול זכוכית וזה לא מזיק לי" is in Hebrew + + unicode-bidi="normal" direction="rtl". + + Text "אני יכול לאכול זכוכית וזה לא מזיק לי" is in Hebrew + + + + $Revision: 1.10 $ + + + + + diff --git a/MacawTests/w3cSVGTests/text-intro-04-t-manual.reference b/MacawTests/w3cSVGTests/text-intro-04-t-manual.reference new file mode 100644 index 00000000..5d49f084 --- /dev/null +++ b/MacawTests/w3cSVGTests/text-intro-04-t-manual.reference @@ -0,0 +1,202 @@ +{ + "contents" : [ + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 255 + }, + "font" : { + "name" : "Arial", + "size" : 16, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 20", + "text" : "Test horizontal text." + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "'Arial Hebrew', 'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 60", + "text" : "Polish: Mogę jeść szkło, i mi nie szkodzi." + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "'Arial Hebrew', 'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 90", + "text" : "Russian: Я могу есть стекло, это мне не вредит." + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "'Arial Hebrew', 'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 15, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 120", + "text" : "Greek: Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα." + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "'Arial Hebrew', 'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 150", + "text" : "Text \"אני יכול לאכול זכוכית וזה לא מזיק לי\" is in Hebrew" + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "'Arial Hebrew', 'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 180", + "text" : "Yiddish: איך קען עסן גלאָז און עס טוט מיר נישט װײ." + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "'Arial Hebrew', 'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 210", + "text" : "Chinese:我能吞下玻璃而不伤身体。" + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "'Arial Hebrew', 'Arial Unicode MS', 'LucidaSansUnicode','MS-Gothic'", + "size" : 15, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 240", + "text" : "Japanese: 私はガラスを食べられます。それは私を傷つけません。" + } + ], + "node" : "Group" + } + ], + "node" : "Group" + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 32, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 340", + "text" : "$Revision: 1.7 $" + } + ], + "node" : "Group" + }, + { + "form" : { + "h" : 358, + "type" : "Rect", + "w" : 478, + "x" : 1, + "y" : 1 + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 0 + }, + "join" : "miter", + "width" : 1 + } + } + ], + "layout" : { + "scalingMode" : "meet", + "svgSize" : { + "height" : "100.0%", + "width" : "100.0%" + }, + "viewBox" : { + "h" : 360, + "type" : "Rect", + "w" : 480, + "x" : 0, + "y" : 0 + }, + "xAligningMode" : "mid", + "yAligningMode" : "mid" + }, + "node" : "Canvas" +} \ No newline at end of file diff --git a/MacawTests/w3cSVGTests/text-intro-04-t-manual.svg b/MacawTests/w3cSVGTests/text-intro-04-t-manual.svg new file mode 100644 index 00000000..565808ba --- /dev/null +++ b/MacawTests/w3cSVGTests/text-intro-04-t-manual.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + +

+ Test basic aspect of internationalized text. +

+

+ Various text strings in various languages appear. The main + purpose of the test is to verify that the correct characters + appear and that they appear in the correct order and orientation. +

+

+ A future version of this test + might include all necessary glyphs as an SVG font. +

+ + +

+ Run the test. No interaction required. +

+
+ +

+ Correct rendering requires that each character is rendered. It is not required that a given character + be rendered with any particular font; just that it is rendered. + It may be rendered with the 'missing glyph' if no + glyphs are found in the fonts listed in the content, or in any fallback font that is available. +

+
+ + $RCSfile: text-intro-04-t.svg,v $ + + + + + + + + + Test horizontal text. + + Polish: Mogę jeść szkło, i mi nie szkodzi. + Russian: Я могу есть стекло, это мне не вредит. + Greek: Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα. + + Text "אני יכול לאכול זכוכית וזה לא מזיק לי" is in Hebrew + Yiddish: איך קען עסן גלאָז און עס טוט מיר נישט װײ. + Chinese:我能吞下玻璃而不伤身体。 + Japanese: 私はガラスを食べられます。それは私を傷つけません。 + + + + $Revision: 1.7 $ + + + + + diff --git a/MacawTests/w3cSVGTests/text-intro-09-b-manual.reference b/MacawTests/w3cSVGTests/text-intro-09-b-manual.reference new file mode 100644 index 00000000..4d50f2cf --- /dev/null +++ b/MacawTests/w3cSVGTests/text-intro-09-b-manual.reference @@ -0,0 +1,183 @@ +{ + "contents" : [ + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 255 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 40", + "text" : "unicode-bidi=\"bidi-override\" direction=\"ltr\"." + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "direction" : "lro", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGHebrew, 'Ezra SIL SR', serif", + "size" : 18, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 80", + "text" : "Text \"אני יכול לאכול זכוכית וזה לא מזיק לי\" is in Hebrew" + } + ], + "node" : "Group" + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 255 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 140", + "text" : "unicode-bidi=\"bidi-override\" direction=\"rtl\"." + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "direction" : "rlo", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGHebrew, 'Ezra SIL SR', serif", + "size" : 18, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 180", + "text" : "Text \"אני יכול לאכול זכוכית וזה לא מזיק לי\" is in Hebrew" + } + ], + "node" : "Group" + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 255 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 17, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 240", + "text" : "unicode-bidi=\"normal\" direction=\"rtl\"." + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "direction" : "rle", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGHebrew, 'Ezra SIL SR', serif", + "size" : 18, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 280", + "text" : "Text \"אני יכול לאכול זכוכית וזה לא מזיק לי\" is in Hebrew" + } + ], + "node" : "Group" + } + ], + "node" : "Group" + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 32, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 340", + "text" : "$Revision: 1.7 $" + } + ], + "node" : "Group" + }, + { + "form" : { + "h" : 358, + "type" : "Rect", + "w" : 478, + "x" : 1, + "y" : 1 + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 0 + }, + "join" : "miter", + "width" : 1 + } + } + ], + "layout" : { + "scalingMode" : "meet", + "svgSize" : { + "height" : "100.0%", + "width" : "100.0%" + }, + "viewBox" : { + "h" : 360, + "type" : "Rect", + "w" : 480, + "x" : 0, + "y" : 0 + }, + "xAligningMode" : "mid", + "yAligningMode" : "mid" + }, + "node" : "Canvas" +} \ No newline at end of file diff --git a/MacawTests/w3cSVGTests/text-intro-09-b-manual.svg b/MacawTests/w3cSVGTests/text-intro-09-b-manual.svg new file mode 100644 index 00000000..1a39e383 --- /dev/null +++ b/MacawTests/w3cSVGTests/text-intro-09-b-manual.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + +

+ Test various aspects of internationalized text, including + left-to-right, right-to-left, and the + following properties: 'writing-mode', + 'direction' and 'unicode-bidi'. +

+

+ Various text strings in various languages appear. Test of bidi algorithms and support of 'unicode-bidi' and + 'direction' properties. Uses Webfonts. +

+

+ This test uses Webfonts; both SVG and WOFF fonts are provided. +

+ + +

+ Run the test. No interaction required. Make sure scripting is enabled. +

+
+ +

+ The test is passed if the correct characters + appear and they appear in the correct order and orientation. + Ensure that the three lines with Hebrew are ordered + correctly, as shown in the reference image. +

+
+ + $RCSfile: text-intro-09-b.svg,v $ + + + + + + + + + + + + + + + unicode-bidi="bidi-override" direction="ltr". + + Text "אני יכול לאכול זכוכית וזה לא מזיק לי" is in Hebrew + + unicode-bidi="bidi-override" direction="rtl". + + Text "אני יכול לאכול זכוכית וזה לא מזיק לי" is in Hebrew + + unicode-bidi="normal" direction="rtl". + + Text "אני יכול לאכול זכוכית וזה לא מזיק לי" is in Hebrew + + + + $Revision: 1.7 $ + + + + + diff --git a/MacawTests/w3cSVGTests/text-intro-11-t-manual.reference b/MacawTests/w3cSVGTests/text-intro-11-t-manual.reference new file mode 100644 index 00000000..c219ccec --- /dev/null +++ b/MacawTests/w3cSVGTests/text-intro-11-t-manual.reference @@ -0,0 +1,419 @@ +{ + "contents" : [ + { + "contents" : [ + { + "contents" : [ + { + "fill" : { + "type" : "Color", + "val" : 0 + }, + "form" : { + "segments" : [ + { + "data" : [ + 120, + 10 + ], + "type" : "M" + }, + { + "data" : [ + 120, + 280 + ], + "type" : "L" + } + ], + "type" : "Path" + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 12303291 + }, + "join" : "miter", + "width" : 1 + } + }, + { + "fill" : { + "type" : "Color", + "val" : 0 + }, + "form" : { + "segments" : [ + { + "data" : [ + 240, + 10 + ], + "type" : "M" + }, + { + "data" : [ + 240, + 280 + ], + "type" : "L" + } + ], + "type" : "Path" + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 12303291 + }, + "join" : "miter", + "width" : 1 + } + }, + { + "fill" : { + "type" : "Color", + "val" : 0 + }, + "form" : { + "segments" : [ + { + "data" : [ + 360, + 10 + ], + "type" : "M" + }, + { + "data" : [ + 360, + 280 + ], + "type" : "L" + } + ], + "type" : "Path" + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 12303291 + }, + "join" : "miter", + "width" : 1 + } + }, + { + "fill" : { + "type" : "Color", + "val" : 0 + }, + "form" : { + "segments" : [ + { + "data" : [ + 10, + 120 + ], + "type" : "M" + }, + { + "data" : [ + 470, + 120 + ], + "type" : "L" + } + ], + "type" : "Path" + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 12303291 + }, + "join" : "miter", + "width" : 1 + } + }, + { + "fill" : { + "type" : "Color", + "val" : 0 + }, + "form" : { + "segments" : [ + { + "data" : [ + 10, + 240 + ], + "type" : "M" + }, + { + "data" : [ + 470, + 240 + ], + "type" : "L" + } + ], + "type" : "Path" + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 12303291 + }, + "join" : "miter", + "width" : 1 + } + }, + { + "align" : "mid", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 7798784 + }, + "font" : { + "name" : "Andalus,Diwani Letter,serif", + "size" : 60, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 240, 120", + "text" : "آلات" + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "Andalus,Diwani Letter,serif", + "size" : 60, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 360, 120", + "text" : "آلات" + }, + { + "align" : "max", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 153 + }, + "font" : { + "name" : "Andalus,Diwani Letter,serif", + "size" : 60, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 120, 120", + "text" : "آلات" + }, + { + "align" : "mid", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 7798784 + }, + "font" : { + "name" : "Andalus,Diwani Letter,serif", + "size" : 60, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 240, 240", + "text" : "ثلاثة" + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "Andalus,Diwani Letter,serif", + "size" : 60, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 360, 240", + "text" : "ثلاثة" + }, + { + "align" : "max", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 153 + }, + "font" : { + "name" : "Andalus,Diwani Letter,serif", + "size" : 60, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 120, 240", + "text" : "ثلاثة" + } + ], + "node" : "Group" + } + ], + "node" : "Group" + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 32, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 340", + "text" : "$Revision: 1.2 $" + } + ], + "node" : "Group" + }, + { + "form" : { + "h" : 358, + "type" : "Rect", + "w" : 478, + "x" : 1, + "y" : 1 + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 0 + }, + "join" : "miter", + "width" : 1 + } + }, + { + "contents" : [ + { + "fill" : { + "type" : "Color", + "val" : 16711680 + }, + "form" : { + "h" : 20, + "type" : "Rect", + "w" : 478, + "x" : 1, + "y" : 1 + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 0 + }, + "join" : "miter", + "width" : 1 + } + }, + { + "align" : "mid", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 16777215 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 20, + "weight" : "bold" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 240, 18", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 0 + }, + "join" : "miter", + "width" : 0.5 + }, + "text" : "DRAFT" + } + ], + "node" : "Group" + } + ], + "layout" : { + "scalingMode" : "meet", + "svgSize" : { + "height" : "100.0%", + "width" : "100.0%" + }, + "viewBox" : { + "h" : 360, + "type" : "Rect", + "w" : 480, + "x" : 0, + "y" : 0 + }, + "xAligningMode" : "mid", + "yAligningMode" : "mid" + }, + "node" : "Canvas" +} \ No newline at end of file diff --git a/MacawTests/w3cSVGTests/text-intro-11-t-manual.svg b/MacawTests/w3cSVGTests/text-intro-11-t-manual.svg new file mode 100644 index 00000000..d754b7e3 --- /dev/null +++ b/MacawTests/w3cSVGTests/text-intro-11-t-manual.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + +

+ This test ensures that mandatory ligatures in Arabic are displayed. + Three values for text-anchor are also tested; + middle, + start and + end. + This test uses platform fonts for rendering. +

+

+ There are two subtests. The first + requires an isolate lam-alef ligature and the second requires + a right-joining lam-alef ligature. +

+

+ The first subtest has the word for 'tools', آلات + 0622: آ ARABIC LETTER ALEF WITH MADDA ABOVE + 0644: ل ARABIC LETTER LAM + 0627: ا ARABIC LETTER ALEF + 062A: ت ARABIC LETTER TEH +

+

+ The second subtest has the word for 'three', ثلاثة + 062B: ث ARABIC LETTER THEH + 0644: ل ARABIC LETTER LAM + 0627: ا ARABIC LETTER ALEF + 062B: ث ARABIC LETTER THEH + 0629: ة ARABIC LETTER TEH MARBUTA +

+ + +

+ Run the test. No interaction required. +

+
+ +

+ The test is passed if the blue glyphs آ and ث touch the first vertical + line. The second vertical line falls in middle of the brown glyphs + inbetween آلا and ت and inbetween ثلا and ثة. The black glyphs ت and ة + touch the last vertical line. +

+
+ + $RCSfile: text-intro-11-t.svg,v $ + + + + + + + + + + + + + + + آلات + آلات + آلات + ثلاثة + ثلاثة + ثلاثة + + + + $Revision: 1.2 $ + + + + + + DRAFT + + diff --git a/Source/model/draw/Align.swift b/Source/model/draw/Align.swift index 2c278597..5798cf79 100644 --- a/Source/model/draw/Align.swift +++ b/Source/model/draw/Align.swift @@ -12,6 +12,9 @@ open class Align { return align(outer: size, inner: 0) } + open func reverse() -> Align { + return .max + } } private class MidAlign: Align { @@ -19,6 +22,10 @@ private class MidAlign: Align { override func align(outer: Double, inner: Double) -> Double { return (outer - inner) / 2 } + + override func reverse() -> Align { + return .mid + } } private class MaxAlign: Align { @@ -26,4 +33,8 @@ private class MaxAlign: Align { override func align(outer: Double, inner: Double) -> Double { return outer - inner } + + override func reverse() -> Align { + return .min + } } diff --git a/Source/model/draw/Direction.swift b/Source/model/draw/Direction.swift new file mode 100644 index 00000000..2b914dfa --- /dev/null +++ b/Source/model/draw/Direction.swift @@ -0,0 +1,40 @@ +import Foundation + +public enum Direction { + case lre + case rle + case lro + case rlo +} + +extension Direction { + + var attributedStringValue: NSArray { + switch self { + case .lre: + return [NSNumber(value: 0)] + case .rle: + return [NSNumber(value: 1)] + case .lro: + return [NSNumber(value: 2)] + case .rlo: + return [NSNumber(value: 3)] + } + } + + static func from(direction: String?, unicodebidi: String?) -> Direction { + let direction = direction ?? "ltr" + let unicodebidi = unicodebidi ?? "normal" + + switch (direction, unicodebidi) { + case ("ltr", "bidi-override"): + return .lro + case ("rtl", "bidi-override"): + return .rlo + case ("rtl", "normal"): + return .rle + default: + return .lre + } + } +} diff --git a/Source/model/scene/Text.swift b/Source/model/scene/Text.swift index 440225ab..1063878b 100644 --- a/Source/model/scene/Text.swift +++ b/Source/model/scene/Text.swift @@ -48,14 +48,27 @@ open class Text: Node { set(val) { kerningVar.value = val } } - public init(text: String, font: Font? = nil, fill: Fill? = Color.black, stroke: Stroke? = nil, align: Align = .min, baseline: Baseline = .top, kerning: Float = 0.0, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, mask: Node? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) { + public let directionVar: Variable + open var direction: Direction { + get { return directionVar.value } + set(val) { directionVar.value = val } + } + + public init(text: String, font: Font? = nil, fill: Fill? = Color.black, stroke: Stroke? = nil, align: Align = .min, baseline: Baseline = .top, kerning: Float = 0.0, direction: Direction = .lre, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, mask: Node? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) { self.textVar = Variable(text) self.fontVar = Variable(font) self.fillVar = Variable(fill) self.strokeVar = Variable(stroke) - self.alignVar = Variable(align) self.baselineVar = Variable(baseline) self.kerningVar = Variable(kerning) + self.directionVar = Variable(direction) + + if direction == .rle || direction == .rlo { + self.alignVar = Variable(align.reverse()) + } else { + self.alignVar = Variable(align) + } + super.init( place: place, opaque: opaque, @@ -71,7 +84,6 @@ open class Text: Node { override open var bounds: Rect { let font: MFont if let f = self.font { - if let customFont = RenderUtils.loadFont(name: f.name, size: f.size, weight: f.weight) { font = customFont } else { diff --git a/Source/render/TextRenderer.swift b/Source/render/TextRenderer.swift index 8883647a..db0222c4 100644 --- a/Source/render/TextRenderer.swift +++ b/Source/render/TextRenderer.swift @@ -70,6 +70,8 @@ class TextRenderer: NodeRenderer { attributes[NSAttributedString.Key.kern] = NSNumber(value: text.kerning) } + attributes[NSAttributedString.Key.writingDirection] = text.direction.attributedStringValue + if attributes.count > 1 { MGraphicsPushContext(context) message.draw(in: getBounds(font), withAttributes: attributes) @@ -141,6 +143,7 @@ class TextRenderer: NodeRenderer { if let stroke = text.stroke { textAttributes[NSAttributedString.Key.strokeWidth] = NSNumber(value: stroke.width) } + let textSize = NSString(string: text.text).size(withAttributes: textAttributes) return CGRect(x: calculateAlignmentOffset(text, font: font), y: calculateBaselineOffset(text, font: font), @@ -164,7 +167,8 @@ class TextRenderer: NodeRenderer { fileprivate func calculateAlignmentOffset(_ text: Text, font: MFont) -> CGFloat { let textAttributes = [ - NSAttributedString.Key.font: font + NSAttributedString.Key.font: font, + NSAttributedString.Key.writingDirection: text.direction.attributedStringValue ] let textSize = NSString(string: text.text).size(withAttributes: textAttributes) return -CGFloat(text.align.align(size: textSize.width.doubleValue)) @@ -172,13 +176,11 @@ class TextRenderer: NodeRenderer { fileprivate func getTextColor(_ fill: Fill) -> MColor { if let color = fill as? Color { - #if os(iOS) return MColor(cgColor: color.toCG()) #elseif os(OSX) return MColor(cgColor: color.toCG()) ?? .black #endif - } return MColor.black } diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 0310c786..f22bbb43 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -930,21 +930,21 @@ open class SVGParser { } fileprivate func anchorToAlign(_ textAnchor: String?) -> Align { - if let anchor = textAnchor { - if anchor == "middle" { - return .mid - } else if anchor == "end" { - return .max - } + switch textAnchor { + case "middle": + return .mid + case "end": + return .max + default: + return .min } - return Align.min } fileprivate func parseSimpleText(_ text: SWXMLHash.XMLElement, textAnchor: String?, fill: Fill?, stroke: Stroke?, opacity: Double, fontName: String?, fontSize: Int?, fontWeight: String?, pos: Transform = Transform()) -> Text? { let string = text.text let position = pos.move(dx: getDoubleValue(text, attribute: "x") ?? 0, dy: getDoubleValue(text, attribute: "y") ?? 0) - - return Text(text: string, font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), fill: fill, stroke: stroke, align: anchorToAlign(textAnchor), baseline: .bottom, place: position, opacity: opacity, tag: getTag(text)) + let direction = getDirection(text) + return Text(text: string, font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), fill: fill, stroke: stroke, align: anchorToAlign(textAnchor), baseline: .bottom, direction: direction, place: position, opacity: opacity, tag: getTag(text)) } // REFACTOR @@ -990,9 +990,7 @@ open class SVGParser { nextStringWhitespace = true } trimmedString = withWhitespace ? " \(trimmedString)" : trimmedString - let text = Text(text: trimmedString, font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), - fill: fill, stroke: stroke, align: anchorToAlign(textAnchor), baseline: .alphabetic, - place: Transform().move(dx: bounds.x + bounds.w, dy: bounds.y), opacity: opacity) + let text = Text(text: trimmedString, font: getFont(fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), fill: fill, stroke: stroke, align: anchorToAlign(textAnchor), baseline: .alphabetic, place: Transform().move(dx: bounds.x + bounds.w, dy: bounds.y), opacity: opacity) collection.append(text) if tagRange.location >= fullString.length { // leave recursion return collection @@ -1013,11 +1011,8 @@ open class SVGParser { let pos = getTspanPosition(element, bounds: bounds, previousCollectedTspan: previousCollectedTspan, withWhitespace: &shouldAddWhitespace) let text = shouldAddWhitespace ? " \(string)" : string let attributes = getStyleAttributes([:], element: element) - - return Text(text: text, font: getFont(attributes, fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), - fill: (attributes[SVGKeys.fill] != nil) ? getFillColor(attributes)! : fill, stroke: stroke ?? getStroke(attributes), - align: anchorToAlign(textAnchor ?? getTextAnchor(attributes)), baseline: .alphabetic, - place: pos, opacity: getOpacity(attributes), tag: getTag(element)) + let direction = getDirection(element) + return Text(text: text, font: getFont(attributes, fontName: fontName, fontWeight: fontWeight, fontSize: fontSize), fill: (attributes[SVGKeys.fill] != nil) ? getFillColor(attributes)! : fill, stroke: stroke ?? getStroke(attributes), align: anchorToAlign(textAnchor ?? getTextAnchor(attributes)), baseline: .alphabetic, direction: direction, place: pos, opacity: getOpacity(attributes), tag: getTag(element)) } fileprivate func getFont(_ attributes: [String: String] = [:], fontName: String?, fontWeight: String?, fontSize: Int?) -> Font { @@ -1027,6 +1022,12 @@ open class SVGParser { weight: getFontWeight(attributes) ?? fontWeight ?? "normal") } + private func getDirection(_ element: SWXMLHash.XMLElement) -> Direction { + let direction = element.allAttributes["direction"]?.text + let unicodebidi = element.allAttributes["unicode-bidi"]?.text + return Direction.from(direction: direction, unicodebidi: unicodebidi) + } + fileprivate func getTspanPosition(_ element: SWXMLHash.XMLElement, bounds: Rect, previousCollectedTspan: Node?, withWhitespace: inout Bool) -> Transform { var xPos: Double = bounds.x + bounds.w var yPos: Double = bounds.y @@ -1594,7 +1595,7 @@ open class SVGParser { return Shape(form: shape.form, fill: shape.fill, stroke: shape.stroke, place: pos, opaque: opaque, clip: clip, visible: visible, tag: tag) } if let text = referenceNode as? Text { - return Text(text: text.text, font: text.font, fill: text.fill, stroke: text.stroke, align: text.align, baseline: text.baseline, place: pos, opaque: opaque, clip: clip, visible: visible, tag: tag) + return Text(text: text.text, font: text.font, fill: text.fill, stroke: text.stroke, align: text.align, baseline: text.baseline, direction: text.direction, place: pos, opaque: opaque, clip: clip, visible: visible, tag: tag) } if let image = referenceNode as? Image { return Image(src: image.src, xAlign: image.xAlign, yAlign: image.yAlign, aspectRatio: image.aspectRatio, w: image.w, h: image.h, place: pos, opaque: opaque, clip: clip, visible: visible, tag: tag)