├── codecov.yml ├── Samples.bundle ├── invalid.svg ├── lines.svg ├── chart.svg ├── feather-activity.svg ├── simple.svg ├── units-cm.svg ├── path-close.svg ├── units.svg ├── key │ ├── key.svg │ ├── key-black.svg │ └── key-ultralight.svg ├── viewbox.svg ├── feather-airplay.svg ├── tabler-anchor.svg ├── spy-glass.svg ├── cjk-fallback.svg ├── dash.svg ├── stylesheet-multiple.svg ├── tabler-alien.svg ├── rect.svg ├── path.svg ├── doc.svg ├── rgba.svg ├── usa.svg ├── pattern-rotate.svg ├── gradient2.svg ├── ellipse.svg ├── amex.svg ├── display.svg ├── gradient-stroke.svg ├── use.svg ├── pattern.svg ├── pattern-bounding.svg ├── alert.svg ├── mask-composite.svg ├── abc.svg ├── blend.svg ├── stylesheet.svg ├── circle.svg ├── thats-no-moon.svg ├── gradient-radial.svg ├── gradient-gratification.svg ├── fill-rule2.svg ├── identity.svg ├── gradient-gratification-p3.svg ├── radialGradient.svg ├── stars.svg ├── linearGradient.svg ├── arc.svg ├── nested-svg.svg ├── curves.svg ├── transform.svg ├── symbol-test.svg ├── mask.svg ├── sun-horizon.svg ├── rings.svg ├── transform-path.svg ├── mask-composite-2.svg ├── base64.svg ├── mask-composite-3.svg ├── effigy.svg └── fill-rule.svg ├── SwiftDraw ├── Tests │ ├── Test.bundle │ │ ├── invalid.svg │ │ ├── lines.svg │ │ ├── chart.svg │ │ ├── key.svg │ │ ├── dash.svg │ │ ├── stylesheet.svg │ │ ├── gradient-gratification.svg │ │ ├── radialGradient.svg │ │ ├── linearGradient.svg │ │ ├── nested-svg.svg │ │ ├── curves.svg │ │ └── symbol-test.svg │ ├── Utilities │ │ ├── Bundle+Extensions.swift │ │ ├── NSBitmapImageRep+Extensions.swift │ │ ├── URL+DataTests.swift │ │ └── PairedSequenceTests.swift │ ├── LayerTree │ │ ├── LayerTree.Builder.ShapeTests.swift │ │ ├── LayerTree.Builder.Path.ArcTests.swift │ │ └── LayerTree.ImageTests.swift │ ├── Renderer │ │ └── Renderer.SVGTests.swift │ └── UIImage+SVGTests.swift └── Sources │ ├── CommandLine │ └── CommandLine.swift │ ├── SVG+UIKit.swift │ ├── LayerTree │ ├── LayerTree.Pattern.swift │ ├── LayerTree.Image.swift │ └── LayerTree.Builder.Text.swift │ ├── SVG+Insets.swift │ ├── Utilities │ ├── Stack.swift │ ├── CGImage+Mask.swift │ └── CGPattern+Closure.swift │ ├── Renderer │ └── Renderer.SFSymbol+CGPath.swift │ ├── SVGCache.swift │ ├── CanvasUIView.swift │ └── CanvasNSView.swift ├── Examples ├── Sources │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── ExampleApp.swift │ ├── Info.plist │ └── GalleryView.swift ├── Basic │ └── Basic.entitlements └── Basic.xcodeproj │ └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── docker-test.sh ├── .swiftpm ├── xcode │ ├── package.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── SwiftDraw.xcscheme └── SwiftDraw.xctestplan ├── SwiftDrawDOM.podspec.json ├── LICENSE.txt ├── SwiftDraw.podspec.json ├── DOM ├── Sources │ ├── DOM.Switch.swift │ ├── Parser.XML.Use.swift │ ├── DOM.Image.swift │ ├── URL+Fragment.swift │ ├── DOM.Filter.swift │ ├── Parser.XML.Image.swift │ ├── XML.Element.swift │ ├── DOM.Text.swift │ ├── DOM.SVG+Parse.swift │ ├── Parser.XML.Pattern.swift │ ├── TextOutputStream+StandardError.swift │ ├── DOM.Pattern.swift │ ├── Parser.XML.Text.swift │ ├── DOM.Use.swift │ ├── URL+Data.swift │ ├── Parser.XML.Filter.swift │ └── DOM.RadialGradient.swift └── Tests │ ├── Test.bundle │ ├── stylesheet.svg │ ├── radialGradient.svg │ ├── linearGradient.svg │ ├── nested-svg.svg │ └── curves.svg │ ├── Bundle+Extensions.swift │ ├── UseTests.swift │ ├── Parser.XML.TextTests.swift │ ├── Parser.XML.FilterTests.swift │ └── StyleTests.swift ├── CommandLine └── main.swift ├── Package.swift └── .gitignore /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "SwiftDrawTests" 3 | - "CommandLine" -------------------------------------------------------------------------------- /Samples.bundle/invalid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Test.bundle/invalid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Examples/Sources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /docker-test.sh: -------------------------------------------------------------------------------- 1 | docker run -it \ 2 | --rm \ 3 | --mount src="$(pwd)",target=/SwiftDraw,type=bind \ 4 | swift \ 5 | /usr/bin/swift test --package-path /SwiftDraw -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/Basic/Basic.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Samples.bundle/lines.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Examples/Basic.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Test.bundle/lines.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Samples.bundle/chart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Test.bundle/chart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Samples.bundle/feather-activity.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Samples.bundle/simple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Samples.bundle/units-cm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Examples/Basic.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Samples.bundle/path-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Samples.bundle/units.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples.bundle/key/key.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Samples.bundle/viewbox.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Samples.bundle/feather-airplay.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Samples.bundle/key/key-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Test.bundle/key.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Samples.bundle/key/key-ultralight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Samples.bundle/tabler-anchor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Samples.bundle/spy-glass.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Samples.bundle/cjk-fallback.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 敏捷的棕色狐狸跳过懒狗,窗前明月光照桌上旧书 4 | いろはにほへとちりぬるを わかよたれそ つねならむ 5 | 다람쥐 헌 쳇바퀴에 타고파, 한글도 잘 보이나요? 6 | 7 | -------------------------------------------------------------------------------- /Samples.bundle/dash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Test.bundle/dash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.swiftpm/SwiftDraw.xctestplan: -------------------------------------------------------------------------------- 1 | { 2 | "configurations" : [ 3 | { 4 | "id" : "5E425660-B3D3-4E55-A5EE-21100C436313", 5 | "name" : "Test Scheme Action", 6 | "options" : { 7 | 8 | } 9 | } 10 | ], 11 | "defaultOptions" : { 12 | "codeCoverage" : false 13 | }, 14 | "testTargets" : [ 15 | { 16 | "target" : { 17 | "containerPath" : "container:", 18 | "identifier" : "SwiftDrawTests", 19 | "name" : "SwiftDrawTests" 20 | } 21 | } 22 | ], 23 | "version" : 1 24 | } 25 | -------------------------------------------------------------------------------- /Samples.bundle/stylesheet-multiple.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Samples.bundle/tabler-alien.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Samples.bundle/rect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples.bundle/path.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Samples.bundle/doc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Samples.bundle/rgba.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Samples.bundle/usa.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SwiftDrawDOM.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SwiftDrawDOM", 3 | "version": "0.25.3", 4 | "summary": "A Swift library that adds support for SVG files to UIImage and NSImage.", 5 | "homepage": "https://github.com/swhitty/SwiftDraw", 6 | "authors": "Simon Whitty", 7 | "license": { 8 | "type": "zlib", 9 | "file": "LICENSE.txt" 10 | }, 11 | "source": { 12 | "git": "https://github.com/swhitty/SwiftDraw.git", 13 | "tag": "0.25.3" 14 | }, 15 | "platforms": { 16 | "ios": "13.0", 17 | "osx": "10.15", 18 | "tvos": "13.0", 19 | "watchos": "6.0", 20 | "visionos": "1.0" 21 | }, 22 | "source_files": "DOM/Sources/*.swift", 23 | "swift_version": "6.0", 24 | "pod_target_xcconfig": { 25 | "OTHER_SWIFT_FLAGS": "-package-name SwiftDraw" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Samples.bundle/pattern-rotate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples.bundle/gradient2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Simon Whitty 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | -------------------------------------------------------------------------------- /Samples.bundle/ellipse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Samples.bundle/amex.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Samples.bundle/display.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Samples.bundle/gradient-stroke.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Samples.bundle/use.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Samples.bundle/pattern.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Samples.bundle/pattern-bounding.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Samples.bundle/alert.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples.bundle/mask-composite.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Samples.bundle/abc.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | -------------------------------------------------------------------------------- /Samples.bundle/blend.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/CommandLine/CommandLine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommandLine.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 7/12/18. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import Foundation 33 | 34 | public enum CommandLine { /* namespace */ } 35 | -------------------------------------------------------------------------------- /SwiftDraw.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SwiftDraw", 3 | "version": "0.25.3", 4 | "summary": "A Swift library that adds support for SVG files to UIImage and NSImage.", 5 | "homepage": "https://github.com/swhitty/SwiftDraw", 6 | "authors": "Simon Whitty", 7 | "license": { 8 | "type": "zlib", 9 | "file": "LICENSE.txt" 10 | }, 11 | "source": { 12 | "git": "https://github.com/swhitty/SwiftDraw.git", 13 | "tag": "0.25.3" 14 | }, 15 | "platforms": { 16 | "ios": "13.0", 17 | "osx": "10.15", 18 | "tvos": "13.0", 19 | "watchos": "6.0", 20 | "visionos": "1.0" 21 | }, 22 | "source_files": "SwiftDraw/Sources/**/*.swift", 23 | "ios": { 24 | "exclude_files": "SwiftDraw/Sources/NSImage+Image.swift", 25 | "frameworks": ["UIKit", "Foundation"] 26 | }, 27 | "osx": { 28 | "exclude_files": "SwiftDraw/Sources/UIImage+Image.swift", 29 | "frameworks": ["AppKit", "Foundation"] 30 | }, 31 | "tvos": { 32 | "exclude_files": "SwiftDraw/Sources/NSImage+Image.swift", 33 | "frameworks": ["UIKit", "Foundation"] 34 | }, 35 | "watchos": { 36 | "exclude_files": "SwiftDraw/Sources/NSImage+Image.swift", 37 | "frameworks": ["UIKit", "WatchKit", "Foundation"] 38 | }, 39 | "swift_version": "6.0", 40 | "pod_target_xcconfig": { 41 | "OTHER_SWIFT_FLAGS": "-package-name SwiftDraw" 42 | }, 43 | "dependencies": { 44 | "SwiftDrawDOM": "~> 0.25.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /DOM/Sources/DOM.Switch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DOM.Swift.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 27/2/17. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | package extension DOM { 33 | final class Switch: GraphicsElement, ContainerElement { 34 | package var childElements = [DOM.GraphicsElement]() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Samples.bundle/stylesheet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 24 | 25 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /DOM/Tests/Test.bundle/stylesheet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 24 | 25 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /CommandLine/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 23/11/18. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | #if canImport(Darwin) 33 | import Darwin.POSIX 34 | #elseif canImport(Android) 35 | import Android 36 | #else 37 | import Glibc 38 | #endif 39 | 40 | import SwiftDraw 41 | 42 | exit(CommandLine.run().rawValue) 43 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Test.bundle/stylesheet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 24 | 25 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Samples.bundle/circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples.bundle/thats-no-moon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Examples/Sources/ExampleApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 10/2/19. 6 | // Copyright 2019 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import SwiftUI 33 | 34 | @main 35 | struct ExampleApp: App { 36 | var body: some Scene { 37 | WindowGroup { 38 | NavigationStack { 39 | GalleryView() 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DOM/Sources/Parser.XML.Use.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.XML.Use.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 27/2/17. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | extension XMLParser { 33 | 34 | func parseUse(_ att: any AttributeParser) throws -> DOM.Use { 35 | let use = DOM.Use(href: try att.parseUrl("xlink:href")) 36 | use.x = try att.parseCoordinate("x") 37 | use.y = try att.parseCoordinate("y") 38 | 39 | return use 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Samples.bundle/gradient-radial.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Samples.bundle/gradient-gratification.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Test.bundle/gradient-gratification.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Examples/Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | UILaunchScreen 22 | 23 | UILaunchScreen 24 | 25 | 26 | LSRequiresIPhoneOS 27 | 28 | UIRequiredDeviceCapabilities 29 | 30 | armv7 31 | 32 | UISupportedInterfaceOrientations 33 | 34 | UIInterfaceOrientationPortrait 35 | UIInterfaceOrientationLandscapeLeft 36 | UIInterfaceOrientationLandscapeRight 37 | 38 | UISupportedInterfaceOrientations~ipad 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationPortraitUpsideDown 42 | UIInterfaceOrientationLandscapeLeft 43 | UIInterfaceOrientationLandscapeRight 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Samples.bundle/fill-rule2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 24 | 25 | 26 | 27 | 28 | 29 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Samples.bundle/identity.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Samples.bundle/gradient-gratification-p3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Samples.bundle/radialGradient.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Samples.bundle/stars.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /DOM/Sources/DOM.Image.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DOM.Image.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 7/3/17. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | package extension DOM { 32 | final class Image: GraphicsElement { 33 | package var href: URL 34 | package var width: Coordinate? 35 | package var height: Coordinate? 36 | 37 | package var x: Coordinate? 38 | package var y: Coordinate? 39 | 40 | package init(href: URL) { 41 | self.href = href 42 | super.init() 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /DOM/Tests/Test.bundle/radialGradient.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Test.bundle/radialGradient.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /DOM/Sources/URL+Fragment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URL+Fragment.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 12/10/24. 6 | // Copyright 2024 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import Foundation 33 | 34 | package extension URL { 35 | 36 | var fragmentID: String? { 37 | #if canImport(Darwin) 38 | if #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) { 39 | return fragment(percentEncoded: false) 40 | } else { 41 | return fragment 42 | } 43 | #else 44 | return fragment 45 | #endif 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /DOM/Sources/DOM.Filter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DOM.Filter.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 16/8/22. 6 | // Copyright 2022 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | package extension DOM { 33 | 34 | final class Filter: Element { 35 | package var id: String 36 | 37 | package var effects: [Effect] 38 | 39 | package init(id: String) { 40 | self.id = id 41 | self.effects = [] 42 | } 43 | 44 | package enum Effect: Hashable { 45 | case gaussianBlur(stdDeviation: DOM.Float) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /DOM/Sources/Parser.XML.Image.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.XML.Image.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 31/12/16. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | extension XMLParser { 33 | 34 | func parseImage(_ att: any AttributeParser) throws -> DOM.Image { 35 | let href: DOM.URL = try att.parseUrl("xlink:href") 36 | 37 | let image = DOM.Image(href: href) 38 | image.x = try att.parseCoordinate("x") 39 | image.y = try att.parseCoordinate("y") 40 | image.width = try att.parseCoordinate("width") 41 | image.height = try att.parseCoordinate("height") 42 | 43 | return image 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /DOM/Tests/Bundle+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle+Extensions.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 19/11/18. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | 33 | import Foundation 34 | 35 | extension Bundle { 36 | 37 | static let test = Bundle(url: Bundle.module.url(forResource: "Test", withExtension: "bundle")!)! 38 | 39 | func url(forResource named: String) throws -> URL { 40 | guard let url = self.url(forResource: named, withExtension: nil) else { 41 | throw Error.invalid 42 | } 43 | return url 44 | } 45 | 46 | private enum Error: Swift.Error { 47 | case invalid 48 | } 49 | 50 | private class Marker { } 51 | } 52 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Utilities/Bundle+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle+Extensions.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 19/11/18. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | 33 | import Foundation 34 | 35 | extension Bundle { 36 | 37 | static let test = Bundle(url: Bundle.module.url(forResource: "Test", withExtension: "bundle")!)! 38 | 39 | func url(forResource named: String) throws -> URL { 40 | guard let url = self.url(forResource: named, withExtension: nil) else { 41 | throw Error.invalid 42 | } 43 | return url 44 | } 45 | 46 | private enum Error: Swift.Error { 47 | case invalid 48 | } 49 | 50 | private class Marker { } 51 | } 52 | -------------------------------------------------------------------------------- /Samples.bundle/linearGradient.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /DOM/Tests/Test.bundle/linearGradient.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Samples.bundle/arc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Test.bundle/linearGradient.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Samples.bundle/nested-svg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /DOM/Sources/XML.Element.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XML.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 31/12/16. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | package enum XML { /* namespace */ } 33 | 34 | package extension XML { 35 | final class Element { 36 | 37 | package let name: String 38 | package var attributes: [String: String] 39 | package var children = [Element]() 40 | package var innerText: String? 41 | 42 | package var parsedLocation: (line: Int, column: Int)? 43 | 44 | package init(name: String, attributes: [String: String] = [:]) { 45 | self.name = name 46 | self.attributes = attributes 47 | self.innerText = nil 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /DOM/Tests/Test.bundle/nested-svg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Samples.bundle/curves.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 33 | 34 | 41 | 42 | 43 | 44 | 45 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /DOM/Tests/Test.bundle/curves.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 33 | 34 | 41 | 42 | 43 | 44 | 45 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Test.bundle/nested-svg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Test.bundle/curves.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 33 | 34 | 41 | 42 | 43 | 44 | 45 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /DOM/Sources/DOM.Text.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DOM.Text.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 31/12/16. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import Foundation 33 | 34 | package extension DOM { 35 | 36 | final class Text: GraphicsElement { 37 | package var x: Coordinate? 38 | package var y: Coordinate? 39 | package var value: String 40 | 41 | package init(x: Coordinate? = nil, y: Coordinate? = nil, value: String) { 42 | self.x = x 43 | self.y = y 44 | self.value = value 45 | } 46 | } 47 | 48 | final class Anchor: GraphicsElement, ContainerElement { 49 | package var href: URL? 50 | package var childElements = [GraphicsElement]() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Samples.bundle/transform.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Samples.bundle/symbol-test.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | Small 11 | Medium 12 | Large 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Test.bundle/symbol-test.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | Small 11 | Medium 12 | Large 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /DOM/Tests/UseTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UseTests.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 27/2/17. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import Testing 33 | @testable import SwiftDrawDOM 34 | 35 | @Suite("Use Tests") 36 | struct UseTests { 37 | 38 | @Test 39 | func use() throws { 40 | var node = ["xlink:href": "#line2", "href": "#line1"] 41 | 42 | var parsed = try XMLParser().parseUse(node) 43 | #expect(parsed.href.fragmentID == "line2") 44 | #expect(parsed.x == nil) 45 | #expect(parsed.y == nil) 46 | 47 | node["x"] = "20" 48 | node["y"] = "30" 49 | 50 | parsed = try XMLParser().parseUse(node) 51 | #expect(parsed.href.fragmentID == "line2") 52 | #expect(parsed.x == 20) 53 | #expect(parsed.y == 30) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /DOM/Sources/DOM.SVG+Parse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DOM.SVG+Parse.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 23/2/25. 6 | // Copyright 2025 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import Foundation 33 | 34 | package extension DOM.SVG { 35 | 36 | static func parse(fileURL url: URL, options: XMLParser.Options = .skipInvalidElements) throws -> DOM.SVG { 37 | let element = try XML.SAXParser.parse(contentsOf: url) 38 | let parser = XMLParser(options: options, filename: url.lastPathComponent) 39 | return try parser.parseSVG(element) 40 | } 41 | 42 | static func parse(data: Data, options: XMLParser.Options = .skipInvalidElements) throws -> DOM.SVG { 43 | let element = try XML.SAXParser.parse(data: data) 44 | let parser = XMLParser(options: options) 45 | return try parser.parseSVG(element) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/SVG+UIKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SVG+UIKit.swift 3 | // SwiftDraw 4 | // 5 | // Created by Daniel Sincere on 25/10/14. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | #if canImport(UIKit) && !os(watchOS) 33 | public import UIKit 34 | 35 | public extension UIGraphicsImageRendererContext { 36 | func draw(_ svg: SVG, in rect: CGRect? = nil) { 37 | self.cgContext.draw(svg, in: rect ) 38 | } 39 | 40 | func draw(_ svg: SVG, in rect: CGRect, byTiling: Bool) { 41 | self.cgContext.draw(svg, in: rect, byTiling: byTiling) 42 | } 43 | 44 | func draw( 45 | _ svg: SVG, 46 | in rect: CGRect, 47 | capInsets: (top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat), 48 | byTiling: Bool 49 | ) { 50 | self.cgContext.draw(svg, in: rect, capInsets: capInsets, byTiling: byTiling) 51 | } 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/LayerTree/LayerTree.Pattern.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayerTree.Pattern.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 27/3/19. 6 | // Copyright 2020 WhileLoop Pty Ltd. All rights reserved. 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | extension LayerTree { 33 | 34 | final class Pattern: Hashable { 35 | 36 | var frame: LayerTree.Rect 37 | var contents: [LayerTree.Layer.Contents] 38 | 39 | init(frame: LayerTree.Rect) { 40 | self.frame = frame 41 | self.contents = [] 42 | } 43 | 44 | func hash(into hasher: inout Hasher) { 45 | frame.hash(into: &hasher) 46 | contents.hash(into: &hasher) 47 | } 48 | 49 | static func == (lhs: LayerTree.Pattern, rhs: LayerTree.Pattern) -> Bool { 50 | return lhs.frame == rhs.frame && lhs.contents == rhs.contents 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/LayerTree/LayerTree.Builder.ShapeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayerTree.Builder.ShapeTests.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 21/11/18. 6 | // Copyright 2020 WhileLoop Pty Ltd. All rights reserved. 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import SwiftDrawDOM 33 | import XCTest 34 | @testable import SwiftDraw 35 | 36 | final class LayerTreeBuilderShapeTests: XCTestCase { 37 | 38 | func testDOMRectMakesRectWithDefaultOrigin() { 39 | let dom = DOM.Rect(width: 10, height: 20) 40 | let rect = LayerTree.Builder.makeRect(from: dom) 41 | XCTAssertEqual(rect, LayerTree.Rect(x: 0, y: 0, width: 10, height: 20)) 42 | } 43 | 44 | func testDOMRectMakesRect() { 45 | let dom = DOM.Rect(x: 10, y: 20, width: 30, height: 40) 46 | let rect = LayerTree.Builder.makeRect(from: dom) 47 | XCTAssertEqual(rect, LayerTree.Rect(x: 10, y: 20, width: 30, height: 40)) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /DOM/Sources/Parser.XML.Pattern.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.XML.Pattern.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 26/3/19. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | 33 | import Foundation 34 | 35 | extension XMLParser { 36 | 37 | func parsePattern(_ att: any AttributeParser) throws -> DOM.Pattern { 38 | 39 | let id: String = try att.parseString("id") 40 | let width: DOM.Coordinate = try att.parseCoordinate("width") 41 | let height: DOM.Coordinate = try att.parseCoordinate("height") 42 | 43 | var pattern = DOM.Pattern(id: id, width: width, height: height) 44 | pattern.x = try att.parseCoordinate("x") 45 | pattern.y = try att.parseCoordinate("y") 46 | 47 | pattern.patternUnits = try att.parseRaw("patternUnits") 48 | pattern.patternContentUnits = try att.parseRaw("patternContentUnits") 49 | 50 | return pattern 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /Samples.bundle/mask.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Examples/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:6.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "SwiftDraw", 7 | platforms: [ 8 | .macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6), .visionOS(.v1) 9 | ], 10 | products: [ 11 | .executable(name: "swiftdrawcli", targets: ["CommandLine"]), 12 | .library( 13 | name: "SwiftDraw", 14 | targets: ["SwiftDraw"]), 15 | ], 16 | dependencies: [], 17 | targets: [ 18 | .target( 19 | name: "SwiftDraw", 20 | dependencies: ["SwiftDrawDOM"], 21 | path: "SwiftDraw/Sources", 22 | swiftSettings: .upcomingFeatures 23 | ), 24 | .target( 25 | name: "SwiftDrawDOM", 26 | dependencies: [], 27 | path: "DOM/Sources" 28 | ), 29 | .executableTarget( 30 | name: "CommandLine", 31 | dependencies: ["SwiftDraw"], 32 | path: "CommandLine", 33 | swiftSettings: .upcomingFeatures 34 | ), 35 | .testTarget( 36 | name: "SwiftDrawDOMTests", 37 | dependencies: ["SwiftDrawDOM"], 38 | path: "DOM/Tests", 39 | resources: [ 40 | .copy("Test.bundle") 41 | ], 42 | swiftSettings: .upcomingFeatures 43 | ), 44 | .testTarget( 45 | name: "SwiftDrawTests", 46 | dependencies: ["SwiftDraw"], 47 | path: "SwiftDraw/Tests", 48 | resources: [ 49 | .copy("Test.bundle") 50 | ], 51 | swiftSettings: .upcomingFeatures 52 | ) 53 | ] 54 | ) 55 | 56 | extension Array where Element == SwiftSetting { 57 | 58 | static var upcomingFeatures: [SwiftSetting] { 59 | [ 60 | .enableUpcomingFeature("ExistentialAny"), 61 | .enableUpcomingFeature("InternalImportsByDefault"), 62 | .enableUpcomingFeature("MemberImportVisibility"), 63 | .swiftLanguageMode(.v6) 64 | ] 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/SVG+Insets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SVG+Insets.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 23/2/25. 6 | // Copyright 2025 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | #if canImport(CoreGraphics) 33 | public import struct CoreGraphics.CGFloat 34 | #else 35 | public import typealias Foundation.CGFloat 36 | #endif 37 | 38 | public extension SVG { 39 | struct Insets: Equatable { 40 | public var top: CGFloat 41 | public var left: CGFloat 42 | public var bottom: CGFloat 43 | public var right: CGFloat 44 | 45 | public init( 46 | top: CGFloat = 0, 47 | left: CGFloat = 0, 48 | bottom: CGFloat = 0, 49 | right: CGFloat = 0 50 | ) { 51 | self.top = top 52 | self.left = left 53 | self.bottom = bottom 54 | self.right = right 55 | } 56 | 57 | public static var zero: Insets { Insets(top: 0, left: 0, bottom: 0, right: 0) } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Samples.bundle/sun-horizon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Samples.bundle/rings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/Utilities/Stack.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Stack.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 15/6/17. 6 | // Copyright 2020 WhileLoop Pty Ltd. All rights reserved. 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | struct Stack { 33 | private(set) var root: Element 34 | private(set) var storage: [Element] 35 | 36 | init(root: Element) { 37 | self.root = root 38 | storage = [Element]() 39 | } 40 | 41 | var top: Element { 42 | get { 43 | guard let last = storage.last else { return root } 44 | return last 45 | } 46 | set { 47 | guard storage.isEmpty else { 48 | storage.removeLast() 49 | storage.append(newValue) 50 | return 51 | } 52 | root = newValue 53 | } 54 | } 55 | 56 | mutating func push(_ element: Element) { 57 | storage.append(element) 58 | } 59 | 60 | @discardableResult 61 | mutating func pop() -> Bool { 62 | guard !storage.isEmpty else { return false } 63 | storage.removeLast() 64 | return true 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /DOM/Sources/TextOutputStream+StandardError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextOutputStream+StandardError.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 17/8/22. 6 | // Copyright 2022 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import Foundation 33 | 34 | package extension TextOutputStream where Self == StandardErrorStream { 35 | static var standardError: Self { 36 | get { 37 | StandardErrorStream.shared 38 | } 39 | set { 40 | StandardErrorStream.shared = newValue 41 | } 42 | } 43 | } 44 | 45 | package struct StandardErrorStream: TextOutputStream { 46 | 47 | nonisolated(unsafe) 48 | fileprivate static var shared = StandardErrorStream() 49 | 50 | package func write(_ string: String) { 51 | if #available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.2, *) { 52 | try! FileHandle.standardError.write(contentsOf: string.data(using: .utf8)!) 53 | } else { 54 | FileHandle.standardError.write(string.data(using: .utf8)!) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /DOM/Sources/DOM.Pattern.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DOM.Pattern.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 26/3/19. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import Foundation 33 | 34 | package extension DOM { 35 | 36 | struct Pattern: ContainerElement { 37 | 38 | package var id: String 39 | package var x: Coordinate? 40 | package var y: Coordinate? 41 | package var width: Coordinate 42 | package var height: Coordinate 43 | 44 | package var patternUnits: Units? 45 | package var patternContentUnits: Units? 46 | 47 | package var childElements: [DOM.GraphicsElement] = [] 48 | 49 | package init(id: String, width: Coordinate, height: Coordinate) { 50 | self.id = id 51 | self.width = width 52 | self.height = height 53 | } 54 | } 55 | } 56 | 57 | package extension DOM.Pattern { 58 | 59 | enum Units: String { 60 | case userSpaceOnUse 61 | case objectBoundingBox 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Utilities/NSBitmapImageRep+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSBitmapImageRep+Extensions.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 18/12/18. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 33 | import AppKit 34 | 35 | extension NSBitmapImageRep { 36 | 37 | convenience init(pixelsWide width: Int, pixelsHigh height: Int) { 38 | self.init(bitmapDataPlanes: nil, 39 | pixelsWide: width, 40 | pixelsHigh: height, 41 | bitsPerSample: 8, 42 | samplesPerPixel: 4, 43 | hasAlpha: true, 44 | isPlanar: false, 45 | colorSpaceName: NSColorSpaceName.deviceRGB, 46 | bytesPerRow: 0, 47 | bitsPerPixel: 32)! 48 | } 49 | 50 | func lockFocus() { 51 | NSGraphicsContext.saveGraphicsState() 52 | NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: self) 53 | } 54 | 55 | func unlockFocus() { 56 | NSGraphicsContext.restoreGraphicsState() 57 | } 58 | } 59 | #endif 60 | -------------------------------------------------------------------------------- /Samples.bundle/transform-path.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Renderer/Renderer.SVGTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Renderer.SVGTests.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 7/03/22. 6 | // Copyright 2022 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import XCTest 33 | @testable import SwiftDraw 34 | 35 | final class SVGRendererTests: XCTestCase { 36 | 37 | func testPathExpands_WithTranslate() throws { 38 | let path = try SVGRenderer.makeExpanded( 39 | path: "M0,50 L 50,50 C 100,0 150,0 200,50", 40 | transform: "translate(10, 10)", 41 | precision: 0 42 | ) 43 | 44 | XCTAssertEqual( 45 | path, 46 | "M10,60 L60,60 C110,10 160,10 210,60" 47 | ) 48 | } 49 | 50 | func testPathExpands_WithRotate() throws { 51 | let path = try SVGRenderer.makeExpanded( 52 | path: "M100,0 L 300,0 L 300,100 L 100,100 Z", 53 | transform: "rotate(90 100 0)", 54 | precision: 0 55 | ) 56 | 57 | XCTAssertEqual( 58 | path, 59 | "M100,0 L100,200 L0,200 L0,0 Z" 60 | ) 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /DOM/Sources/Parser.XML.Text.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.XML.Text.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 31/12/16. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import Foundation 33 | 34 | extension XMLParser { 35 | 36 | func parseText(_ att: any AttributeParser, element: XML.Element) throws -> DOM.Text? { 37 | guard 38 | let text = element.innerText?.trimmingCharacters(in: .whitespacesAndNewlines), 39 | !text.isEmpty else { 40 | return nil 41 | } 42 | 43 | return try parseText(att, value: text) 44 | } 45 | 46 | func parseAnchor(_ att: any AttributeParser, element: XML.Element) throws -> DOM.Anchor? { 47 | let anchor = DOM.Anchor() 48 | anchor.href = try att.parseUrl("href") 49 | return anchor 50 | } 51 | 52 | func parseText(_ att: any AttributeParser, value: String) throws -> DOM.Text { 53 | let element = DOM.Text(value: value) 54 | element.x = try att.parseCoordinate("x") 55 | element.y = try att.parseCoordinate("y") 56 | return element 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/LayerTree/LayerTree.Builder.Path.ArcTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayerTree.Builder.Path.ArcTests.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 14/2/25. 6 | // Copyright 2025 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | 33 | import XCTest 34 | @testable import SwiftDraw 35 | 36 | final class LayerTreeBuilderPathArcTests: XCTestCase { 37 | 38 | func testClamped() { 39 | XCTAssertEqual(5.clamped(to: 0...10), 5) 40 | XCTAssertEqual((10.1).clamped(to: 0...10), 10) 41 | XCTAssertEqual((-1).clamped(to: 0...10), 0) 42 | XCTAssertEqual(Double.infinity.clamped(to: 0...10), 10) 43 | XCTAssertEqual((-Double.infinity).clamped(to: 0...10), 0) 44 | XCTAssertEqual(Double.nan.clamped(to: 0...10), 0) 45 | XCTAssertEqual((-Double.nan).clamped(to: 0...10), 0) 46 | } 47 | 48 | func testVectorAngle() { 49 | XCTAssertEqual(vectorAngle(ux: 1, uy: 1, vx: 1, vy: 1), 0) 50 | XCTAssertEqual(vectorAngle(ux: 1, uy: 1, vx: -1, vy: 1), 1.5707964, accuracy: 0.001) 51 | XCTAssertEqual(vectorAngle(ux: 1, uy: 1, vx: .nan, vy: 1), 3.1415925, accuracy: 0.001) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/LayerTree/LayerTree.Image.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayerTree.Image.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 3/6/17. 6 | // Copyright 2020 WhileLoop Pty Ltd. All rights reserved. 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import Foundation 33 | 34 | extension LayerTree { 35 | struct Image: Hashable { 36 | 37 | var bitmap: Bitmap 38 | var origin: Point = .zero 39 | var width: LayerTree.Float? 40 | var height: LayerTree.Float? 41 | 42 | enum Bitmap: Hashable { 43 | case jpeg(Data) 44 | case png(Data) 45 | } 46 | 47 | init(bitmap: Bitmap) { 48 | self.bitmap = bitmap 49 | } 50 | 51 | init?(mimeType: String, data: Data) { 52 | guard data.count > 0 else { return nil } 53 | 54 | switch mimeType { 55 | case "image/png": 56 | self.bitmap = .png(data) 57 | case "image/jpeg": 58 | self.bitmap = .jpeg(data) 59 | case "image/jpg": 60 | self.bitmap = .jpeg(data) 61 | default: 62 | return nil 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Samples.bundle/mask-composite-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /DOM/Sources/DOM.Use.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DOM.Use.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 27/2/17. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | package extension DOM { 33 | final class Use: GraphicsElement { 34 | package var x: Coordinate? 35 | package var y: Coordinate? 36 | 37 | //references element ids within defs 38 | package var href: URL 39 | 40 | package init(href: URL) { 41 | self.href = href 42 | } 43 | } 44 | } 45 | 46 | package extension DOM.SVG { 47 | 48 | func firstGraphicsElement(with id: String) -> DOM.GraphicsElement? { 49 | if let def = defs.elements[id] { 50 | return def 51 | } 52 | 53 | return childElements.firstGraphicsElement(with: id) 54 | } 55 | } 56 | 57 | package extension Array { 58 | 59 | func firstGraphicsElement(with id: String) -> DOM.GraphicsElement? { 60 | for element in self { 61 | if element.id == id { 62 | return element 63 | } 64 | if let container = element as? any ContainerElement { 65 | return container.childElements.firstGraphicsElement(with: id) 66 | } 67 | } 68 | return nil 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/LayerTree/LayerTree.Builder.Text.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayerTree.Builder.Text.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 26/8/22. 6 | // Copyright 2022 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | // Convert a DOM.SVG into a layer tree 33 | 34 | import SwiftDrawDOM 35 | import Foundation 36 | #if canImport(CoreText) 37 | import CoreText 38 | #endif 39 | 40 | extension LayerTree.Builder { 41 | 42 | #if canImport(CoreText) 43 | static func makeXOffset(for text: String, with attributes: LayerTree.TextAttributes) -> LayerTree.Float { 44 | let font = CTFontCreateWithName(attributes.fontName as CFString, 45 | CGFloat(attributes.size), 46 | nil) 47 | guard let bounds = text.toPath(font: font)?.boundingBoxOfPath else { return 0 } 48 | switch attributes.anchor { 49 | case .start: 50 | return LayerTree.Float(bounds.minX) 51 | case .middle: 52 | return LayerTree.Float(-bounds.midX) 53 | case .end: 54 | return LayerTree.Float(-bounds.maxX) 55 | } 56 | } 57 | #else 58 | static func makeXOffset(for text: String, with attributes: LayerTree.TextAttributes) -> LayerTree.Float { 59 | return 0 60 | } 61 | #endif 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/Renderer/Renderer.SFSymbol+CGPath.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Renderer.SFSymbol+CGPath.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 21/8/22. 6 | // Copyright 2022 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | #if canImport(CoreGraphics) 33 | import Foundation 34 | import CoreGraphics 35 | 36 | extension SFSymbolRenderer { 37 | 38 | 39 | static func expandOutlines(for path: LayerTree.Path, 40 | stroke: LayerTree.StrokeAttributes) -> LayerTree.Path? { 41 | 42 | var mediaBox = CGRect(x: 0.0, y: 0.0, width: 100, height: 100) 43 | let data = NSMutableData() 44 | guard let consumer = CGDataConsumer(data: data as CFMutableData), 45 | let ctx = CGContext(consumer: consumer, mediaBox: &mediaBox, nil) else { 46 | return nil 47 | } 48 | 49 | let provider = CGProvider() 50 | 51 | ctx.setLineWidth(provider.createFloat(from: stroke.width)) 52 | ctx.setLineJoin(provider.createLineJoin(from: stroke.join)) 53 | ctx.setLineCap(provider.createLineCap(from: stroke.cap)) 54 | ctx.setMiterLimit(provider.createFloat(from: stroke.miterLimit)) 55 | ctx.addPath(provider.createPath(from: .path(path))) 56 | ctx.replacePathWithStrokedPath() 57 | guard let cgPath = ctx.path else { 58 | return nil 59 | } 60 | 61 | return cgPath.makePath() 62 | } 63 | } 64 | #endif 65 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/SVGCache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SVG.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 24/5/17. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import SwiftDrawDOM 33 | public import Foundation 34 | 35 | #if canImport(CoreGraphics) 36 | import CoreGraphics 37 | 38 | public final class SVGGCache: Sendable { 39 | 40 | public static let shared = SVGGCache() 41 | 42 | nonisolated(unsafe)private let cache: NSCache> 43 | 44 | public init(totalCostLimit: Int = defaultTotalCostLimit) { 45 | self.cache = NSCache() 46 | self.cache.totalCostLimit = totalCostLimit 47 | } 48 | 49 | public func svg(fileURL: URL) -> SVG? { 50 | cache.object(forKey: fileURL as NSURL)?.value 51 | } 52 | 53 | public func setSVG(_ svg: SVG, for fileURL: URL) { 54 | cache.setObject(Box(svg), forKey: fileURL as NSURL, cost: svg.commands.estimatedCost) 55 | } 56 | 57 | final class Box: NSObject { 58 | let value: T 59 | init(_ value: T) { self.value = value } 60 | } 61 | 62 | public static var defaultTotalCostLimit: Int { 63 | #if canImport(WatchKit) 64 | // 2 MB 65 | return 2 * 1024 * 1024 66 | #elseif canImport(AppKit) 67 | // 200 MB 68 | return 200 * 1024 * 1024 69 | #else 70 | // 50 MB 71 | return 50 * 1024 * 1024 72 | #endif 73 | } 74 | } 75 | #endif 76 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/LayerTree/LayerTree.ImageTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayerTree.ImageTests.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 3/6/17. 6 | // Copyright 2020 WhileLoop Pty Ltd. All rights reserved. 7 | // 8 | // https://github.com/swhitty/SwiftDraw 9 | // 10 | // This software is provided 'as-is', without any express or implied 11 | // warranty. In no event will the authors be held liable for any damages 12 | // arising from the use of this software. 13 | // 14 | // Permission is granted to anyone to use this software for any purpose, 15 | // including commercial applications, and to alter it and redistribute it 16 | // freely, subject to the following restrictions: 17 | // 18 | // 1. The origin of this software must not be misrepresented; you must not 19 | // claim that you wrote the original software. If you use this software 20 | // in a product, an acknowledgment in the product documentation would be 21 | // appreciated but is not required. 22 | // 23 | // 2. Altered source versions must be plainly marked as such, and must not be 24 | // misrepresented as being the original software. 25 | // 26 | // 3. This notice may not be removed or altered from any source distribution. 27 | // 28 | 29 | import XCTest 30 | @testable import SwiftDraw 31 | 32 | final class LayerTreeImageTests: XCTestCase { 33 | 34 | let someData = Data(base64Encoded: "8badf00d")! 35 | let moreData = Data(base64Encoded: "f00d")! 36 | 37 | func testInit() { 38 | let i1 = LayerTree.Image(mimeType: "image/png", data: someData) 39 | let i2 = LayerTree.Image(mimeType: "image/jpg", data: moreData) 40 | let i3 = LayerTree.Image(mimeType: "image/jpeg", data: someData) 41 | 42 | XCTAssertEqual(i1, .png(data: someData)) 43 | XCTAssertEqual(i2, .jpeg(data: moreData)) 44 | XCTAssertEqual(i3, .jpeg(data: someData)) 45 | 46 | XCTAssertNil(LayerTree.Image(mimeType: "image/jpg", data: Data())) 47 | XCTAssertNil(LayerTree.Image(mimeType: "image", data: someData)) 48 | } 49 | 50 | func testImageEquality() { 51 | let i1 = LayerTree.Image(mimeType: "image/jpeg", data: someData) 52 | let i2 = LayerTree.Image(mimeType: "image/jpg", data: someData) 53 | let i3 = LayerTree.Image(mimeType: "image/png", data: someData) 54 | 55 | XCTAssertEqual(i1, .jpeg(data: someData)) 56 | XCTAssertEqual(i1, i1) 57 | XCTAssertEqual(i1, i2) 58 | 59 | XCTAssertEqual(i3, .png(data: someData)) 60 | XCTAssertEqual(i3, i3) 61 | 62 | XCTAssertNotEqual(i1, i3) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /DOM/Sources/URL+Data.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URL+Data.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 28/2/17. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import Foundation 33 | 34 | package extension URL { 35 | 36 | init?(maybeData string: String) { 37 | guard string.hasPrefix("data:") else { 38 | self.init(string: string) 39 | return 40 | } 41 | 42 | var removed = string.replacingOccurrences(of: "\t", with: "") 43 | removed = removed.replacingOccurrences(of: "\n", with: "") 44 | removed = removed.replacingOccurrences(of: " ", with: "") 45 | 46 | self.init(string: removed) 47 | } 48 | 49 | 50 | var isDataURL: Bool { 51 | return scheme == "data" 52 | } 53 | 54 | var decodedData: (mimeType: String, data: Data)? { 55 | let txt = absoluteString 56 | guard let schemeRange = txt.range(of: "data:"), 57 | let mimeRange = txt.range(of: ";", options: [], range: schemeRange.upperBound.. 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/Utilities/CGImage+Mask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGImage+Mask.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 31/3/19. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | #if canImport(CoreGraphics) 33 | import CoreGraphics 34 | import Foundation 35 | 36 | func CGColorSpaceCreateExtendedGray() -> CGColorSpace { 37 | return CGColorSpace(name: CGColorSpace.extendedGray)! 38 | } 39 | 40 | extension CGImage { 41 | 42 | static func makeMask(size: CGSize, draw: (CGContext) -> ()) -> CGImage { 43 | 44 | let width = Int(size.width) 45 | let height = Int(size.height) 46 | 47 | var data = Data(repeating: 0xff, count: width*height) 48 | data.withUnsafeMutableBytes { 49 | let ctx = CGContext(data: $0.baseAddress, 50 | width: width, 51 | height: height, 52 | bitsPerComponent: 8, 53 | bytesPerRow: width, 54 | space: CGColorSpaceCreateDeviceGray(), 55 | bitmapInfo: 0)! 56 | draw(ctx) 57 | } 58 | 59 | return CGImage(maskWidth: width, 60 | height: height, 61 | bitsPerComponent: 8, 62 | bitsPerPixel: 8, 63 | bytesPerRow: width, 64 | provider: CGDataProvider(data: data as CFData)!, 65 | decode: nil, 66 | shouldInterpolate: true)! 67 | } 68 | } 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /Examples/Sources/GalleryView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GalleryView.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 19/2/25. 6 | // Copyright 2019 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import SwiftDraw 33 | import SwiftUI 34 | 35 | struct GalleryView: View { 36 | 37 | var images = [ 38 | "thats-no-moon.svg", 39 | "avocado.svg", 40 | "angry.svg", 41 | "ogre.svg", 42 | "monkey.svg", 43 | "fuji.svg", 44 | "dish.svg", 45 | "mouth-open.svg", 46 | "sleepy.svg", 47 | "smile.svg", 48 | "snake.svg", 49 | "spider.svg", 50 | "star-struck.svg", 51 | "worried.svg", 52 | "yawning.svg", 53 | "thats-no-moon.svg", 54 | "alert.svg", 55 | "effigy.svg", 56 | "stylesheet-multiple.svg" 57 | ] 58 | 59 | var body: some View { 60 | ScrollView { 61 | LazyVStack(spacing: 20) { 62 | ForEach(images, id: \.self) { image in 63 | SVGView(image, bundle: .samples) 64 | .resizable() 65 | .scaledToFit() 66 | .padding([.leading, .trailing], 10) 67 | } 68 | } 69 | .background(Color.white) 70 | } 71 | } 72 | } 73 | 74 | extension Bundle { 75 | static let samples: Bundle = { 76 | let url = Bundle.main.url(forResource: "Samples.bundle", withExtension: nil)! 77 | return Bundle(url: url)! 78 | }() 79 | } 80 | -------------------------------------------------------------------------------- /DOM/Sources/Parser.XML.Filter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.XML.Filter.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 16/8/22. 6 | // Copyright 2022 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | extension XMLParser { 33 | 34 | func parseFilters(_ e: XML.Element) throws -> [DOM.Filter] { 35 | var filters = [DOM.Filter]() 36 | 37 | for n in e.children { 38 | if n.name == "filter" { 39 | filters.append(try parseFilter(n)) 40 | } else { 41 | filters.append(contentsOf: try parseFilters(n)) 42 | } 43 | } 44 | return filters 45 | } 46 | 47 | func parseFilter(_ e: XML.Element) throws -> DOM.Filter { 48 | guard e.name == "filter" else { 49 | throw Error.invalid 50 | } 51 | 52 | let nodeAtt: any AttributeParser = try parseAttributes(e) 53 | let node = DOM.Filter(id: try nodeAtt.parseString("id")) 54 | 55 | for n in e.children { 56 | if let effect = try parseEffect(n) { 57 | node.effects.append(effect) 58 | } 59 | } 60 | 61 | return node 62 | } 63 | 64 | func parseEffect(_ e: XML.Element) throws -> DOM.Filter.Effect? { 65 | switch e.name { 66 | case "feGaussianBlur": 67 | let att: any AttributeParser = try parseAttributes(e) 68 | return try .gaussianBlur(stdDeviation: att.parseFloat("stdDeviation")) 69 | default: 70 | return nil 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Utilities/URL+DataTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URL.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 28/2/17. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import SwiftDrawDOM 33 | import XCTest 34 | @testable import SwiftDraw 35 | 36 | final class URLTests: XCTestCase { 37 | 38 | func testDecodedData() { 39 | let url = URL(maybeData: "data:image/png;base64,f00d") 40 | XCTAssertEqual(url?.decodedData?.mimeType, "image/png") 41 | XCTAssertEqual(url?.decodedData?.data.base64EncodedString(), "f00d") 42 | 43 | XCTAssertNil(URL(maybeData: "data:;base64,f00d")?.decodedData) 44 | XCTAssertNil(URL(maybeData: "data:image/png;bas,f00d")?.decodedData) 45 | XCTAssertNil(URL(maybeData: "data:image/png;base64")?.decodedData) 46 | XCTAssertNil(URL(maybeData: "data:image/png;base64,")?.decodedData) 47 | } 48 | 49 | func testDataURL() { 50 | XCTAssertTrue(URL(maybeData: "data:image/png;base64,f00d")!.isDataURL) 51 | XCTAssertTrue(URL(maybeData: "data:f00d")!.isDataURL) 52 | XCTAssertFalse(URL(maybeData: "#identifier")!.isDataURL) 53 | XCTAssertFalse(URL(maybeData: "data")!.isDataURL) 54 | XCTAssertFalse(URL(maybeData: "www.google.com")!.isDataURL) 55 | } 56 | 57 | func testDecodedDataLineBreak() { 58 | let url = URL(maybeData: "data:image/png;base64,8badf00d\n\t 8badf00d") 59 | XCTAssertEqual(url?.decodedData?.mimeType, "image/png") 60 | XCTAssertEqual(url?.decodedData?.data.base64EncodedString(), "8badf00d8badf00d") 61 | } 62 | 63 | } 64 | 65 | -------------------------------------------------------------------------------- /DOM/Tests/Parser.XML.TextTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.XML.TextTests 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 31/12/16. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | 33 | import Testing 34 | @testable import SwiftDrawDOM 35 | 36 | @Suite("Parser XML Text Tests") 37 | struct ParserXMLTextTests { 38 | 39 | @Test 40 | func textNodeParses() throws { 41 | let el = XML.Element(name: "text", attributes: [:]) 42 | el.innerText = "Simon" 43 | el.attributes["x"] = "1" 44 | el.attributes["y"] = "2" 45 | el.attributes["font-family"] = "Futura" 46 | el.attributes["font-size"] = "12.5" 47 | el.attributes["text-anchor"] = "end" 48 | 49 | let parsed = try XMLParser().parseGraphicsElement(el) as? DOM.Text 50 | let text = try #require(parsed) 51 | 52 | #expect(text.x == 1) 53 | #expect(text.y == 2) 54 | #expect(text.value == "Simon") 55 | #expect(text.attributes.fontFamily == "Futura") 56 | #expect(text.attributes.fontSize == 12.5) 57 | #expect(text.attributes.textAnchor == .end) 58 | } 59 | 60 | @Test 61 | func emptyTextNodeReturnsNil() throws { 62 | let el = XML.Element(name: "text", attributes: [:]) 63 | let first = try XMLParser().parseText(["x": "1", "y": "1"], element: el) 64 | #expect(first == nil) 65 | 66 | el.innerText = " " 67 | let second = try XMLParser().parseText(["x": "1", "y": "1"], element: el) 68 | #expect(second == nil) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Samples.bundle/mask-composite-3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Samples.bundle/effigy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/CanvasUIView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CanvasUIView.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 07/9/25. 6 | // Copyright 2025 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | #if canImport(UIKit) && !os(watchOS) 33 | import UIKit 34 | import SwiftUI 35 | 36 | @available(iOS, deprecated: 15.0, message: "use SwiftUI.Canvas") 37 | struct CanvasFallbackView: UIViewRepresentable { 38 | 39 | var svg: SVG 40 | var capInsets: EdgeInsets 41 | var resizingMode: SVGView.ResizingMode 42 | 43 | func makeUIView(context: Context) -> CanvasUIView { 44 | let uiView = CanvasUIView() 45 | uiView.isOpaque = false 46 | uiView.contentMode = .redraw 47 | return uiView 48 | } 49 | 50 | func updateUIView(_ uiView: CanvasUIView, context: Context) { 51 | uiView.svg = svg 52 | uiView.resizeMode = resizingMode 53 | uiView.capInsets = (capInsets.top, capInsets.leading, capInsets.bottom, capInsets.trailing) 54 | uiView.setNeedsDisplay() 55 | } 56 | } 57 | 58 | final class CanvasUIView: UIView { 59 | 60 | var svg: SVG? 61 | var resizeMode: SVGView.ResizingMode = .stretch 62 | var capInsets: (top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat) = (0, 0, 0, 0) 63 | 64 | override func draw(_ rect: CGRect) { 65 | guard let svg, 66 | let ctx = UIGraphicsGetCurrentContext() else { return } 67 | 68 | ctx.draw( 69 | svg, 70 | in: rect, 71 | capInsets: capInsets, 72 | byTiling: resizeMode == .tile 73 | ) 74 | } 75 | } 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | 92 | # Samples Folder 93 | Samples.bundle/*.pdf 94 | Samples.bundle/*.png 95 | Samples.bundle/*.jpg 96 | Samples.bundle/*.jpeg 97 | Samples.bundle/*.swift 98 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/Utilities/PairedSequenceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PairedSequence.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 6/8/22. 6 | // Copyright 2022 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import XCTest 33 | @testable import SwiftDraw 34 | 35 | final class PairedSequenceTests: XCTestCase { 36 | 37 | func testSequences() { 38 | XCTAssertEqual( 39 | ["a", "b", "c", "d"].paired(with: .nextSkippingLast).map { "\($0)\($1)" }, 40 | ["ab", "bc", "cd"] 41 | ) 42 | 43 | XCTAssertEqual( 44 | ["a", "b", "c", "d"].paired(with: .nextWrappingToFirst).map { "\($0)\($1)" }, 45 | ["ab", "bc", "cd", "da"] 46 | ) 47 | 48 | XCTAssertEqual( 49 | ["a", "b"].paired(with: .nextSkippingLast).map { "\($0)\($1)" }, 50 | ["ab"] 51 | ) 52 | 53 | XCTAssertEqual( 54 | ["a", "b"].paired(with: .nextWrappingToFirst).map { "\($0)\($1)" }, 55 | ["ab", "ba"] 56 | ) 57 | } 58 | 59 | func testEmptySequences() { 60 | XCTAssertEqual( 61 | ["a"].paired(with: .nextSkippingLast).map { "\($0)\($1)" }, 62 | [] 63 | ) 64 | 65 | XCTAssertEqual( 66 | ["a"].paired(with: .nextWrappingToFirst).map { "\($0)\($1)" }, 67 | [] 68 | ) 69 | 70 | XCTAssertEqual( 71 | [].paired(with: .nextSkippingLast).map { "\($0)\($1)" }, 72 | [] 73 | ) 74 | 75 | XCTAssertEqual( 76 | [].paired(with: .nextWrappingToFirst).map { "\($0)\($1)" }, 77 | [] 78 | ) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Samples.bundle/fill-rule.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 38 | 39 | 40 | 41 | 42 | 43 | 51 | 52 | 53 | 54 | 55 | 56 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /DOM/Tests/Parser.XML.FilterTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.XML.FilterTests.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 16/8/22. 6 | // Copyright 2022 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | 33 | import Foundation 34 | 35 | @testable import SwiftDrawDOM 36 | import Testing 37 | 38 | struct ParserXMLFilterTests { 39 | 40 | @Test 41 | func parseFilters() throws { 42 | let child = XML.Element(name: "child") 43 | child.children = [XML.Element.makeMockFilter(), XML.Element.makeMockFilter()] 44 | 45 | let parent = XML.Element(name: "parent") 46 | parent.children = [XML.Element.makeMockFilter(), child] 47 | 48 | #expect(try XMLParser().parseFilters(child).count == 2) 49 | #expect(try XMLParser().parseFilters(parent).count == 3) 50 | } 51 | 52 | @Test 53 | func parseEffect() throws { 54 | let element = XML.Element.makeMockFilter(id: "blur") 55 | 56 | element.children = [ 57 | .makeElement("feGaussianBlur", ["stdDeviation": "0.5"]), 58 | .makeElement("other") 59 | ] 60 | 61 | let filter = try XMLParser().parseFilter(element) 62 | #expect(filter.id == "blur") 63 | #expect(filter.effects == [.gaussianBlur(stdDeviation: 0.5)]) 64 | } 65 | } 66 | 67 | private extension XML.Element { 68 | 69 | static func makeMockFilter(id: String = "mock") -> XML.Element { 70 | return XML.Element(name: "filter", attributes: ["id": id]) 71 | } 72 | 73 | static func makeElement(_ name: String, _ attributes: [String: String] = [:]) -> XML.Element { 74 | return XML.Element(name: name, attributes: attributes) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /DOM/Tests/StyleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StyleTests.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 27/2/17. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | import Testing 33 | @testable import SwiftDrawDOM 34 | 35 | @Suite("Style Tests") 36 | struct StyleTests { 37 | 38 | @Test 39 | func style() throws { 40 | #expect(try XMLParser().parseStyleAttributes("selector: hi;") == ["selector": "hi"]) 41 | #expect(try XMLParser().parseStyleAttributes("selector: hi") == ["selector": "hi"]) 42 | #expect(try XMLParser().parseStyleAttributes("selector: hi ") == ["selector": "hi"]) 43 | #expect(try XMLParser().parseStyleAttributes(" trans-form : rotate(4)") == ["trans-form": "rotate(4)"]) 44 | 45 | #expect(throws: (any Error).self) { 46 | try XMLParser().parseStyleAttributes("selector") 47 | } 48 | #expect(throws: (any Error).self) { 49 | try XMLParser().parseStyleAttributes(": hmm") 50 | } 51 | } 52 | 53 | @Test 54 | func styles() throws { 55 | let e = XML.Element(name: "line") 56 | e.attributes["x"] = "5" 57 | e.attributes["y"] = "5" 58 | e.attributes["stroke-color"] = "black" 59 | e.attributes["style"] = "fill: red; x: 20" 60 | 61 | // Style attributes should override any XML.Element attribute 62 | let att = try XMLParser().parseAttributes(e) 63 | 64 | #expect(try att.parseCoordinate("x") == 20.0) 65 | #expect(try att.parseCoordinate("y") == 5.0) 66 | #expect(try att.parseColor("stroke-color") == .keyword(.black)) 67 | #expect(try att.parseColor("fill") == .keyword(.red)) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /DOM/Sources/DOM.RadialGradient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DOM.RadialGradient.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 13/8/22. 6 | // Copyright 2022 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | package extension DOM { 33 | 34 | final class RadialGradient: Element { 35 | package typealias Units = LinearGradient.Units 36 | 37 | package var id: String 38 | package var r: Coordinate? 39 | package var cx: Coordinate? 40 | package var cy: Coordinate? 41 | package var fr: Coordinate? 42 | package var fx: Coordinate? 43 | package var fy: Coordinate? 44 | 45 | package var stops: [Stop] 46 | package var gradientUnits: Units? 47 | package var gradientTransform: [Transform] 48 | 49 | //references another RadialGradient element id within defs 50 | package var href: URL? 51 | 52 | package init(id: String) { 53 | self.id = id 54 | self.stops = [] 55 | self.gradientTransform = [] 56 | } 57 | 58 | package struct Stop: Equatable { 59 | package var offset: Float 60 | package var color: Color 61 | package var opacity: Float 62 | 63 | package init(offset: Float, color: Color, opacity: Opacity = 1.0) { 64 | self.offset = offset 65 | self.color = color 66 | self.opacity = opacity 67 | } 68 | } 69 | } 70 | } 71 | 72 | extension DOM.RadialGradient: Equatable { 73 | package static func ==(lhs: DOM.RadialGradient, rhs: DOM.RadialGradient) -> Bool { 74 | return lhs.id == rhs.id && lhs.stops == rhs.stops 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/Utilities/CGPattern+Closure.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPattern+Closure.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 28/3/19. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | #if canImport(CoreGraphics) 33 | import CoreGraphics 34 | 35 | extension CGPattern { 36 | 37 | static func make(bounds: CGRect, 38 | matrix: CGAffineTransform, 39 | step: CGSize, 40 | tiling: CGPatternTiling, 41 | isColored: Bool, 42 | draw: @escaping (CGContext) -> Void) -> CGPattern { 43 | 44 | let drawPattern: CGPatternDrawPatternCallback = { info, ctx in 45 | let box = Unmanaged.fromOpaque(info!).takeUnretainedValue() 46 | box.closure(ctx) 47 | } 48 | 49 | let releaseInfo: CGPatternReleaseInfoCallback = { info in 50 | Unmanaged.fromOpaque(info!).release() 51 | } 52 | 53 | var callbacks = CGPatternCallbacks(version: 0, 54 | drawPattern: drawPattern, 55 | releaseInfo: releaseInfo) 56 | 57 | return CGPattern(info: Unmanaged.passRetained(Box(draw)).toOpaque(), 58 | bounds: bounds, 59 | matrix: matrix, 60 | xStep: step.width, 61 | yStep: step.height, 62 | tiling: tiling, 63 | isColored: isColored, 64 | callbacks: &callbacks)! 65 | } 66 | 67 | private final class Box { 68 | let closure: (CGContext) -> Void 69 | init(_ closure: @escaping (CGContext) -> Void) { 70 | self.closure = closure 71 | } 72 | } 73 | } 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /.swiftpm/xcode/xcshareddata/xcschemes/SwiftDraw.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 16 | 22 | 23 | 24 | 25 | 26 | 31 | 32 | 35 | 36 | 37 | 38 | 48 | 49 | 55 | 56 | 62 | 63 | 64 | 65 | 67 | 68 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /SwiftDraw/Tests/UIImage+SVGTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+SVGTests.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 27/11/18. 6 | // Copyright 2020 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | @testable import SwiftDraw 33 | import Testing 34 | 35 | #if canImport(UIKit) 36 | import UIKit 37 | 38 | struct UIImageSVGTests { 39 | 40 | @Test 41 | func imageLoads() { 42 | let image = UIImage(svgNamed: "lines.svg", in: .test) 43 | #expect(image != nil) 44 | } 45 | 46 | @Test 47 | func missingImageDoesNotLoad() { 48 | let image = UIImage(svgNamed: "missing.svg", in: .test) 49 | #expect(image == nil) 50 | } 51 | 52 | @Test 53 | func imageSize() throws { 54 | let image = try SVG.parseXML(#""" 55 | 56 | 57 | 58 | """# 59 | ) 60 | 61 | #expect( 62 | image.rasterize(scale: 1).size == CGSize(width: 64, height: 64) 63 | ) 64 | #expect( 65 | image.rasterize(scale: 1).scale == 1 66 | ) 67 | #expect( 68 | image.rasterize(scale: 2).size == CGSize(width: 64, height: 64) 69 | ) 70 | #expect( 71 | image.rasterize(scale: 2).scale == 2 72 | ) 73 | } 74 | } 75 | 76 | private extension SVG { 77 | 78 | static func parseXML(_ xml: String) throws -> SVG { 79 | guard let svg = SVG(xml: xml) else { 80 | throw InvalidSVG() 81 | } 82 | return svg 83 | } 84 | 85 | private struct InvalidSVG: LocalizedError { 86 | var errorDescription: String? = "Invalid SVG" 87 | } 88 | } 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /SwiftDraw/Sources/CanvasNSView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CanvasNSView.swift 3 | // SwiftDraw 4 | // 5 | // Created by Simon Whitty on 07/9/25. 6 | // Copyright 2025 Simon Whitty 7 | // 8 | // Distributed under the permissive zlib license 9 | // Get the latest version from here: 10 | // 11 | // https://github.com/swhitty/SwiftDraw 12 | // 13 | // This software is provided 'as-is', without any express or implied 14 | // warranty. In no event will the authors be held liable for any damages 15 | // arising from the use of this software. 16 | // 17 | // Permission is granted to anyone to use this software for any purpose, 18 | // including commercial applications, and to alter it and redistribute it 19 | // freely, subject to the following restrictions: 20 | // 21 | // 1. The origin of this software must not be misrepresented; you must not 22 | // claim that you wrote the original software. If you use this software 23 | // in a product, an acknowledgment in the product documentation would be 24 | // appreciated but is not required. 25 | // 26 | // 2. Altered source versions must be plainly marked as such, and must not be 27 | // misrepresented as being the original software. 28 | // 29 | // 3. This notice may not be removed or altered from any source distribution. 30 | // 31 | 32 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 33 | import AppKit 34 | import SwiftUI 35 | 36 | @available(macOS, deprecated: 12.0, message: "use SwiftUI.Canvas") 37 | struct CanvasFallbackView: NSViewRepresentable { 38 | 39 | var svg: SVG 40 | var capInsets: EdgeInsets 41 | var resizingMode: SVGView.ResizingMode 42 | 43 | func makeNSView(context: Context) -> CanvasNSView { 44 | let nsView = CanvasNSView() 45 | nsView.wantsLayer = true 46 | nsView.layerContentsRedrawPolicy = .duringViewResize 47 | nsView.layer?.needsDisplayOnBoundsChange = true 48 | return nsView 49 | } 50 | 51 | func updateNSView(_ nsView: CanvasNSView, context: Context) { 52 | nsView.svg = svg 53 | nsView.resizeMode = resizingMode 54 | nsView.capInsets = (capInsets.top, capInsets.leading, capInsets.bottom, capInsets.trailing) 55 | nsView.needsDisplay = true 56 | } 57 | } 58 | 59 | final class CanvasNSView: NSView { 60 | 61 | var svg: SVG? 62 | var resizeMode: SVGView.ResizingMode = .stretch 63 | var capInsets: (top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat) = (0, 0, 0, 0) 64 | 65 | override var isFlipped: Bool { true } 66 | 67 | override func draw(_ dirtyRect: NSRect) { 68 | guard let svg, 69 | let ctx = NSGraphicsContext.current?.cgContext else { return } 70 | 71 | ctx.draw( 72 | svg, 73 | in: bounds, 74 | capInsets: capInsets, 75 | byTiling: resizeMode == .tile 76 | ) 77 | } 78 | } 79 | 80 | #endif 81 | --------------------------------------------------------------------------------