├── 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 |
--------------------------------------------------------------------------------
/SwiftDraw/Tests/Test.bundle/invalid.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
6 |
--------------------------------------------------------------------------------
/Examples/Basic.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftDraw/Tests/Test.bundle/lines.svg:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/Samples.bundle/chart.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/SwiftDraw/Tests/Test.bundle/chart.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Samples.bundle/feather-activity.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Samples.bundle/simple.svg:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/Samples.bundle/units-cm.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Examples/Basic.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Samples.bundle/path-close.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Samples.bundle/units.svg:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/Samples.bundle/key/key.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Samples.bundle/viewbox.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Samples.bundle/feather-airplay.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Samples.bundle/key/key-black.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/SwiftDraw/Tests/Test.bundle/key.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Samples.bundle/key/key-ultralight.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Samples.bundle/tabler-anchor.svg:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Samples.bundle/spy-glass.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Samples.bundle/cjk-fallback.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/Samples.bundle/dash.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/SwiftDraw/Tests/Test.bundle/dash.svg:
--------------------------------------------------------------------------------
1 |
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 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Samples.bundle/rect.svg:
--------------------------------------------------------------------------------
1 |
2 |
16 |
--------------------------------------------------------------------------------
/Samples.bundle/path.svg:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Samples.bundle/doc.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/Samples.bundle/rgba.svg:
--------------------------------------------------------------------------------
1 |
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 |
--------------------------------------------------------------------------------
/Samples.bundle/gradient2.svg:
--------------------------------------------------------------------------------
1 |
2 |
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 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Samples.bundle/amex.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Samples.bundle/display.svg:
--------------------------------------------------------------------------------
1 |
2 |
26 |
--------------------------------------------------------------------------------
/Samples.bundle/gradient-stroke.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
23 |
--------------------------------------------------------------------------------
/Samples.bundle/use.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Samples.bundle/pattern.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Samples.bundle/pattern-bounding.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Samples.bundle/alert.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/Samples.bundle/mask-composite.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Samples.bundle/abc.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/Samples.bundle/blend.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
70 |
--------------------------------------------------------------------------------
/DOM/Tests/Test.bundle/stylesheet.svg:
--------------------------------------------------------------------------------
1 |
2 |
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 |
70 |
--------------------------------------------------------------------------------
/Samples.bundle/circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
11 |
--------------------------------------------------------------------------------
/Samples.bundle/thats-no-moon.svg:
--------------------------------------------------------------------------------
1 |
2 |
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 |
35 |
--------------------------------------------------------------------------------
/Samples.bundle/gradient-gratification.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/SwiftDraw/Tests/Test.bundle/gradient-gratification.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
49 |
--------------------------------------------------------------------------------
/Samples.bundle/identity.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/Samples.bundle/gradient-gratification-p3.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Samples.bundle/radialGradient.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Samples.bundle/stars.svg:
--------------------------------------------------------------------------------
1 |
2 |
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 |
--------------------------------------------------------------------------------
/SwiftDraw/Tests/Test.bundle/radialGradient.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/DOM/Tests/Test.bundle/linearGradient.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Samples.bundle/arc.svg:
--------------------------------------------------------------------------------
1 |
2 |
31 |
--------------------------------------------------------------------------------
/SwiftDraw/Tests/Test.bundle/linearGradient.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Samples.bundle/nested-svg.svg:
--------------------------------------------------------------------------------
1 |
2 |
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 |
--------------------------------------------------------------------------------