TRAVIS_OS_NAME=osx default= <<<<<< ENV ./codecov.yml .swift-version CommandLine/CommandLine.swift CommandLine/main.swift Examples/Basic/Basic.xcodeproj/project.pbxproj Examples/Basic/Basic.xcodeproj/project.xcworkspace/contents.xcworkspacedata Examples/Basic/Basic.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist Examples/Basic/Sources/AppDelegate.swift Examples/Basic/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json Examples/Basic/Sources/Assets.xcassets/Contents.json Examples/Basic/Sources/Base.lproj/LaunchScreen.storyboard Examples/Basic/Sources/Info.plist Examples/Basic/Sources/ViewController.swift Examples/Examples.xcworkspace/contents.xcworkspacedata Examples/Examples.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist Examples/Examples.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings LinuxMain.swift Package.swift Samples/arc.svg Samples/arc2.svg Samples/blend.svg Samples/curves.svg Samples/display.svg Samples/gradient-gratification.svg Samples/gradient.svg Samples/gradient2.svg Samples/identity.svg Samples/invalid.svg Samples/lines.svg Samples/mask-composite-2.svg Samples/mask-composite-3.svg Samples/mask-composite.svg Samples/mask.svg Samples/mystery.svg Samples/path.svg Samples/pattern-bounding.svg Samples/pattern.svg Samples/quad.svg Samples/shapes.svg Samples/starry.svg Samples/thats-no-moon.svg Samples/transform.svg Samples/use.svg Samples/viewbox.svg SwiftDraw.podspec.json SwiftDraw.xcodeproj/project.pbxproj SwiftDraw.xcodeproj/project.xcworkspace/contents.xcworkspacedata SwiftDraw.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist SwiftDraw.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings SwiftDraw.xcodeproj/xcshareddata/xcschemes/SwiftDraw-macOS.xcscheme SwiftDraw.xcodeproj/xcshareddata/xcschemes/SwiftDraw.xcscheme SwiftDraw/CGImage+Mask.swift SwiftDraw/CGPath+Segment.swift SwiftDraw/CGPattern+Closure.swift SwiftDraw/CommandLine.Arguments.swift SwiftDraw/CommandLine.Configuration.swift SwiftDraw/CommandLine.swift SwiftDraw/DOM.Color.swift SwiftDraw/DOM.Element.swift SwiftDraw/DOM.Gradient.swift SwiftDraw/DOM.Image.swift SwiftDraw/DOM.Path.swift SwiftDraw/DOM.Pattern.swift SwiftDraw/DOM.SVG.swift SwiftDraw/DOM.Switch.swift SwiftDraw/DOM.Text.swift SwiftDraw/DOM.Use.swift SwiftDraw/DOM.swift SwiftDraw/Formatter.XML.swift SwiftDraw/Formatter.swift SwiftDraw/Image+CoreGraphics.swift SwiftDraw/Image.swift SwiftDraw/Info.plist SwiftDraw/LayerTree.Builder.Layer.swift SwiftDraw/LayerTree.Builder.Path.Arc.swift SwiftDraw/LayerTree.Builder.Path.swift SwiftDraw/LayerTree.Builder.Shape.swift SwiftDraw/LayerTree.Builder.swift SwiftDraw/LayerTree.Color.swift SwiftDraw/LayerTree.CommandGenerator.swift SwiftDraw/LayerTree.Gradient.swift SwiftDraw/LayerTree.Image.swift SwiftDraw/LayerTree.Layer.swift SwiftDraw/LayerTree.Path.swift SwiftDraw/LayerTree.Pattern.swift SwiftDraw/LayerTree.Shape.swift SwiftDraw/LayerTree.Transform.swift SwiftDraw/LayerTree.swift SwiftDraw/NSImage+Image.swift SwiftDraw/Parser.XML.Attributes.swift SwiftDraw/Parser.XML.Color.swift SwiftDraw/Parser.XML.Element.swift SwiftDraw/Parser.XML.Gradient.swift SwiftDraw/Parser.XML.Image.swift SwiftDraw/Parser.XML.Path.swift SwiftDraw/Parser.XML.Pattern.swift SwiftDraw/Parser.XML.SVG.swift SwiftDraw/Parser.XML.Text.swift SwiftDraw/Parser.XML.Transform.swift SwiftDraw/Parser.XML.Use.swift SwiftDraw/Parser.XML.swift SwiftDraw/Renderer.CoreGraphics.swift SwiftDraw/Renderer.LayerTree.swift SwiftDraw/Renderer.swift SwiftDraw/Stack.swift SwiftDraw/SwiftDraw-macOS.h SwiftDraw/SwiftDraw.h SwiftDraw/UIImage+Image.swift SwiftDraw/URL+Data.swift SwiftDraw/XML.Parser.Scanner.swift SwiftDraw/XML.SAXParser.swift SwiftDraw/XML.swift SwiftDrawTests/Bundle+Extensions.swift SwiftDrawTests/CGPath+SegmentTests.swift SwiftDrawTests/CGRenderer.PathTests.swift SwiftDrawTests/CGRendererTests.swift SwiftDrawTests/CommandLine.ArgumentsTests.swift SwiftDrawTests/CommandLine.ConfigurationTests.swift SwiftDrawTests/CoordinateTests.swift SwiftDrawTests/DOM+Extensions.swift SwiftDrawTests/DOM.ElementTests.swift SwiftDrawTests/GradientTests.swift SwiftDrawTests/ImageTests.swift SwiftDrawTests/Info.plist SwiftDrawTests/LayerTree.Builder.LayerTests.swift SwiftDrawTests/LayerTree.Builder.ShapeTests.swift SwiftDrawTests/LayerTree.BuilderTests.swift SwiftDrawTests/LayerTree.ColorTests.swift SwiftDrawTests/LayerTree.CommandGeneratorTests.swift SwiftDrawTests/LayerTree.ImageTests.swift SwiftDrawTests/LayerTree.LayerTests.swift SwiftDrawTests/LayerTree.PathTests.swift SwiftDrawTests/LayerTree.ShapeTests.swift SwiftDrawTests/LayerTree.TransformTests.swift SwiftDrawTests/MockRenderer.swift SwiftDrawTests/NSBitmapImageRep+Extensions.swift SwiftDrawTests/NSImage+ImageTests.swift SwiftDrawTests/Parser.AttributesTests.swift SwiftDrawTests/Parser.GraphicAttributeTests.swift SwiftDrawTests/Parser.SVGTests.swift SwiftDrawTests/Parser.XML.ColorTests.swift SwiftDrawTests/Parser.XML.ElementTests.swift SwiftDrawTests/Parser.XML.GradientTests.swift SwiftDrawTests/Parser.XML.ImageTests.swift SwiftDrawTests/Parser.XML.PathTests.swift SwiftDrawTests/Parser.XML.PatternTests.swift SwiftDrawTests/Parser.XML.TextTests.swift SwiftDrawTests/Parser.XML.TransformTests.swift SwiftDrawTests/ParserSVGImageTests.swift SwiftDrawTests/Renderer.CoreGraphicsTypesTests.swift SwiftDrawTests/Renderer.LayerTreeProviderTests.swift SwiftDrawTests/RendererTests.swift SwiftDrawTests/StackTests.swift SwiftDrawTests/StyleTests.swift SwiftDrawTests/UIImage+ImageTests.swift SwiftDrawTests/URL+DataTests.swift SwiftDrawTests/UseTests.swift SwiftDrawTests/ValueParserTests.swift SwiftDrawTests/XCTestManifests.swift SwiftDrawTests/XML.Parser.ScannerTests.swift SwiftDrawTests/XML.SAXParserTests.swift <<<<<< network # path=SwiftDraw.framework.coverage.txt /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| |import CoreGraphics 33| |import Foundation 34| | 35| 0|func CGColorSpaceCreateExtendedGray() -> CGColorSpace { 36| 0| return CGColorSpace(name: CGColorSpace.extendedGray)! 37| 0|} 38| | 39| |extension CGImage { 40| | 41| 0| static func makeMask(size: CGSize, draw: (CGContext) -> ()) -> CGImage { 42| 0| 43| 0| let width = Int(size.width) 44| 0| let height = Int(size.height) 45| 0| 46| 0| var data = Data(repeating: 0xff, count: width*height) 47| 0| data.withUnsafeMutableBytes { 48| 0| let ctx = CGContext(data: $0.baseAddress, 49| 0| width: width, 50| 0| height: height, 51| 0| bitsPerComponent: 8, 52| 0| bytesPerRow: width, 53| 0| space: CGColorSpaceCreateDeviceGray(), 54| 0| bitmapInfo: 0)! 55| 0| draw(ctx) 56| 0| } 57| 0| 58| 0| return CGImage(maskWidth: width, 59| 0| height: height, 60| 0| bitsPerComponent: 8, 61| 0| bitsPerPixel: 8, 62| 0| bytesPerRow: width, 63| 0| provider: CGDataProvider(data: data as CFData)!, 64| 0| decode: nil, 65| 0| shouldInterpolate: true)! 66| 0| } 67| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/CGPath+Segment.swift: 1| |// 2| |// CGPath+Segment.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 10/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| | 32| |import CoreGraphics 33| |import CoreText 34| |import Foundation 35| |#if os(macOS) 36| |import AppKit 37| |#elseif os(iOS) 38| |import UIKit 39| |#endif 40| | 41| |extension CGPath { 42| 6| func doApply(action: @escaping (CGPathElement)->()) { 43| 6| var action = action 44| 29| apply(info: &action) { 45| 29| let action = $0!.bindMemory(to: ((CGPathElement)->()).self, capacity: 1).pointee 46| 29| action($1.pointee) 47| 29| } 48| 6| } 49| |} 50| | 51| |extension CGPath { 52| | enum Segment: Equatable { 53| | case move(CGPoint) 54| | case line(CGPoint) 55| | case quad(CGPoint, CGPoint) 56| | case cubic(CGPoint, CGPoint, CGPoint) 57| | case close 58| | } 59| | 60| 6| func segments() -> [Segment] { 61| 6| var segments = [Segment]() 62| 29| self.doApply { 63| 29| let p = $0 64| 29| switch (p.type) { 65| 29| case .moveToPoint: 66| 7| segments.append(Segment.move(p.points[0])) 67| 29| case .addLineToPoint: 68| 14| segments.append(Segment.line(p.points[0])) 69| 29| case .addQuadCurveToPoint: 70| 1| segments.append(Segment.quad(p.points[0], p.points[1])) 71| 29| case .addCurveToPoint: 72| 1| segments.append(Segment.cubic(p.points[0], p.points[1], p.points[2])) 73| 29| case .closeSubpath: 74| 6| segments.append(Segment.close) 75| 29| @unknown default: 76| 0| () 77| 29| } 78| 29| } 79| 6| return segments 80| 6| } 81| |} 82| | 83| |extension String { 84| | 85| 2| func toPath(font: CTFont) -> CGPath? { 86| 2| let attributes = [kCTFontAttributeName: font] 87| 2| let attString = CFAttributedStringCreate(nil, self as CFString, attributes as CFDictionary)! 88| 2| let line = CTLineCreateWithAttributedString(attString) 89| 2| let glyphRuns = CTLineGetGlyphRuns(line) 90| 2| 91| 2| var ascent = CGFloat(0) 92| 2| var descent = CGFloat(0) 93| 2| var leading = CGFloat(0) 94| 2| CTLineGetTypographicBounds(line, &ascent, &descent, &leading) 95| 2| let baseline = ascent 96| 2| 97| 2| 98| 2| let path = CGMutablePath() 99| 2| 100| 2| for idx in 0.. Void) -> CGPattern { 42| 0| 43| 0| let drawPattern: CGPatternDrawPatternCallback = { info, ctx in 44| 0| let box = Unmanaged.fromOpaque(info!).takeUnretainedValue() 45| 0| box.closure(ctx) 46| 0| } 47| 0| 48| 0| let releaseInfo: CGPatternReleaseInfoCallback = { info in 49| 0| Unmanaged.fromOpaque(info!).release() 50| 0| } 51| 0| 52| 0| var callbacks = CGPatternCallbacks(version: 0, 53| 0| drawPattern: drawPattern, 54| 0| releaseInfo: releaseInfo) 55| 0| 56| 0| return CGPattern(info: Unmanaged.passRetained(Box(draw)).toOpaque(), 57| 0| bounds: bounds, 58| 0| matrix: matrix, 59| 0| xStep: step.width, 60| 0| yStep: step.height, 61| 0| tiling: tiling, 62| 0| isColored: isColored, 63| 0| callbacks: &callbacks)! 64| 0| } 65| | 66| | private final class Box { 67| | let closure: (CGContext) -> Void 68| 0| init(_ closure: @escaping (CGContext) -> Void) { 69| 0| self.closure = closure 70| 0| } 71| | } 72| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/CommandLine.Arguments.swift: 1| |// 2| |// CommandLine.Arguments.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| |extension CommandLine { 35| | 36| | enum Modifier: String { 37| | case format 38| | case output 39| | case size 40| | case scale 41| | 42| 22| static func parse(from string: String) -> Modifier? { 43| 22| guard string.hasPrefix("--") else { 44| 2| return nil 45| 20| } 46| 20| 47| 20| return Modifier(rawValue: String(string.dropFirst(2))) 48| 22| } 49| | } 50| | 51| 13| static func parseModifiers(from args: [String]) throws -> [Modifier: String] { 52| 13| var args = args 53| 13| var modifiers = [Modifier: String]() 54| 22| while let pair = args.takePair() { 55| 22| if let modifier = Modifier.parse(from: pair.0), modifiers.keys.contains(modifier) == false { 56| 16| modifiers[modifier] = pair.1 57| 22| } else { 58| 6| throw Error.invalid 59| 16| } 60| 16| } 61| 7| 62| 7| guard args.isEmpty else { 63| 2| throw CommandLine.Error.invalid 64| 5| } 65| 5| 66| 5| return modifiers 67| 7| } 68| | 69| | public enum Error: Swift.Error { 70| | case invalid 71| | } 72| |} 73| | 74| |private extension Array where Element == String { 75| | 76| 29| mutating func takePair() -> (String, String)? { 77| 29| guard count > 1 else { 78| 7| return nil 79| 22| } 80| 22| 81| 22| let pair = (self[0], self[1]) 82| 22| removeFirst(2) 83| 22| return pair 84| 29| } 85| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/CommandLine.Configuration.swift: 1| |// 2| |// CommandLine.Configuration.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| |extension CommandLine { 35| | 36| | public struct Configuration { 37| | public var input: URL 38| | public var output: URL 39| | public var format: Format 40| | public var size: Size 41| | public var scale: Scale 42| | } 43| | 44| | public enum Format: String { 45| | case jpeg 46| | case pdf 47| | case png 48| | } 49| | 50| | public enum Size: Equatable { 51| | case `default` 52| | case custom(width: Int, height: Int) 53| | } 54| | 55| | public enum Scale: Equatable { 56| | case `default` 57| | case retina 58| | case superRetina 59| | } 60| | 61| 6| public static func parseConfiguration(from args: [String], baseDirectory: URL) throws -> Configuration { 62| 6| guard args.count > 2 else { 63| 2| throw Error.invalid 64| 4| } 65| 4| 66| 4| let source = try parseFileURL(file: args[1], within: baseDirectory) 67| 4| let modifiers = try parseModifiers(from: Array(args.dropFirst(2))) 68| 4| guard 69| 4| let formatString = modifiers[.format], 70| 4| let format = Format(rawValue: formatString) else { 71| 1| throw Error.invalid 72| 3| } 73| 3| 74| 3| let size = try parseSize(from: modifiers[.size]) 75| 3| let scale = try parseScale(from: modifiers[.scale]) 76| 3| let result = source.newURL(for: format, scale: scale) 77| 3| return Configuration(input: source, 78| 3| output: result, 79| 3| format: format, 80| 3| size: size, 81| 3| scale: scale) 82| 4| } 83| | 84| 7| static func parseFileURL(file: String, within directory: URL) throws -> URL { 85| 7| guard #available(macOS 10.11, *) else { 86| 0| throw Error.invalid 87| 7| } 88| 7| 89| 7| return URL(fileURLWithPath: file, relativeTo: directory).standardizedFileURL 90| 7| } 91| | 92| 3| static func parseScale(from value: String?) throws -> Scale { 93| 3| guard let value = value else { 94| 2| return .default 95| 2| } 96| 1| guard let scale = Scale(value) else { 97| 0| throw Error.invalid 98| 1| } 99| 1| return scale 100| 1| } 101| | 102| 3| static func parseSize(from value: String?) throws -> Size { 103| 3| guard let value = value else { 104| 2| return .default 105| 2| } 106| 1| 107| 1| let scanner = Scanner(string: value) 108| 1| var width: Int32 = 0 109| 1| var height: Int32 = 0 110| 1| guard 111| 1| scanner.scanInt32(&width), 112| 1| scanner.scanString("x", into: nil), 113| 1| scanner.scanInt32(&height), 114| 1| width > 0, height > 0 else { 115| 0| throw Error.invalid 116| 1| } 117| 1| 118| 1| return .custom(width: Int(width), height: Int(height)) 119| 1| } 120| |} 121| | 122| |extension URL { 123| | 124| 7| var lastPathComponentName: String { 125| 7| let filename = lastPathComponent 126| 7| let extensionOffset = pathExtension.isEmpty ? 0 : -pathExtension.count - 1 127| 7| let index = filename.index(filename.endIndex, offsetBy: extensionOffset) 128| 7| return String(filename[.. URL { 132| 7| let newFilename = lastPathComponentName(scale: scale) 133| 7| let newFilenameWithExtension = "\(newFilename).\(format.pathExtension)" 134| 7| return deletingLastPathComponent() 135| 7| .appendingPathComponent(newFilenameWithExtension) 136| 7| .standardizedFileURL 137| 7| } 138| | 139| 7| func lastPathComponentName(scale: CommandLine.Scale) -> String { 140| 7| switch scale { 141| 7| case .default: 142| 6| return lastPathComponentName 143| 7| case .retina: 144| 1| return "\(lastPathComponentName)@2x" 145| 7| case .superRetina: 146| 0| return "\(lastPathComponentName)@3x" 147| 7| } 148| 7| } 149| |} 150| | 151| |private extension CommandLine.Format { 152| | 153| 7| var pathExtension: String { 154| 7| switch self { 155| 7| case .jpeg: 156| 2| return "jpg" 157| 7| case .pdf: 158| 2| return "pdf" 159| 7| case .png: 160| 3| return "png" 161| 7| } 162| 7| } 163| |} 164| | 165| |private extension CommandLine.Scale { 166| | 167| 1| init?(_ value: String) { 168| 1| switch value { 169| 1| case "1x": 170| 0| self = .default 171| 1| case "2x": 172| 1| self = .retina 173| 1| case "3x": 174| 0| self = .superRetina 175| 1| default: 176| 0| return nil 177| 1| } 178| 1| } 179| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/DOM.Color.swift: 1| |// 2| |// DOM.Color.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 DOM { 33| | 34| | enum Color: Equatable { 35| | case none 36| | case keyword(Keyword) 37| | case rgbi(UInt8, UInt8, UInt8) 38| | case rgbf(DOM.Float, DOM.Float, DOM.Float) 39| | case hex(UInt8, UInt8, UInt8) 40| | 41| | // see: https://www.w3.org/TR/SVG11/types.html#ColorKeywords 42| | enum Keyword: String { 43| | case aliceblue 44| | case antiquewhite 45| | case aqua 46| | case aquamarine 47| | case azure 48| | case beige 49| | case bisque 50| | case black 51| | case blanchedalmond 52| | case blue 53| | case blueviolet 54| | case brown 55| | case burlywood 56| | case cadetblue 57| | case chartreuse 58| | case chocolate 59| | case coral 60| | case cornflowerblue 61| | case cornsilk 62| | case crimson 63| | case cyan 64| | case darkblue 65| | case darkcyan 66| | case darkgoldenrod 67| | case darkgray 68| | case darkgreen 69| | case darkgrey 70| | case darkkhaki 71| | case darkmagenta 72| | case darkolivegreen 73| | case darkorange 74| | case darkorchid 75| | case darkred 76| | case darksalmon 77| | case darkseagreen 78| | case darkslateblue 79| | case darkslategray 80| | case darkslategrey 81| | case darkturquoise 82| | case darkviolet 83| | case deeppink 84| | case deepskyblue 85| | case dimgray 86| | case dimgrey 87| | case dodgerblue 88| | case firebrick 89| | case floralwhite 90| | case forestgreen 91| | case fuchsia 92| | case gainsboro 93| | case ghostwhite 94| | case gold 95| | case goldenrod 96| | case gray 97| | case grey 98| | case green 99| | case greenyellow 100| | case honeydew 101| | case hotpink 102| | case indianred 103| | case indigo 104| | case ivory 105| | case khaki 106| | case lavender 107| | case lavenderblush 108| | case lawngreen 109| | case lemonchiffon 110| | case lightblue 111| | case lightcoral 112| | case lightcyan 113| | case lightgoldenrodyellow 114| | case lightgray 115| | case lightgreen 116| | case lightgrey 117| | case lightpink 118| | case lightsalmon 119| | case lightseagreen 120| | case lightskyblue 121| | case lightslategray 122| | case lightslategrey 123| | case lightsteelblue 124| | case lightyellow 125| | case lime 126| | case limegreen 127| | case linen 128| | case magenta 129| | case maroon 130| | case mediumaquamarine 131| | case mediumblue 132| | case mediumorchid 133| | case mediumpurple 134| | case mediumseagreen 135| | case mediumslateblue 136| | case mediumspringgreen 137| | case mediumturquoise 138| | case mediumvioletred 139| | case midnightblue 140| | case mintcream 141| | case mistyrose 142| | case moccasin 143| | case navajowhite 144| | case navy 145| | case oldlace 146| | case olive 147| | case olivedrab 148| | case orange 149| | case orangered 150| | case orchid 151| | case palegoldenrod 152| | case palegreen 153| | case paleturquoise 154| | case palevioletred 155| | case papayawhip 156| | case peachpuff 157| | case peru 158| | case pink 159| | case plum 160| | case powderblue 161| | case purple 162| | case red 163| | case rosybrown 164| | case royalblue 165| | case saddlebrown 166| | case salmon 167| | case sandybrown 168| | case seagreen 169| | case seashell 170| | case sienna 171| | case silver 172| | case skyblue 173| | case slateblue 174| | case slategray 175| | case slategrey 176| | case snow 177| | case springgreen 178| | case steelblue 179| | case tan 180| | case teal 181| | case thistle 182| | case tomato 183| | case turquoise 184| | case violet 185| | case wheat 186| | case white 187| | case whitesmoke 188| | case yellow 189| | case yellowgreen 190| | } 191| | } 192| |} 193| | 194| |extension DOM.Color.Keyword { 195| | 196| | // each color keyword maps to an rgbi 197| 27| var rgbi: (UInt8, UInt8, UInt8) { 198| 27| switch self { 199| 27| case .aliceblue: return (240, 248, 255) 200| 27| case .antiquewhite: return (250, 235, 215) 201| 27| case .aqua: return (0, 255, 255) 202| 27| case .aquamarine: return (127, 255, 212) 203| 27| case .azure: return (240, 255, 255) 204| 27| case .beige: return (245, 245, 220) 205| 27| case .bisque: return (255, 228, 196) 206| 27| case .black: return (0, 0, 0) 207| 27| case .blanchedalmond: return (255, 235, 205) 208| 27| case .blue: return (0, 0, 255) 209| 27| case .blueviolet: return (138, 43, 226) 210| 27| case .brown: return (165, 42, 42) 211| 27| case .burlywood: return (222, 184, 135) 212| 27| case .cadetblue: return (95, 158, 160) 213| 27| case .chartreuse: return (127, 255, 0) 214| 27| case .chocolate: return (210, 105, 30) 215| 27| case .coral: return (255, 127, 80) 216| 27| case .cornflowerblue: return (100, 149, 237) 217| 27| case .cornsilk: return (255, 248, 220) 218| 27| case .crimson: return (220, 20, 60) 219| 27| case .cyan: return (0, 255, 255) 220| 27| case .darkblue: return (0, 0, 139) 221| 27| case .darkcyan: return (0, 139, 139) 222| 27| case .darkgoldenrod: return (184, 134, 11) 223| 27| case .darkgray: return (169, 169, 169) 224| 27| case .darkgreen: return (0, 100, 0) 225| 27| case .darkgrey: return (169, 169, 169) 226| 27| case .darkkhaki: return (189, 183, 107) 227| 27| case .darkmagenta: return (139, 0, 139) 228| 27| case .darkolivegreen: return (85, 107, 47) 229| 27| case .darkorange: return (255, 140, 0) 230| 27| case .darkorchid: return (153, 50, 204) 231| 27| case .darkred: return (139, 0, 0) 232| 27| case .darksalmon: return (233, 150, 122) 233| 27| case .darkseagreen: return (143, 188, 143) 234| 27| case .darkslateblue: return (72, 61, 139) 235| 27| case .darkslategray: return (47, 79, 79) 236| 27| case .darkslategrey: return (47, 79, 79) 237| 27| case .darkturquoise: return (0, 206, 209) 238| 27| case .darkviolet: return (148, 0, 211) 239| 27| case .deeppink: return (255, 20, 147) 240| 27| case .deepskyblue: return (0, 191, 255) 241| 27| case .dimgray: return (105, 105, 105) 242| 27| case .dimgrey: return (105, 105, 105) 243| 27| case .dodgerblue: return (30, 144, 255) 244| 27| case .firebrick: return (178, 34, 34) 245| 27| case .floralwhite: return (255, 250, 240) 246| 27| case .forestgreen: return (34, 139, 34) 247| 27| case .fuchsia: return (255, 0, 255) 248| 27| case .gainsboro: return (220, 220, 220) 249| 27| case .ghostwhite: return (248, 248, 255) 250| 27| case .gold: return (255, 215, 0) 251| 27| case .goldenrod: return (218, 165, 32) 252| 27| case .gray: return (128, 128, 128) 253| 27| case .grey: return (128, 128, 128) 254| 27| case .green: return (0, 128, 0) 255| 27| case .greenyellow: return (173, 255, 47) 256| 27| case .honeydew: return (240, 255, 240) 257| 27| case .hotpink: return (255, 105, 180) 258| 27| case .indianred: return (205, 92, 92) 259| 27| case .indigo: return (75, 0, 130) 260| 27| case .ivory: return (255, 255, 240) 261| 27| case .khaki: return (240, 230, 140) 262| 27| case .lavender: return (230, 230, 250) 263| 27| case .lavenderblush: return (255, 240, 245) 264| 27| case .lawngreen: return (124, 252, 0) 265| 27| case .lemonchiffon: return (255, 250, 205) 266| 27| case .lightblue: return (173, 216, 230) 267| 27| case .lightcoral: return (240, 128, 128) 268| 27| case .lightcyan: return (224, 255, 255) 269| 27| case .lightgoldenrodyellow: return (250, 250, 210) 270| 27| case .lightgray: return (211, 211, 211) 271| 27| case .lightgreen: return (144, 238, 144) 272| 27| case .lightgrey: return (211, 211, 211) 273| 27| case .lightpink: return (255, 182, 193) 274| 27| case .lightsalmon: return (255, 160, 122) 275| 27| case .lightseagreen: return (32, 178, 170) 276| 27| case .lightskyblue: return (135, 206, 250) 277| 27| case .lightslategray: return (119, 136, 153) 278| 27| case .lightslategrey: return (119, 136, 153) 279| 27| case .lightsteelblue: return (176, 196, 222) 280| 27| case .lightyellow: return (255, 255, 224) 281| 27| case .lime: return (0, 255, 0) 282| 27| case .limegreen: return (50, 205, 50) 283| 27| case .linen: return (250, 240, 230) 284| 27| case .magenta: return (255, 0, 255) 285| 27| case .maroon: return (128, 0, 0) 286| 27| case .mediumaquamarine: return (102, 205, 170) 287| 27| case .mediumblue: return (0, 0, 205) 288| 27| case .mediumorchid: return (186, 85, 211) 289| 27| case .mediumpurple: return (147, 112, 219) 290| 27| case .mediumseagreen: return (60, 179, 113) 291| 27| case .mediumslateblue: return (123, 104, 238) 292| 27| case .mediumspringgreen: return (0, 250, 154) 293| 27| case .mediumturquoise: return (72, 209, 204) 294| 27| case .mediumvioletred: return (199, 21, 133) 295| 27| case .midnightblue: return (25, 25, 112) 296| 27| case .mintcream: return (245, 255, 250) 297| 27| case .mistyrose: return (255, 228, 225) 298| 27| case .moccasin: return (255, 228, 181) 299| 27| case .navajowhite: return (255, 222, 173) 300| 27| case .navy: return (0, 0, 128) 301| 27| case .oldlace: return (253, 245, 230) 302| 27| case .olive: return (128, 128, 0) 303| 27| case .olivedrab: return (107, 142, 35) 304| 27| case .orange: return (255, 165, 0) 305| 27| case .orangered: return (255, 69, 0) 306| 27| case .orchid: return (218, 112, 214) 307| 27| case .palegoldenrod: return (238, 232, 170) 308| 27| case .palegreen: return (152, 251, 152) 309| 27| case .paleturquoise: return (175, 238, 238) 310| 27| case .palevioletred: return (219, 112, 147) 311| 27| case .papayawhip: return (255, 239, 213) 312| 27| case .peachpuff: return (255, 218, 185) 313| 27| case .peru: return (205, 133, 63) 314| 27| case .pink: return (255, 192, 203) 315| 27| case .plum: return (221, 160, 221) 316| 27| case .powderblue: return (176, 224, 230) 317| 27| case .purple: return (128, 0, 128) 318| 27| case .red: return (255, 0, 0) 319| 27| case .rosybrown: return (188, 143, 143) 320| 27| case .royalblue: return (65, 105, 225) 321| 27| case .saddlebrown: return (139, 69, 19) 322| 27| case .salmon: return (250, 128, 114) 323| 27| case .sandybrown: return (244, 164, 96) 324| 27| case .seagreen: return (46, 139, 87) 325| 27| case .seashell: return (255, 245, 238) 326| 27| case .sienna: return (160, 82, 45) 327| 27| case .silver: return (192, 192, 192) 328| 27| case .skyblue: return (135, 206, 235) 329| 27| case .slateblue: return (106, 90, 205) 330| 27| case .slategray: return (112, 128, 144) 331| 27| case .slategrey: return (112, 128, 144) 332| 27| case .snow: return (255, 250, 250) 333| 27| case .springgreen: return (0, 255, 127) 334| 27| case .steelblue: return (70, 130, 180) 335| 27| case .tan: return (210, 180, 140) 336| 27| case .teal: return (0, 128, 128) 337| 27| case .thistle: return (216, 191, 216) 338| 27| case .tomato: return (255, 99, 71) 339| 27| case .turquoise: return (64, 224, 208) 340| 27| case .violet: return (238, 130, 238) 341| 27| case .wheat: return (245, 222, 179) 342| 27| case .white: return (255, 255, 255) 343| 27| case .whitesmoke: return (245, 245, 245) 344| 27| case .yellow: return (255, 255, 0) 345| 27| case .yellowgreen: return (154, 205, 50) 346| 27| } 347| 27| } 348| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/DOM.Element.swift: 1| |// 2| |// DOM.Element.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| |protocol ContainerElement { 35| | var childElements: [DOM.GraphicsElement] { get set } 36| |} 37| | 38| |protocol PresentationAttributes { 39| | var opacity: DOM.Float? { get set } 40| | var display: DOM.DisplayMode? { get set } 41| | 42| | var stroke: DOM.Color? { get set } 43| | var strokeWidth: DOM.Float? { get set } 44| | var strokeOpacity: DOM.Float? { get set } 45| | var strokeLineCap: DOM.LineCap? { get set } 46| | var strokeLineJoin: DOM.LineJoin? { get set } 47| | var strokeDashArray: [DOM.Float]? { get set } 48| | 49| | var fill: DOM.Fill? { get set } 50| | var fillOpacity: DOM.Float? { get set } 51| | var fillRule: DOM.FillRule? { get set } 52| | 53| | var transform: [DOM.Transform]? { get set } 54| | var clipPath: DOM.URL? { get set } 55| | var mask: DOM.URL? { get set } 56| |} 57| | 58| |extension DOM { 59| | 60| | class Element {} 61| | 62| | class GraphicsElement: Element, PresentationAttributes { 63| | var id: String? 64| | 65| | var opacity: DOM.Float? 66| | var display: DOM.DisplayMode? 67| | 68| | var stroke: DOM.Color? 69| | var strokeWidth: DOM.Float? 70| | var strokeOpacity: DOM.Float? 71| | var strokeLineCap: DOM.LineCap? 72| | var strokeLineJoin: DOM.LineJoin? 73| | var strokeDashArray: [DOM.Float]? 74| | 75| | var fill: DOM.Fill? 76| | var fillOpacity: DOM.Float? 77| | var fillRule: DOM.FillRule? 78| | 79| | var transform: [DOM.Transform]? 80| | var clipPath: URL? 81| | var mask: URL? 82| | } 83| | 84| | final class Line: GraphicsElement { 85| | var x1: Coordinate 86| | var y1: Coordinate 87| | var x2: Coordinate 88| | var y2: Coordinate 89| | 90| 22| init(x1: Coordinate, y1: Coordinate, x2: Coordinate, y2: Coordinate) { 91| 22| self.x1 = x1 92| 22| self.y1 = y1 93| 22| self.x2 = x2 94| 22| self.y2 = y2 95| 22| super.init() 96| 22| } 97| | } 98| | 99| | final class Circle: GraphicsElement { 100| | var cx: Coordinate 101| | var cy: Coordinate 102| | var r: Coordinate 103| | 104| 24| init(cx: Coordinate, cy: Coordinate, r: Coordinate) { 105| 24| self.cx = cx 106| 24| self.cy = cy 107| 24| self.r = r 108| 24| super.init() 109| 24| } 110| | } 111| | 112| | final class Ellipse: GraphicsElement { 113| | var cx: Coordinate 114| | var cy: Coordinate 115| | var rx: Coordinate 116| | var ry: Coordinate 117| | 118| 9| init(cx: Coordinate, cy: Coordinate, rx: Coordinate, ry: Coordinate) { 119| 9| self.cx = cx 120| 9| self.cy = cy 121| 9| self.rx = rx 122| 9| self.ry = ry 123| 9| super.init() 124| 9| } 125| | } 126| | 127| | final class Rect: GraphicsElement { 128| | var x: Coordinate? 129| | var y: Coordinate? 130| | var width: Coordinate 131| | var height: Coordinate 132| | 133| | var rx: Coordinate? 134| | var ry: Coordinate? 135| | 136| 23| init(x: Coordinate? = nil, y: Coordinate? = nil, width: Coordinate, height: Coordinate) { 137| 23| self.x = x 138| 23| self.y = y 139| 23| self.width = width 140| 23| self.height = height 141| 23| super.init() 142| 23| } 143| | } 144| | 145| | final class Polyline: GraphicsElement { 146| | var points: [Point] 147| | 148| 6| init(points: [Point]) { 149| 6| self.points = points 150| 6| super.init() 151| 6| } 152| | } 153| | 154| | final class Polygon: GraphicsElement { 155| | var points: [Point] 156| | 157| 14| init(points: [Point]) { 158| 14| self.points = points 159| 14| super.init() 160| 14| } 161| | } 162| | 163| | final class Group: GraphicsElement, ContainerElement { 164| 4| var childElements = [GraphicsElement]() 165| | } 166| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/DOM.Gradient.swift: 1| |// 2| |// DOM.Gradient.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 DOM { 33| | 34| | final class LinearGradient: Element { 35| | 36| | var id: String 37| | var x1: Coordinate? 38| | var y1: Coordinate? 39| | var x2: Coordinate? 40| | var y2: Coordinate? 41| | 42| | var stops: [Stop] 43| | 44| 12| init(id: String) { 45| 12| self.id = id 46| 12| self.stops = [] 47| 12| } 48| | 49| | struct Stop: Equatable { 50| | var offset: Float 51| | var color: Color 52| | var opacity: Float 53| | 54| 22| init(offset: Float, color: Color, opacity: Opacity = 1.0) { 55| 22| self.offset = offset 56| 22| self.color = color 57| 22| self.opacity = opacity 58| 22| } 59| | } 60| | } 61| |} 62| | 63| |extension DOM.LinearGradient: Equatable { 64| 1| static func ==(lhs: DOM.LinearGradient, rhs: DOM.LinearGradient) -> Bool { 65| 1| return 66| 1| lhs.id == rhs.id && 67| 1| lhs.x1 == rhs.x1 && 68| 1| lhs.y1 == rhs.y1 && 69| 1| lhs.x2 == rhs.x2 && 70| 1| lhs.y2 == rhs.y2 && 71| 1| lhs.stops == rhs.stops 72| 1| } 73| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| |extension DOM { 32| | final class Image: GraphicsElement { 33| | var href: URL 34| | var width: Coordinate 35| | var height: Coordinate 36| | 37| | var x: Coordinate? 38| | var y: Coordinate? 39| | 40| 4| init(href: URL, width: Coordinate, height: Coordinate) { 41| 4| self.href = href 42| 4| self.width = width 43| 4| self.height = height 44| 4| super.init() 45| 4| } 46| | } 47| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/DOM.Path.swift: 1| |// 2| |// DOM.Path.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 8/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| | 32| |import Foundation 33| | 34| |extension DOM { 35| | 36| | final class Path: GraphicsElement { 37| | 38| | // segments[0] should always be a .move 39| | var segments: [Segment] 40| | 41| 8| init(x: Coordinate, y: Coordinate) { 42| 8| let s = Segment.move(x: x, y: y, space: .absolute) 43| 8| segments = [s] 44| 8| super.init() 45| 8| } 46| | 47| | enum Segment { 48| | case move(x: Coordinate, y: Coordinate, space: CoordinateSpace) 49| | case line(x: Coordinate, y: Coordinate, space: CoordinateSpace) 50| | case horizontal(x: Coordinate, space: CoordinateSpace) 51| | case vertical(y: Coordinate, space: CoordinateSpace) 52| | case cubic(x1: Coordinate, y1: Coordinate, 53| | x2: Coordinate, y2: Coordinate, 54| | x: Coordinate, y: Coordinate, space: CoordinateSpace) 55| | case cubicSmooth(x2: Coordinate, y2: Coordinate, 56| | x: Coordinate, y: Coordinate, space: CoordinateSpace) 57| | case quadratic(x1: Coordinate, y1: Coordinate, 58| | x: Coordinate, y: Coordinate, space: CoordinateSpace) 59| | case quadraticSmooth(x: Coordinate, y: Coordinate, space: CoordinateSpace) 60| | case arc(rx: Coordinate, ry: Coordinate, rotate: Coordinate, 61| | large: Bool, sweep: Bool, 62| | x: Coordinate, y: Coordinate, space: CoordinateSpace) 63| | case close 64| | 65| | enum CoordinateSpace { 66| | case absolute 67| | case relative 68| | } 69| | } 70| | 71| | enum Command: UnicodeScalar { 72| | case move = "M" 73| | case moveRelative = "m" 74| | case line = "L" 75| | case lineRelative = "l" 76| | case horizontal = "H" 77| | case horizontalRelative = "h" 78| | case vertical = "V" 79| | case verticalRelative = "v" 80| | case cubic = "C" 81| | case cubicRelative = "c" 82| | case cubicSmooth = "S" 83| | case cubicSmoothRelative = "s" 84| | case quadratic = "Q" 85| | case quadraticRelative = "q" 86| | case quadraticSmooth = "T" 87| | case quadraticSmoothRelative = "t" 88| | case arc = "A" 89| | case arcRelative = "a" 90| | case close = "Z" 91| | case closeAlias = "z" 92| | 93| 68| var coordinateSpace: Segment.CoordinateSpace { 94| 68| switch self { 95| 68| case .move, .line, 96| 47| .horizontal, .vertical, 97| 47| .cubic, .cubicSmooth, 98| 47| .quadratic, .quadraticSmooth, 99| 47| .arc, .close, .closeAlias: 100| 47| return .absolute 101| 68| case .moveRelative, .lineRelative, 102| 21| .horizontalRelative, .verticalRelative, 103| 21| .cubicRelative, .cubicSmoothRelative, 104| 21| .quadraticRelative, .quadraticSmoothRelative, 105| 21| .arcRelative: 106| 21| return .relative 107| 68| } 108| 68| } 109| | } 110| | } 111| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| |extension DOM { 35| | 36| | struct Pattern: ContainerElement { 37| | 38| | var id: String 39| | var x: Coordinate? 40| | var y: Coordinate? 41| | var width: Coordinate 42| | var height: Coordinate 43| | 44| | var patternUnits: Units? 45| | var patternContentUnits: Units? 46| | 47| 13| var childElements: [DOM.GraphicsElement] = [] 48| | 49| 13| init(id: String, width: Coordinate, height: Coordinate) { 50| 13| self.id = id 51| 13| self.width = width 52| 13| self.height = height 53| 13| } 54| | } 55| |} 56| | 57| |extension DOM.Pattern { 58| | 59| | enum Units: String { 60| | case userSpaceOnUse 61| | case objectBoundingBox 62| | } 63| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/DOM.SVG.swift: 1| |// 2| |// DOM.SVG.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 11/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 DOM { 33| | final class SVG: GraphicsElement, ContainerElement { 34| | var width: Length 35| | var height: Length 36| | var viewBox: ViewBox? 37| | 38| 18| var childElements = [GraphicsElement]() 39| | 40| 18| var defs = Defs() 41| | 42| 18| init(width: Length, height: Length) { 43| 18| self.width = width 44| 18| self.height = height 45| 18| } 46| | 47| | struct ViewBox { 48| | var x: Coordinate 49| | var y: Coordinate 50| | var width: Coordinate 51| | var height: Coordinate 52| | } 53| | 54| | struct Defs { 55| 26| var clipPaths = [ClipPath]() 56| 26| var linearGradients = [LinearGradient]() 57| 26| var masks = [Mask]() 58| 26| var patterns = [Pattern]() 59| | 60| 26| var elements = [String: GraphicsElement]() 61| | } 62| | } 63| | 64| | struct ClipPath: ContainerElement { 65| | var id: String 66| 0| var childElements = [GraphicsElement]() 67| | } 68| | 69| | struct Mask: ContainerElement { 70| | var id: String 71| 0| var childElements = [GraphicsElement]() 72| | } 73| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| |extension DOM { 33| | final class Switch: GraphicsElement, ContainerElement { 34| 0| var childElements = [DOM.GraphicsElement]() 35| | } 36| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| |extension DOM { 33| | 34| | final class Text: GraphicsElement { 35| | var x: Coordinate? 36| | var y: Coordinate? 37| | var value: String 38| | 39| | var fontFamily: String? 40| | var fontSize: Float? 41| | 42| | // var textLength: Coordinate 43| | // var text: [TSpan] child nodes 44| | 45| 9| init(x: Coordinate? = nil, y: Coordinate? = nil, value: String) { 46| 9| self.x = x 47| 9| self.y = y 48| 9| self.value = value 49| 9| } 50| | } 51| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| |extension DOM { 33| | final class Use: GraphicsElement { 34| | var x: Coordinate? 35| | var y: Coordinate? 36| | 37| | //references element ids within defs 38| | var href: URL 39| | 40| 4| init(href: URL) { 41| 4| self.href = href 42| 4| } 43| | } 44| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/DOM.swift: 1| |// 2| |// DOM.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| |public enum DOM { /* namespace */ } 35| | 36| |public extension DOM { 37| | typealias Float = Swift.Float 38| | typealias Coordinate = Swift.Float 39| | typealias Length = Swift.Int 40| | typealias Opacity = Swift.Float 41| | typealias Bool = Swift.Bool 42| | typealias URL = Foundation.URL 43| |} 44| | 45| |extension DOM { 46| | struct Point: Equatable { 47| | var x: Coordinate 48| | var y: Coordinate 49| | 50| 87| init(_ x: Coordinate, _ y: Coordinate) { 51| 87| self.x = x 52| 87| self.y = y 53| 87| } 54| | } 55| | 56| | enum Fill: Equatable { 57| | case url(URL) 58| | case color(DOM.Color) 59| | 60| 48| func getColor() throws -> DOM.Color { 61| 48| switch self { 62| 48| case .url: 63| 0| throw Error.missing("Color") 64| 48| case .color(let c): 65| 48| return c 66| 48| } 67| 48| } 68| | } 69| | 70| | enum FillRule: String { 71| | case nonzero 72| | case evenodd 73| | } 74| | 75| | enum DisplayMode: String { 76| | case none 77| | case inline 78| | } 79| | 80| | enum LineCap: String { 81| | case butt 82| | case round 83| | case square 84| | } 85| | 86| | enum LineJoin: String { 87| | case miter 88| | case round 89| | case bevel 90| | } 91| | 92| | enum Transform: Equatable { 93| | case matrix(a: Float, b: Float, c: Float, d: Float, e: Float, f: Float) 94| | case translate(tx: Float, ty: Float) 95| | case scale(sx: Float, sy: Float) 96| | case rotate(angle: Float) 97| | case rotatePoint(angle: Float, cx: Float, cy: Float) 98| | case skewX(angle: Float) 99| | case skewY(angle: Float) 100| | } 101| | 102| | enum Error: Swift.Error { 103| | case missing(String) 104| | } 105| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Formatter.XML.swift: 1| |// 2| |// Formatter.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| |import Foundation 33| | 34| |extension XMLFormatter { 35| | 36| | struct CoordinateFormatter { 37| 4| var delimeter: Delimeter = .space 38| 4| var precision: Precision = .capped(max: 5) 39| | 40| | enum Precision { 41| | case capped(max: Int) 42| | case maximum 43| | } 44| | 45| | enum Delimeter: String { 46| | case space = " " 47| | case comma = "," 48| | } 49| | 50| 4| func format(_ coordinates: DOM.Coordinate...) -> String { 51| 12| return coordinates.map { format(Double($0)) }.joined(separator: delimeter.rawValue) 52| 4| } 53| | 54| 32| func format(_ c: Double) -> String { 55| 32| switch precision { 56| 32| case .capped(let max): 57| 23| return format(c, capped: max) 58| 32| default: 59| 9| return String(describing: c) 60| 32| } 61| 32| } 62| | 63| 23| func format(fraction n: Double, maxDigits: Int) -> String { 64| 23| assert(n.sign == .plus) 65| 23| 66| 23| let min = pow(Double(10), Double(-maxDigits)) - Double.ulpOfOne 67| 23| 68| 23| guard n >= min else { 69| 10| return "" 70| 13| } 71| 13| 72| 13| let s = String(format: "%.\(maxDigits)g", n) 73| 13| let idx = s.index(s.startIndex, offsetBy: 1) 74| 13| return String(s[idx.. String { 78| 23| let sign: String 79| 23| let n: (Double, Double) 80| 23| 81| 23| if c.sign == .minus { 82| 0| sign = "-" 83| 0| n = modf(abs(c)) 84| 23| } else { 85| 23| sign = "" 86| 23| n = modf(c) 87| 23| } 88| 23| 89| 23| let integer = Int(n.0) 90| 23| let fraction = format(fraction: n.1, maxDigits: capped) 91| 23| 92| 23| return "\(sign)\(integer)\(fraction)" 93| 23| } 94| | } 95| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Image+CoreGraphics.swift: 1| |// 2| |// Image.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 CoreGraphics 33| |import Foundation 34| | 35| |public extension CGContext { 36| | 37| 7| func draw(_ image: Image, in rect: CGRect? = nil) { 38| 7| let defaultRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) 39| 7| let renderer = CGRenderer(context: self) 40| 7| 41| 7| guard let rect = rect, rect != defaultRect else { 42| 5| renderer.perform(image.commands) 43| 5| return 44| 5| } 45| 2| 46| 2| let scale = CGSize(width: rect.width / image.size.width, 47| 2| height: rect.height / image.size.height) 48| 2| draw(image.commands, in: rect, scale: scale) 49| 2| } 50| | 51| 2| fileprivate func draw(_ commands: [RendererCommand], in rect: CGRect, scale: CGSize = CGSize(width: 1.0, height: 1.0)) { 52| 2| let renderer = CGRenderer(context: self) 53| 2| saveGState() 54| 2| translateBy(x: rect.origin.x, y: rect.origin.y) 55| 2| scaleBy(x: scale.width, y: scale.height) 56| 2| renderer.perform(commands) 57| 2| restoreGState() 58| 2| } 59| |} 60| | 61| |public extension Image { 62| | 63| 1| func pdfData(size: CGSize? = nil) -> Data? { 64| 1| let renderSize = size ?? self.size 65| 1| let data = NSMutableData() 66| 1| guard let consumer = CGDataConsumer(data: data as CFMutableData) else { return nil } 67| 1| 68| 1| var mediaBox = CGRect(x: 0.0, y: 0.0, width: renderSize.width, height: renderSize.height) 69| 1| 70| 1| guard let ctx = CGContext(consumer: consumer, mediaBox: &mediaBox, nil) else { return nil } 71| 1| 72| 1| ctx.beginPage(mediaBox: &mediaBox) 73| 1| let flip = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: mediaBox.size.height) 74| 1| ctx.concatenate(flip) 75| 1| ctx.draw(self, in: mediaBox) 76| 1| ctx.endPage() 77| 1| ctx.closePDF() 78| 1| 79| 1| return data as Data 80| 1| } 81| | 82| 0| static func pdfData(fileURL url: URL, size: CGSize? = nil) throws -> Data { 83| 0| let svg = try DOM.SVG.parse(fileURL: url) 84| 0| let size = size ?? CGSize(width: CGFloat(svg.width), height: CGFloat(svg.height)) 85| 0| let layer = LayerTree.Builder(svg: svg).makeLayer() 86| 0| var mediaBox = CGRect(origin: .zero, size: size) 87| 0| let generator = LayerTree.CommandGenerator(provider: CGProvider(supportsTransparencyLayers: false), 88| 0| size: LayerTree.Size(size)) 89| 0| let commands = generator.renderCommands(for: layer) 90| 0| let data = NSMutableData() 91| 0| guard let consumer = CGDataConsumer(data: data as CFMutableData) else { throw Error.unknown } 92| 0| 93| 0| guard let ctx = CGContext(consumer: consumer, mediaBox: &mediaBox, nil) else { throw Error.unknown } 94| 0| 95| 0| ctx.beginPage(mediaBox: &mediaBox) 96| 0| let flip = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: mediaBox.size.height) 97| 0| ctx.concatenate(flip) 98| 0| 99| 0| let scale = CGSize(width: mediaBox.width / CGFloat(svg.width), 100| 0| height: mediaBox.height / CGFloat(svg.height)) 101| 0| ctx.draw(commands, in: mediaBox, scale: scale) 102| 0| ctx.endPage() 103| 0| ctx.closePDF() 104| 0| 105| 0| return data as Data 106| 0| } 107| | 108| | private enum Error: Swift.Error { 109| | case unknown 110| | } 111| |} 112| | 113| |private extension LayerTree.Size { 114| | 115| 0| init(_ size: CGSize) { 116| 0| self.width = LayerTree.Float(size.width) 117| 0| self.height = LayerTree.Float(size.height) 118| 0| } 119| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Image.swift: 1| |// 2| |// Image.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 Foundation 33| | 34| |#if canImport(CoreGraphics) 35| | 36| |@objc(SVGImage) 37| |public final class Image: NSObject { 38| | public let size: CGSize 39| | 40| | //An Image is simply an array of CoreGraphics draw commands 41| | //see: Renderer.swift 42| | let commands: [RendererCommand] 43| | 44| 6| init(svg: DOM.SVG) { 45| 6| size = CGSize(width: svg.width, height: svg.height) 46| 6| 47| 6| //To create the draw commands; 48| 6| // - XML is parsed into DOM.SVG 49| 6| // - DOM.SVG is converted into a LayerTree 50| 6| // - LayerTree is converted into RenderCommands 51| 6| // - RenderCommands are performed by Renderer (drawn to CGContext) 52| 6| let layer = LayerTree.Builder(svg: svg).makeLayer() 53| 6| let generator = LayerTree.CommandGenerator(provider: CGProvider(), 54| 6| size: LayerTree.Size(svg.width, svg.height)) 55| 6| commands = generator.renderCommands(for: layer) 56| 6| } 57| |} 58| |#else 59| | 60| |public final class Image: NSObject { 61| | public let size: CGSize 62| | 63| | init(svg: DOM.SVG) { 64| | size = CGSize(width: svg.width, height: svg.height) 65| | } 66| |} 67| | 68| |public extension Image { 69| | 70| | func pngData(size: CGSize? = nil, scale: CGFloat = 1) -> Data? { 71| | return nil 72| | } 73| | 74| | func jpegData(size: CGSize? = nil, scale: CGFloat = 1, compressionQuality quality: CGFloat = 1) -> Data? { 75| | return nil 76| | } 77| | 78| | func pdfData(size: CGSize? = nil) -> Data? { 79| | return nil 80| | } 81| | 82| | static func pdfData(fileURL url: URL, size: CGSize? = nil) throws -> Data { 83| | throw DOM.Error.missing("not implemented") 84| | } 85| |} 86| |#endif 87| | 88| |extension DOM.SVG { 89| | 90| 4| static func parse(fileURL url: URL) throws -> DOM.SVG { 91| 4| let parser = XMLParser(options: [.skipInvalidElements]) 92| 4| let element = try XML.SAXParser.parse(contentsOf: url) 93| 4| return try parser.parseSVG(element) 94| 4| } 95| |} 96| | 97| |public extension Image { 98| | 99| 4| convenience init?(fileURL url: URL) { 100| 4| guard let svg = try? DOM.SVG.parse(fileURL: url) else { 101| 1| return nil 102| 3| } 103| 3| 104| 3| self.init(svg: svg) 105| 3| } 106| | 107| 6| convenience init?(named name: String, in bundle: Bundle = Bundle.main) { 108| 6| guard let url = bundle.url(forResource: name, withExtension: nil) else { 109| 2| return nil 110| 4| } 111| 4| 112| 4| self.init(fileURL: url) 113| 4| } 114| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/LayerTree.Builder.Layer.swift: 1| |// 2| |// LayerTree.Builder.Layer.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 Foundation 33| | 34| |extension LayerTree.Builder { 35| | 36| 20| func makeShapeContents(from shape: LayerTree.Shape, with state: State) -> LayerTree.Layer.Contents { 37| 20| let stroke = LayerTree.Builder.makeStrokeAttributes(with: state) 38| 20| let fill = makeFillAttributes(with: state) 39| 20| return .shape(shape, stroke, fill) 40| 20| } 41| | 42| 4| func makeUseLayerContents(from use: DOM.Use, with state: State) throws -> LayerTree.Layer.Contents { 43| 4| guard 44| 4| let id = use.href.fragment, 45| 4| let element = svg.defs.elements[id] else { 46| 1| throw LayerTree.Error.invalid("missing referenced element: \(use.href)") 47| 3| } 48| 3| 49| 3| let l = makeLayer(from: element, inheriting: state) 50| 3| let x = use.x ?? 0.0 51| 3| let y = use.y ?? 0.0 52| 3| 53| 3| if x != 0 || y != 0 { 54| 2| l.transform.insert(.translate(tx: x, ty: y), at: 0) 55| 3| } 56| 3| 57| 3| return .layer(l) 58| 3| 59| 3| } 60| | 61| 1| static func makeTextContents(from text: DOM.Text, with state: State) -> LayerTree.Layer.Contents { 62| 1| let point = Point(text.x ?? 0, text.y ?? 0) 63| 1| var att = makeTextAttributes(with: state) 64| 1| att.fontName = text.fontFamily ?? att.fontName 65| 1| att.size = text.fontSize ?? att.size 66| 1| return .text(text.value, point, att) 67| 1| } 68| | 69| 2| static func makeImageContents(from image: DOM.Image) throws -> LayerTree.Layer.Contents { 70| 2| guard 71| 2| let decoded = image.href.decodedData, 72| 2| let im = LayerTree.Image(mimeType: decoded.mimeType, data: decoded.data) else { 73| 1| throw LayerTree.Error.invalid("Cannot decode image") 74| 1| } 75| 1| return .image(im) 76| 2| } 77| | 78| | 79| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/LayerTree.Builder.Path.Arc.swift: 1| |// 2| |// LayerTree.Builder.Path.Arc.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 10/2/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| |import Foundation 33| | 34| |//converts DOM.Path.Arc -> LayerTree.Path.Cubic 35| | 36| |private extension Comparable { 37| 0| func clamped(to limits: ClosedRange) -> Self { 38| 0| return min(max(self, limits.lowerBound), limits.upperBound) 39| 0| } 40| |} 41| | 42| 0|private func almostEqual(_ a: T, _ b: T) -> Bool { 43| 0| return a >= b.nextDown && a <= b.nextUp 44| 0|} 45| | 46| 0|private func vectorAngle(ux: LayerTree.Float, uy: LayerTree.Float, vx: LayerTree.Float, vy: LayerTree.Float) -> LayerTree.Float { 47| 0| let sign: LayerTree.Float = (ux * vy - uy * vx) < 0.0 ? -1.0 : 1.0 48| 0| let dot = (ux * vx + uy * vy).clamped(to: -1.0...1.0) 49| 0| return sign * acos(dot) 50| 0|} 51| | 52| |private extension LayerTree.Float { 53| | 54| 0| static var tau: LayerTree.Float { 55| 0| return .pi * 2 56| 0| } 57| |} 58| | 59| |private func getArcCenter(from origin: LayerTree.Point, to destination: LayerTree.Point, 60| | fa: Bool, fs: Bool, 61| | rx: LayerTree.Float, ry: LayerTree.Float, 62| | sin_phi: LayerTree.Float, 63| 0| cos_phi: LayerTree.Float) -> (point: LayerTree.Point, theta: LayerTree.Float, deltaT: LayerTree.Float) { 64| 0| 65| 0| let x1p = cos_phi * (origin.x - destination.x) / 2 + sin_phi * (origin.y - destination.y) / 2 66| 0| let y1p = -sin_phi * (origin.x - destination.x) / 2 + cos_phi * (origin.y - destination.y) / 2 67| 0| 68| 0| let rx_sq = rx * rx 69| 0| let ry_sq = ry * ry 70| 0| let x1p_sq = x1p * x1p 71| 0| let y1p_sq = y1p * y1p 72| 0| 73| 0| var radicant = max((rx_sq * ry_sq) - (rx_sq * y1p_sq) - (ry_sq * x1p_sq), 0.0) 74| 0| let radicantSign: LayerTree.Float = fa == fs ? -1.0 : 1.0 75| 0| 76| 0| radicant /= (rx_sq * y1p_sq) + (ry_sq * x1p_sq) 77| 0| radicant = radicant.squareRoot() * radicantSign 78| 0| 79| 0| let cxp = radicant * rx / ry * y1p 80| 0| let cyp = radicant * -ry / rx * x1p 81| 0| 82| 0| let cx = cos_phi * cxp - sin_phi * cyp + (origin.x + destination.x) / 2 83| 0| let cy = sin_phi * cxp + cos_phi * cyp + (origin.y + destination.y) / 2 84| 0| 85| 0| let v1x = (x1p - cxp) / rx 86| 0| let v1y = (y1p - cyp) / ry 87| 0| let v2x = (-x1p - cxp) / rx 88| 0| let v2y = (-y1p - cyp) / ry 89| 0| 90| 0| let theta1 = vectorAngle(ux: 1, uy: 0, vx: v1x, vy: v1y); 91| 0| var delta_theta = vectorAngle(ux: v1x, uy: v1y, vx: v2x, vy: v2y); 92| 0| 93| 0| if (fs == false && delta_theta > 0) { 94| 0| delta_theta -= .tau; 95| 0| } 96| 0| if (fs == true && delta_theta < 0) { 97| 0| delta_theta += .tau; 98| 0| } 99| 0| 100| 0| return (point: LayerTree.Point(cx, cy), theta: theta1, deltaT: delta_theta) 101| 0|} 102| | 103| 0|private func approximateUnitArc(theta: LayerTree.Float, deltaT: LayerTree.Float) -> [LayerTree.Float] { 104| 0| let alpha = (4.0 / 3.0) * tan(deltaT / 4.0) 105| 0| 106| 0| let x1 = cos(theta) 107| 0| let y1 = sin(theta) 108| 0| let x2 = cos(theta + deltaT) 109| 0| let y2 = sin(theta + deltaT) 110| 0| 111| 0| return [x1, 112| 0| y1, 113| 0| x1 - y1 * alpha, 114| 0| y1 + x1 * alpha, 115| 0| x2 + y2 * alpha, 116| 0| y2 - x2 * alpha, 117| 0| x2, 118| 0| y2] 119| 0|} 120| | 121| | 122| 0|private func makePoint(x: LayerTree.Float, y: LayerTree.Float, rx: LayerTree.Float, ry: LayerTree.Float, cos_phi: LayerTree.Float, sin_phi: LayerTree.Float, center: LayerTree.Point) -> LayerTree.Point { 123| 0| 124| 0| let x1 = x * rx 125| 0| let y1 = y * ry 126| 0| 127| 0| let xp = cos_phi * x1 - sin_phi * y1 128| 0| let yp = sin_phi * x1 + cos_phi * y1 129| 0| 130| 0| return LayerTree.Point(xp + center.x, yp + center.y) 131| 0|} 132| | 133| |func makeCubic(from origin: LayerTree.Point, to destination: LayerTree.Point, 134| | large: Bool, sweep: Bool, 135| | rx: LayerTree.Float, ry: LayerTree.Float, 136| 0| rotation: LayerTree.Float) -> [(cp1: LayerTree.Point, cp2: LayerTree.Point, p: LayerTree.Point)] { 137| 0| 138| 0| let sin_phi = sin(rotation * .tau / 360.0) 139| 0| let cos_phi = cos(rotation * .tau / 360.0) 140| 0| 141| 0| let x1p = cos_phi * (origin.x - destination.x) / 2.0 + sin_phi * (origin.y - destination.y) / 2.0 142| 0| let y1p = -sin_phi * (origin.x - destination.x) / 2.0 + cos_phi * (origin.y - destination.y) / 2.0 143| 0| 144| 0| if almostEqual(x1p, 0.0) && almostEqual(y1p, 0.0) { 145| 0| return []; 146| 0| } 147| 0| 148| 0| if almostEqual(rx, 0.0) || almostEqual(ry, 0.0) { 149| 0| return []; 150| 0| } 151| 0| 152| 0| var rx1 = abs(rx) 153| 0| var ry1 = abs(ry) 154| 0| 155| 0| let lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry) 156| 0| if (lambda > 1.0) { 157| 0| let lambdaSquareRoot = lambda.squareRoot() 158| 0| rx1 *= lambdaSquareRoot 159| 0| ry1 *= lambdaSquareRoot 160| 0| } 161| 0| 162| 0| let cc = getArcCenter(from: origin, to: destination, fa: large, fs: sweep, rx: rx, ry: ry, sin_phi: sin_phi, cos_phi: cos_phi) 163| 0| 164| 0| var result = [[LayerTree.Float]]() 165| 0| 166| 0| let segments = max(Int(ceil(abs(cc.deltaT) / (LayerTree.Float.tau / 4.0))), 1) 167| 0| let deltaT = cc.deltaT / LayerTree.Float(segments) 168| 0| 169| 0| var theta1 = cc.theta 170| 0| 171| 0| for _ in 0.. LayerTree.Path 35| | 36| |extension LayerTree.Builder { 37| | 38| | typealias Path = LayerTree.Path 39| | typealias Point = LayerTree.Point 40| | 41| 1| static func createPath(from element: DOM.Path) throws -> LayerTree.Path { 42| 1| let path = Path() 43| 1| 44| 2| for s in element.segments { 45| 2| let segments = try makeSegments(from: s, 46| 2| last: path.location ?? Point.zero, 47| 2| previous: path.lastControl) 48| 2| path.segments.append(contentsOf: segments) 49| 2| } 50| 1| 51| 1| return path 52| 1| } 53| | 54| 4| static func makeSegment(from segment: DOM.Path.Segment, last point: Point, previous control: Point?) -> Path.Segment? { 55| 4| if let s = createMove(from: segment, last: point) { 56| 1| return s 57| 3| } else if let s = createLine(from: segment, last: point) { 58| 1| return s 59| 2| } else if let s = createHorizontal(from: segment, last: point) { 60| 0| return s 61| 2| } else if let s = createVertical(from: segment, last: point) { 62| 0| return s 63| 2| } else if let s = createCubic(from: segment, last: point) { 64| 0| return s 65| 2| } else if let s = createCubicSmooth(from: segment, last: point, previous: control ?? point) { 66| 1| return s 67| 1| } else if let s = createQuadratic(from: segment, last: point) { 68| 0| return s 69| 1| } else if let s = createQuadraticSmooth(from: segment, last: point, previous: control ?? point) { 70| 1| return s 71| 1| } else if let s = createClose(from: segment) { 72| 0| return s 73| 0| } 74| 0| 75| 0| return nil 76| 0| } 77| | 78| 2| static func makeSegments(from segment: DOM.Path.Segment, last point: Point, previous control: Point?) throws -> [Path.Segment] { 79| 2| if let s = createArc(from: segment, last: point) { 80| 0| return s 81| 2| } else if let s = makeSegment(from: segment, last: point, previous: control) { 82| 2| return [s] 83| 2| } 84| 0| 85| 0| throw LayerTree.Error.unsupported(segment) 86| 2| } 87| | 88| 7| static func createMove(from segment: DOM.Path.Segment, last point: Point) -> Path.Segment? { 89| 7| guard case .move(to: let m) = segment else { return nil } 90| 4| 91| 4| let p = Point(m.x, m.y) 92| 4| 93| 4| switch m.space { 94| 4| case .relative: return .move(to: p.absolute(from: point)) 95| 4| case .absolute: return .move(to: p) 96| 4| } 97| 4| } 98| | 99| 6| static func createLine(from segment: DOM.Path.Segment, last point: Point) -> Path.Segment? { 100| 6| guard case let .line(x, y, space) = segment else { return nil } 101| 4| 102| 4| let p = Point(x, y) 103| 4| 104| 4| switch space { 105| 4| case .relative: return .line(to: p.absolute(from: point)) 106| 4| case .absolute: return .line(to: p) 107| 4| } 108| 4| } 109| | 110| 6| static func createHorizontal(from segment: DOM.Path.Segment, last point: Point) -> Path.Segment? { 111| 6| guard case let .horizontal(x, space) = segment else { return nil } 112| 4| 113| 4| switch space { 114| 4| case .relative: return .line(to: Point(x + point.x , point.y)) 115| 4| case .absolute: return .line(to: Point(x, point.y)) 116| 4| } 117| 4| } 118| | 119| 6| static func createVertical(from segment: DOM.Path.Segment, last point: Point) -> Path.Segment? { 120| 6| guard case let .vertical(y, space) = segment else { return nil } 121| 4| 122| 4| switch space { 123| 4| case .relative: return .line(to: Point(point.x , y + point.y)) 124| 4| case .absolute: return .line(to: Point(point.x, y)) 125| 4| } 126| 4| } 127| | 128| 4| static func createCubic(from segment: DOM.Path.Segment, last point: Point) -> Path.Segment? { 129| 4| guard case let .cubic(x1, y1, x2, y2, x, y, space) = segment else { return nil } 130| 2| 131| 2| let p = Point(x, y) 132| 2| let cp1 = Point(x1, y1) 133| 2| let cp2 = Point(x2, y2) 134| 2| 135| 2| switch space { 136| 2| case .relative: return .cubic(to: p.absolute(from: point), 137| 1| control1: cp1.absolute(from: point), 138| 1| control2: cp2.absolute(from: point)) 139| 2| case .absolute: return .cubic(to: p, control1: cp1, control2: cp2) 140| 2| } 141| 2| } 142| | 143| 6| static func createCubicSmooth(from segment: DOM.Path.Segment, last point: Point, previous control: Point) -> Path.Segment? { 144| 6| guard case let .cubicSmooth(x2, y2, x, y, space) = segment else { return nil } 145| 5| 146| 5| let delta = Point(point.x - control.x, 147| 5| point.y - control.y) 148| 5| 149| 5| let p = Point(x, y) 150| 5| let cp1 = Point(point.x + delta.x, 151| 5| point.y + delta.y) 152| 5| let cp2 = Point(x2, y2) 153| 5| 154| 5| switch space { 155| 5| case .relative: return .cubic(to: p.absolute(from: point), 156| 3| control1: cp1, 157| 3| control2: cp2.absolute(from: point)) 158| 5| case .absolute: return .cubic(to: p, control1: cp1, control2: cp2) 159| 5| } 160| 5| } 161| | 162| 5| static func createQuadratic(from segment: DOM.Path.Segment, last point: Point) -> Path.Segment? { 163| 5| guard case let .quadratic(x1, y1, x, y, space) = segment else { return nil } 164| 4| 165| 4| var p = Point(x, y) 166| 4| var cp1 = Point(x1, y1) 167| 4| 168| 4| if space == .relative { 169| 1| p = p.absolute(from: point) 170| 1| cp1 = cp1.absolute(from: point) 171| 4| } 172| 4| 173| 4| return createCubic(from: point, to: p, quadratic: cp1) 174| 4| } 175| | 176| 4| static func createCubic(from origin: Point, to final: Point, quadratic controlPoint: Point) -> Path.Segment { 177| 4| //Approximate a quadratic curve using cubic curve. 178| 4| //Converting the quadratic control point into 2 cubic control points 179| 4| 180| 4| let ratio = Float(2.0/3.0) 181| 4| 182| 4| let cp1 = Point(origin.x + (controlPoint.x - origin.x) * ratio, 183| 4| origin.y + (controlPoint.y - origin.y) * ratio) 184| 4| 185| 4| let cpX = (final.x - origin.x)*Float(1.0/3.0) 186| 4| 187| 4| let cp2 = Point(cp1.x + cpX, 188| 4| cp1.y) 189| 4| 190| 4| return .cubic(to: final, control1: cp1, control2: cp2) 191| 4| } 192| | 193| 2| static func createQuadraticSmooth(from segment: DOM.Path.Segment, last point: Point, previous control: Point) -> Path.Segment? { 194| 2| guard case let .quadraticSmooth(x, y, space) = segment else { return nil } 195| 2| 196| 2| let delta = Point(point.x - control.x, 197| 2| point.y - control.y) 198| 2| 199| 2| let cp1 = Point(point.x + delta.x, 200| 2| point.y + delta.y) 201| 2| 202| 2| let final = space == .absolute ? Point(x, y) : Point(x, y).absolute(from: point) 203| 2| let cpX = (final.x - point.x)*Float(1.0/3.0) 204| 2| let cp2 = Point(cp1.x + cpX, 205| 2| cp1.y) 206| 2| 207| 2| return .cubic(to: final, control1: cp1, control2: cp2) 208| 2| } 209| | 210| 2| static func createArc(from segment: DOM.Path.Segment, last point: Point) -> [Path.Segment]? { 211| 2| guard case let .arc(rx, ry, rotate, large, sweep, x, y, space) = segment else { return nil } 212| 0| 213| 0| let p: Point 214| 0| 215| 0| switch space { 216| 0| case .relative: p = Point(x, y).absolute(from: point) 217| 0| case .absolute: p = Point(x, y) 218| 0| } 219| 0| 220| 0| let curves = makeCubic(from: point, to: p, 221| 0| large: large, sweep: sweep, 222| 0| rx: LayerTree.Float(rx), 223| 0| ry: LayerTree.Float(ry), 224| 0| rotation: LayerTree.Float(rotate)) 225| 0| 226| 0| return curves.map { .cubic(to: $0.p, control1: $0.cp1, control2: $0.cp2) } 227| 0| } 228| | 229| 1| static func createClose(from segment: DOM.Path.Segment) -> Path.Segment? { 230| 1| guard case .close = segment else { return nil } 231| 1| return .close 232| 1| } 233| | 234| |} 235| | 236| |extension LayerTree.Point { 237| 14| func absolute(from base: LayerTree.Point) -> LayerTree.Point { 238| 14| return LayerTree.Point(base.x + x, base.y + y) 239| 14| } 240| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/LayerTree.Builder.Shape.swift: 1| |// 2| |// LayerTree.Builder.Shape.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 Foundation 33| | 34| |extension LayerTree.Builder { 35| | 36| 36| static func makeShape(from element: DOM.GraphicsElement) -> LayerTree.Shape? { 37| 36| if let line = element as? DOM.Line { 38| 13| let from = Point(line.x1, line.y1) 39| 13| let to = Point(line.x2, line.y2) 40| 13| return .line(between: [from, to]) 41| 23| } else if let circle = element as? DOM.Circle { 42| 8| return .ellipse(within: makeRect(from: circle)) 43| 15| } else if let ellipse = element as? DOM.Ellipse { 44| 1| return .ellipse(within: makeRect(from: ellipse)) 45| 14| } else if let rect = element as? DOM.Rect { 46| 4| let radii = LayerTree.Size(rect.rx ?? 0, rect.ry ?? 0) 47| 4| return .rect(within: makeRect(from: rect), radii: radii) 48| 10| } else if let polyline = element as? DOM.Polyline { 49| 3| return .line(between: polyline.points.map{ Point($0.x, $0.y) }) 50| 9| } else if let polygon = element as? DOM.Polygon { 51| 3| return .polygon(between: polygon.points.map{ Point($0.x, $0.y) }) 52| 8| } else if let domPath = element as? DOM.Path, 53| 8| let path = try? createPath(from: domPath) { 54| 1| return .path(path) 55| 7| } 56| 7| 57| 7| return nil; 58| 8| } 59| | 60| 6| static func makeRect(from rect: DOM.Rect) -> LayerTree.Rect { 61| 6| return LayerTree.Rect(x: rect.x ?? 0, 62| 6| y: rect.y ?? 0, 63| 6| width: rect.width, 64| 6| height: rect.height) 65| 6| } 66| | 67| 1| static func makeRect(from ellipse: DOM.Ellipse) -> LayerTree.Rect { 68| 1| return LayerTree.Rect(x: ellipse.cx - ellipse.rx, 69| 1| y: ellipse.cy - ellipse.ry, 70| 1| width: ellipse.rx * 2, 71| 1| height: ellipse.ry * 2) 72| 1| } 73| | 74| 8| static func makeRect(from circle: DOM.Circle) -> LayerTree.Rect { 75| 8| return LayerTree.Rect(x: circle.cx - circle.r, 76| 8| y: circle.cy - circle.r, 77| 8| width: circle.r * 2, 78| 8| height: circle.r * 2) 79| 8| } 80| | 81| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/LayerTree.Builder.swift: 1| |// 2| |// LayerTree.Builder.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 4/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| |// Convert a DOM.SVG into a layer tree 33| | 34| |extension LayerTree { 35| | 36| | struct Builder { 37| | 38| | let svg: DOM.SVG 39| | 40| 13| init(svg: DOM.SVG) { 41| 13| self.svg = svg 42| 13| } 43| | 44| 6| func makeLayer() -> Layer { 45| 6| let l = makeLayer(from: svg, inheriting: State()) 46| 6| l.transform = Builder.makeTransform(for: svg.viewBox, 47| 6| width: svg.width, 48| 6| height: svg.height) 49| 6| return l 50| 6| } 51| | 52| 9| static func makeTransform(for viewBox: DOM.SVG.ViewBox?, width: DOM.Length, height: DOM.Length) -> [LayerTree.Transform] { 53| 9| guard let viewBox = viewBox else { 54| 7| return [] 55| 7| } 56| 2| 57| 2| let sx = LayerTree.Float(width) / viewBox.width 58| 2| let sy = LayerTree.Float(height) / viewBox.height 59| 2| let scale = LayerTree.Transform.scale(sx: sx, sy: sy) 60| 2| let translate = LayerTree.Transform.translate(tx: -viewBox.x, ty: -viewBox.y) 61| 2| 62| 2| var transform = [LayerTree.Transform]() 63| 2| 64| 2| if scale != .scale(sx: 1, sy: 1) { 65| 1| transform.append(scale) 66| 2| } 67| 2| 68| 2| if translate != .translate(tx: 0, ty: 0) { 69| 1| transform.append(translate) 70| 2| } 71| 2| 72| 2| return transform 73| 2| } 74| | 75| 27| func makeLayer(from element: DOM.GraphicsElement, inheriting previousState: State) -> Layer { 76| 27| let state = Builder.createState(for: element, inheriting: previousState) 77| 27| let l = Layer() 78| 27| 79| 27| guard state.display == .inline else { return l } 80| 27| 81| 27| l.transform = Builder.createTransforms(from: element.transform ?? []) 82| 27| l.clip = createClipShapes(for: element) 83| 27| l.mask = createMaskLayer(for: element) 84| 27| l.opacity = state.opacity 85| 27| l.contents = makeAllContents(from: element, with: state) 86| 27| 87| 27| // // clips the mask to the content 88| 27| // l.mask?.clip = l.contents.compactMap { (contents: Layer.Contents) -> LayerTree.Shape? in 89| 27| // switch(contents) { 90| 27| // case .shape(let s, _, _): return s 91| 27| // default: return nil 92| 27| // } 93| 27| // } 94| 27| 95| 27| return l 96| 27| } 97| | 98| 27| func makeAllContents(from element: DOM.GraphicsElement, with state: State) -> [Layer.Contents] { 99| 27| var all = [Layer.Contents]() 100| 27| if let contents = makeContents(from: element, with: state) { 101| 20| all.append(contents) 102| 27| } 103| 27| else if let container = element as? ContainerElement { 104| 14| container.childElements.forEach{ 105| 14| let contents = Layer.Contents.layer(makeLayer(from: $0, inheriting: state)) 106| 14| all.append(contents) 107| 14| } 108| 27| } 109| 27| return all 110| 27| } 111| | 112| 27| func makeContents(from element: DOM.GraphicsElement, with state: State) -> Layer.Contents? { 113| 27| if let shape = Builder.makeShape(from: element) { 114| 20| return makeShapeContents(from: shape, with: state) 115| 20| } else if let text = element as? DOM.Text { 116| 0| return Builder.makeTextContents(from: text, with: state) 117| 7| } else if let image = element as? DOM.Image { 118| 0| return try? Builder.makeImageContents(from: image) 119| 7| } else if let use = element as? DOM.Use { 120| 0| return try? makeUseLayerContents(from: use, with: state) 121| 7| } else if let sw = element as? DOM.Switch, 122| 7| let e = sw.childElements.first { 123| 0| //TODO: select first element that creates non empty Layer 124| 0| return .layer(makeLayer(from: e, inheriting: state)) 125| 7| } 126| 7| 127| 7| return nil 128| 7| } 129| | 130| 28| func createClipShapes(for element: DOM.GraphicsElement) -> [Shape] { 131| 28| guard let clipId = element.clipPath?.fragment, 132| 28| let clip = svg.defs.clipPaths.first(where: { $0.id == clipId }) else { return [] } 133| 1| 134| 1| return clip.childElements.compactMap{ Builder.makeShape(from: $0) } 135| 28| } 136| | 137| 28| func createMaskLayer(for element: DOM.GraphicsElement) -> Layer? { 138| 28| guard let maskId = element.mask?.fragment, 139| 28| let mask = svg.defs.masks.first(where: { $0.id == maskId }) else { return nil } 140| 1| 141| 1| let l = Layer() 142| 1| 143| 2| mask.childElements.forEach { 144| 2| let contents = Layer.Contents.layer(makeLayer(from: $0, inheriting: State())) 145| 2| l.appendContents(contents) 146| 2| } 147| 1| 148| 1| return l 149| 28| } 150| | } 151| |} 152| | 153| | 154| |extension LayerTree.Builder { 155| | 156| 23| static func makeStrokeAttributes(with state: State) -> LayerTree.StrokeAttributes { 157| 23| let stroke: LayerTree.Color 158| 23| 159| 23| if state.strokeWidth > 0.0 { 160| 22| stroke = LayerTree.Color.create(from: state.stroke).withAlpha(state.strokeOpacity).maybeNone() 161| 23| } else { 162| 1| stroke = .none 163| 23| } 164| 23| 165| 23| return LayerTree.StrokeAttributes(color: stroke, 166| 23| width: state.strokeWidth, 167| 23| cap: state.strokeLineCap, 168| 23| join: state.strokeLineJoin, 169| 23| miterLimit: state.strokeLineMiterLimit) 170| 23| } 171| | 172| 21| func makeFillAttributes(with state: State) -> LayerTree.FillAttributes { 173| 21| let fill = LayerTree.Color.create(from: state.fill.makeColor()).withAlpha(state.fillOpacity).maybeNone() 174| 21| 175| 21| if case .url(let patternId) = state.fill, 176| 21| let element = svg.defs.patterns.first(where: { $0.id == patternId.fragment }) { 177| 0| let pattern = makePattern(for: element) 178| 0| return LayerTree.FillAttributes(pattern: pattern, rule: state.fillRule, opacity: state.fillOpacity) 179| 21| } else if case .url(let gradientId) = state.fill, 180| 21| let element = svg.defs.linearGradients.first(where: { $0.id == gradientId.fragment }) { 181| 0| let gradient = makeGradient(for: element)! 182| 0| return LayerTree.FillAttributes(gradient: gradient, rule: state.fillRule, opacity: state.fillOpacity) 183| 21| } else { 184| 21| return LayerTree.FillAttributes(color: fill, rule: state.fillRule) 185| 21| } 186| 0| } 187| | 188| 1| static func makeTextAttributes(with state: State) -> LayerTree.TextAttributes { 189| 1| return .normal 190| 1| } 191| | 192| 1| func makePattern(for element: DOM.Pattern) -> LayerTree.Pattern { 193| 1| let frame = LayerTree.Rect(x: 0, y: 0, width: element.width, height: element.height) 194| 1| let pattern = LayerTree.Pattern(frame: frame) 195| 1| pattern.contents = element.childElements.compactMap { .layer(makeLayer(from: $0, inheriting: .init())) } 196| 1| return pattern 197| 1| } 198| | 199| 0| func makeGradient(for element: DOM.LinearGradient) -> LayerTree.Gradient? { 200| 0| guard 201| 0| let x1 = element.x1, 202| 0| let y1 = element.y1, 203| 0| let x2 = element.x2, 204| 0| let y2 = element.y2 else { 205| 0| return nil 206| 0| } 207| 0| 208| 0| let gradient = LayerTree.Gradient(start: Point(x1, y1), end: Point(x2, y2)) 209| 0| gradient.stops = element.stops.map { 210| 0| return LayerTree.Gradient.Stop(offset: $0.offset, 211| 0| color: LayerTree.Color($0.color), 212| 0| opacity: $0.opacity) 213| 0| } 214| 0| return gradient 215| 0| } 216| | 217| | //current state of the render tree, updated as builder traverses child nodes 218| | struct State { 219| | var opacity: DOM.Float 220| | var display: DOM.DisplayMode 221| | 222| | var stroke: DOM.Color 223| | var strokeWidth: DOM.Float 224| | var strokeOpacity: DOM.Float 225| | var strokeLineCap: DOM.LineCap 226| | var strokeLineJoin: DOM.LineJoin 227| | var strokeLineMiterLimit: DOM.Float 228| | var strokeDashArray: [DOM.Float] 229| | 230| | var fill: DOM.Fill 231| | var fillOpacity: DOM.Float 232| | var fillRule: DOM.FillRule 233| | 234| 45| init() { 235| 45| //default root SVG element state 236| 45| opacity = 1.0 237| 45| display = .inline 238| 45| 239| 45| stroke = .none 240| 45| strokeWidth = 1.0 241| 45| strokeOpacity = 1.0 242| 45| strokeLineCap = .butt 243| 45| strokeLineJoin = .miter 244| 45| strokeLineMiterLimit = 4.0 245| 45| strokeDashArray = [] 246| 45| 247| 45| fill = .color(.keyword(.black)) 248| 45| fillOpacity = 1.0 249| 45| fillRule = .evenodd 250| 45| } 251| | } 252| | 253| 27| static func createState(for attributes: PresentationAttributes, inheriting existing: State) -> State { 254| 27| var state = State() 255| 27| 256| 27| state.opacity = attributes.opacity ?? 1.0 257| 27| state.display = attributes.display ?? existing.display 258| 27| 259| 27| state.stroke = attributes.stroke ?? existing.stroke 260| 27| state.strokeWidth = attributes.strokeWidth ?? existing.strokeWidth 261| 27| state.strokeOpacity = attributes.strokeOpacity ?? existing.strokeOpacity 262| 27| state.strokeLineCap = attributes.strokeLineCap ?? existing.strokeLineCap 263| 27| state.strokeLineJoin = attributes.strokeLineJoin ?? existing.strokeLineJoin 264| 27| state.strokeDashArray = attributes.strokeDashArray ?? existing.strokeDashArray 265| 27| 266| 27| state.fill = attributes.fill ?? existing.fill 267| 27| state.fillOpacity = attributes.fillOpacity ?? existing.fillOpacity 268| 27| state.fillRule = attributes.fillRule ?? existing.fillRule 269| 27| 270| 27| return state 271| 27| } 272| |} 273| | 274| |extension LayerTree.Builder { 275| 9| static func createTransform(for dom: DOM.Transform) -> [LayerTree.Transform] { 276| 9| switch dom { 277| 9| case let .matrix(a, b, c, d, e, f): 278| 1| let matrix = LayerTree.Transform.Matrix(a: Float(a), 279| 1| b: Float(b), 280| 1| c: Float(c), 281| 1| d: Float(d), 282| 1| tx: Float(e), 283| 1| ty: Float(f)) 284| 1| return [.matrix(matrix)] 285| 9| 286| 9| case let .translate(tx, ty): 287| 2| return [.translate(tx: Float(tx), ty: Float(ty))] 288| 9| 289| 9| case let .scale(sx, sy): 290| 2| return [.scale(sx: Float(sx), sy: Float(sy))] 291| 9| 292| 9| case .rotate(let angle): 293| 1| let radians = Float(angle)*Float.pi/180.0 294| 1| return [.rotate(radians: radians)] 295| 9| 296| 9| case let .rotatePoint(angle, cx, cy): 297| 1| let radians = Float(angle)*Float.pi/180.0 298| 1| let t1 = LayerTree.Transform.translate(tx: cx, ty: cy) 299| 1| let t2 = LayerTree.Transform.rotate(radians: radians) 300| 1| let t3 = LayerTree.Transform.translate(tx: -cx, ty: -cy) 301| 1| return [t1, t2, t3] 302| 9| 303| 9| case let .skewX(angle): 304| 1| let radians = Float(angle)*Float.pi/180.0 305| 1| return [.skewX(angle: radians)] 306| 9| case let .skewY(angle): 307| 1| let radians = Float(angle)*Float.pi/180.0 308| 1| return [.skewY(angle: radians)] 309| 9| } 310| 9| } 311| | 312| 28| static func createTransforms(from transforms: [DOM.Transform]) -> [LayerTree.Transform] { 313| 28| return transforms.flatMap{ createTransform(for: $0) } 314| 28| } 315| |} 316| | 317| | 318| | 319| |private extension DOM.Fill { 320| | 321| 21| func makeColor() -> DOM.Color { 322| 21| switch self { 323| 21| case .color(let c): 324| 21| return c 325| 21| case .url: 326| 0| return .keyword(.black) 327| 21| } 328| 21| } 329| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/LayerTree.Color.swift: 1| |// 2| |// LayerTree.Color.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| | 33| |extension LayerTree { 34| | enum Color: Hashable { 35| | 36| | case none 37| | case rgba(r: Float, g: Float, b: Float, a: Float) 38| | case gray(white: Float, a: Float) 39| | 40| 2| static var white: Color { return Color.rgba(r: 1, g: 1, b: 1, a: 1) } 41| 14| static var black: Color { return Color.rgba(r: 0, g: 0, b: 0, a: 1) } 42| | } 43| |} 44| | 45| |extension LayerTree.Color { 46| | 47| 6| init(_ color: DOM.Color) { 48| 6| self = LayerTree.Color.create(from: color) 49| 6| } 50| | 51| 49| static func create(from color: DOM.Color) -> LayerTree.Color { 52| 49| switch(color){ 53| 49| case .none: 54| 16| return .none 55| 49| case let .keyword(c): 56| 27| let rgbi = c.rgbi 57| 27| return LayerTree.Color(rgbi.0, rgbi.1, rgbi.2) 58| 49| case let .rgbi(r, g, b): 59| 5| return LayerTree.Color(r, g, b) 60| 49| case let .hex(r, g, b): 61| 0| return LayerTree.Color(r, g, b) 62| 49| case let .rgbf(r, g, b): 63| 1| return .rgba(r: Float(r), 64| 1| g: Float(g), 65| 1| b: Float(b), 66| 1| a: 1.0) 67| 49| } 68| 49| } 69| | 70| 38| init(_ r: UInt8, _ g: UInt8, _ b: UInt8) { 71| 38| self = .rgba(r: Float(r)/255.0, 72| 38| g: Float(g)/255.0, 73| 38| b: Float(b)/255.0, 74| 38| a: 1.0) 75| 38| } 76| | 77| 0| var isOpaque: Bool { 78| 0| switch self { 79| 0| case .none: 80| 0| return false 81| 0| case .rgba(r: _, g: _, b: _, a: let a): 82| 0| return a >= 1.0 83| 0| case .gray(white: _, a: let a): 84| 0| return a >= 1.0 85| 0| } 86| 0| } 87| | 88| 55| func withAlpha(_ alpha: Float) -> LayerTree.Color { 89| 55| switch self { 90| 55| case .none: 91| 19| return .none 92| 55| case .rgba(r: let r, g: let g, b: let b, a: _): 93| 36| return .rgba(r: r, 94| 36| g: g, 95| 36| b: b, 96| 36| a: alpha) 97| 55| case .gray(white: let w, a: _): 98| 0| return .gray(white: w, a: alpha) 99| 55| } 100| 55| } 101| | 102| 57| func maybeNone() -> LayerTree.Color { 103| 57| switch self { 104| 57| case .none: 105| 19| return .none 106| 57| case .rgba(r: _, g: _, b: _, a: let a): 107| 38| return a > 0 ? self : .none 108| 57| case .gray(white: _, a: let a): 109| 0| return a > 0 ? self : .none 110| 57| } 111| 57| } 112| | 113| 10| func withMultiplyingAlpha(_ alpha: Float) -> LayerTree.Color { 114| 10| switch self { 115| 10| case .none: 116| 4| return .none 117| 10| case .rgba(r: let r, g: let g, b: let b, a: let a): 118| 6| let newAlpha = a * alpha 119| 6| return .rgba(r: r, 120| 6| g: g, 121| 6| b: b, 122| 6| a: newAlpha) 123| 10| case .gray(white: let w, a: let a): 124| 0| let newAlpha = a * alpha 125| 0| return .gray(white: w, a: newAlpha) 126| 10| } 127| 10| } 128| |} 129| | 130| |protocol ColorConverter { 131| | func createColor(from color: LayerTree.Color) -> LayerTree.Color 132| |} 133| | 134| |struct DefaultColorConverter: ColorConverter { 135| 18| func createColor(from color: LayerTree.Color) -> LayerTree.Color { 136| 18| return color 137| 18| } 138| |} 139| | 140| |struct LuminanceColorConverter: ColorConverter { 141| 5| func createColor(from color: LayerTree.Color) -> LayerTree.Color { 142| 5| switch color { 143| 5| case .rgba(let r, let g, let b, let a): 144| 5| //sRGB Luminance to alpha 145| 5| let alpha = ((r*0.2126) + (g*0.7152) + (b*0.0722)) * a 146| 5| return .gray(white: 0.0, a: alpha) 147| 5| default: 148| 0| return color 149| 5| } 150| 5| } 151| |} 152| | 153| |struct GrayscaleMaskColorConverter: ColorConverter { 154| 0| func createColor(from color: LayerTree.Color) -> LayerTree.Color { 155| 0| switch color { 156| 0| case .rgba(let r, let g, let b, let a): 157| 0| //sRGB Luminance to alpha 158| 0| let white = 1.0 - ((r*0.2126) + (g*0.7152) + (b*0.0722)) 159| 0| // let white = (r*0.2126) + (g*0.7152) + (b*0.0722) 160| 0| return .gray(white: white, a: a) 161| 0| //return .rgba(r: white, g: white, b: white, a: a) 162| 0| default: 163| 0| return color 164| 0| } 165| 0| } 166| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/LayerTree.CommandGenerator.swift: 1| |// 2| |// LayerTree.CommandGenerator.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 5/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| |import Foundation 32| | 33| |// Convert a LayerTree into RenderCommands 34| | 35| |extension LayerTree { 36| | 37| | final class CommandGenerator{ 38| | 39| | let provider: P 40| | let size: LayerTree.Size 41| | let scale: LayerTree.Float 42| | 43| 8| init(provider: P, size: LayerTree.Size, scale: LayerTree.Float = 3.0) { 44| 8| self.provider = provider 45| 8| self.size = size 46| 8| self.scale = scale 47| 8| } 48| | 49| 18| func renderCommands(for layer: Layer, colorConverter: ColorConverter = DefaultColorConverter()) -> [RendererCommand] { 50| 18| if provider.supportsTransparencyLayers { 51| 18| return renderCommandsWithTransparency(for: layer, colorConverter: colorConverter) 52| 18| } else { 53| 0| return renderCommandsWithoutTransparency(for: layer, colorConverter: colorConverter) 54| 0| } 55| 0| } 56| | 57| 18| func renderCommandsWithTransparency(for layer: Layer, colorConverter: ColorConverter = DefaultColorConverter()) -> [RendererCommand] { 58| 18| guard layer.opacity > 0.0 else { return [] } 59| 18| 60| 18| let opacityCommands = renderCommands(forOpacity: layer.opacity) 61| 18| let transformCommands = renderCommands(forTransforms: layer.transform) 62| 18| let clipCommands = renderCommands(forClip: layer.clip) 63| 18| let maskCommands = renderCommands(forMask: layer.mask) 64| 18| 65| 18| var commands = [RendererCommand]() 66| 18| 67| 18| if !opacityCommands.isEmpty || 68| 18| !transformCommands.isEmpty || 69| 18| !clipCommands.isEmpty || 70| 18| !maskCommands.isEmpty { 71| 0| commands.append(.pushState) 72| 18| } 73| 18| 74| 18| commands.append(contentsOf: transformCommands) 75| 18| commands.append(contentsOf: opacityCommands) 76| 18| commands.append(contentsOf: clipCommands) 77| 18| 78| 18| if !maskCommands.isEmpty { 79| 0| commands.append(.pushTransparencyLayer) 80| 18| } 81| 18| 82| 18| //render all of the layer contents 83| 24| for contents in layer.contents { 84| 24| commands.append(contentsOf: renderCommands(for: contents, colorConverter: colorConverter)) 85| 24| } 86| 18| 87| 18| //render apply mask 88| 18| if !maskCommands.isEmpty { 89| 0| commands.append(contentsOf: maskCommands) 90| 0| commands.append(.popTransparencyLayer) 91| 18| } 92| 18| 93| 18| if !opacityCommands.isEmpty { 94| 0| commands.append(.popTransparencyLayer) 95| 18| } 96| 18| 97| 18| if !opacityCommands.isEmpty || 98| 18| !transformCommands.isEmpty || 99| 18| !clipCommands.isEmpty || 100| 18| !maskCommands.isEmpty { 101| 0| commands.append(.popState) 102| 18| } 103| 18| 104| 18| return commands 105| 18| } 106| | 107| 0| func renderCommandsWithoutTransparency(for layer: Layer, colorConverter: ColorConverter = DefaultColorConverter()) -> [RendererCommand] { 108| 0| guard layer.opacity > 0.0 else { return [] } 109| 0| 110| 0| let opacityCommands = renderCommands(forOpacity: layer.opacity) 111| 0| let transformCommands = renderCommands(forTransforms: layer.transform) 112| 0| let clipCommands = renderCommands(forClip: layer.clip) 113| 0| let mask = makeMask(forMask: layer.mask) 114| 0| 115| 0| var commands = [RendererCommand]() 116| 0| 117| 0| if !opacityCommands.isEmpty || 118| 0| !transformCommands.isEmpty || 119| 0| !clipCommands.isEmpty || 120| 0| mask != nil { 121| 0| commands.append(.pushState) 122| 0| } 123| 0| 124| 0| commands.append(contentsOf: transformCommands) 125| 0| commands.append(contentsOf: opacityCommands) 126| 0| commands.append(contentsOf: clipCommands) 127| 0| 128| 0| if let mask = mask { 129| 0| let bounds = provider.createRect(from: LayerTree.Rect(x: 0, y: 0, width: size.width * scale, height: size.height * scale)) 130| 0| commands.append(.setClipMask(mask, frame: bounds)) 131| 0| 132| 0| //render all of the layer contents 133| 0| for contents in layer.contents { 134| 0| commands.append(contentsOf: renderCommands(for: contents, colorConverter: colorConverter)) 135| 0| } 136| 0| 137| 0| } else { 138| 0| //render all of the layer contents 139| 0| for contents in layer.contents { 140| 0| commands.append(contentsOf: renderCommands(for: contents, colorConverter: colorConverter)) 141| 0| } 142| 0| } 143| 0| 144| 0| if !opacityCommands.isEmpty { 145| 0| commands.append(.popTransparencyLayer) 146| 0| } 147| 0| 148| 0| if !opacityCommands.isEmpty || 149| 0| !transformCommands.isEmpty || 150| 0| !clipCommands.isEmpty || 151| 0| mask != nil { 152| 0| commands.append(.popState) 153| 0| } 154| 0| 155| 0| return commands 156| 0| } 157| | 158| 24| func renderCommands(for contents: Layer.Contents, colorConverter: ColorConverter) -> [RendererCommand] { 159| 24| switch contents { 160| 24| case .shape(let shape, let stroke, let fill): 161| 12| return renderCommands(for: shape, stroke: stroke, fill: fill, colorConverter: colorConverter) 162| 24| case .image(let image): 163| 0| return renderCommands(for: image) 164| 24| case .text(let text, let point, let att): 165| 0| return renderCommands(for: text, at: point, attributes: att, colorConverter: colorConverter) 166| 24| case .layer(let layer): 167| 12| return renderCommands(for: layer, colorConverter: colorConverter) 168| 24| } 169| 24| } 170| | 171| | func renderCommands(for shape: Shape, 172| | stroke: StrokeAttributes, 173| | fill: FillAttributes, 174| 12| colorConverter: ColorConverter) -> [RendererCommand] { 175| 12| var commands = [RendererCommand]() 176| 12| let path = provider.createPath(from: shape) 177| 12| 178| 12| switch fill.fill { 179| 12| case .color(let color): 180| 12| if (color != .none) { 181| 12| let converted = colorConverter.createColor(from: color) 182| 12| let color = provider.createColor(from: converted) 183| 12| let rule = provider.createFillRule(from: fill.rule) 184| 12| commands.append(.setFill(color: color)) 185| 12| commands.append(.fill(path, rule: rule)) 186| 12| } 187| 12| case .pattern(let fillPattern): 188| 0| var patternCommands = [RendererCommand]() 189| 0| for contents in fillPattern.contents { 190| 0| patternCommands.append(contentsOf: renderCommands(for: contents, colorConverter: colorConverter)) 191| 0| } 192| 0| 193| 0| let pattern = provider.createPattern(from: fillPattern, contents: patternCommands) 194| 0| let rule = provider.createFillRule(from: fill.rule) 195| 0| commands.append(.setFillPattern(pattern)) 196| 0| commands.append(.fill(path, rule: rule)) 197| 12| case .gradient(let fillGradient): 198| 0| commands.append(.pushState) 199| 0| commands.append(.setClip(path: path)) 200| 0| 201| 0| let pathBounds = provider.getBounds(from: path) 202| 0| let pathStart = pathBounds.getPoint(offset: fillGradient.start) 203| 0| let pathEnd = pathBounds.getPoint(offset: fillGradient.end) 204| 0| 205| 0| let converted = apply(colorConverter: colorConverter, to: fillGradient) 206| 0| let gradient = provider.createGradient(from: converted) 207| 0| let start = provider.createPoint(from: pathStart) 208| 0| let end = provider.createPoint(from: pathEnd) 209| 0| 210| 0| let apha = provider.createFloat(from: fill.opacity) 211| 0| commands.append(.setAlpha(apha)) 212| 0| 213| 0| commands.append(.drawGradient(gradient, from: start, to: end)) 214| 0| commands.append(.popState) 215| 12| } 216| 12| 217| 12| if stroke.color != .none, 218| 12| stroke.width > 0.0 { 219| 6| let converted = colorConverter.createColor(from: stroke.color) 220| 6| let color = provider.createColor(from: converted) 221| 6| let width = provider.createFloat(from: stroke.width) 222| 6| let cap = provider.createLineCap(from: stroke.cap) 223| 6| let join = provider.createLineJoin(from: stroke.join) 224| 6| let limit = provider.createFloat(from: stroke.miterLimit) 225| 6| 226| 6| commands.append(.setLineCap(cap)) 227| 6| commands.append(.setLineJoin(join)) 228| 6| commands.append(.setLine(width: width)) 229| 6| commands.append(.setLineMiter(limit: limit)) 230| 6| commands.append(.setStroke(color: color)) 231| 6| commands.append(.stroke(path)) 232| 12| } 233| 12| 234| 12| return commands 235| 12| } 236| | 237| 0| func renderCommands(for image: Image) -> [RendererCommand] { 238| 0| guard let renderImage = provider.createImage(from: image) else { return [] } 239| 0| return [.draw(image: renderImage)] 240| 0| } 241| | 242| 0| func renderCommands(for text: String, at point: Point, attributes: TextAttributes, colorConverter: ColorConverter = DefaultColorConverter()) -> [RendererCommand] { 243| 0| guard let path = provider.createPath(from: text, at: point, with: attributes) else { return [] } 244| 0| 245| 0| let converted = colorConverter.createColor(from: attributes.color) 246| 0| let color = provider.createColor(from: converted) 247| 0| let rule = provider.createFillRule(from: .nonzero) 248| 0| 249| 0| return [.setFill(color: color), 250| 0| .fill(path, rule: rule)] 251| 0| } 252| | 253| 18| func renderCommands(forOpacity opacity: Float) -> [RendererCommand] { 254| 18| guard opacity < 1.0 else { return [] } 255| 0| 256| 0| return [.setAlpha(provider.createFloat(from: opacity)), 257| 0| .pushTransparencyLayer] 258| 18| } 259| | 260| 19| func renderCommands(forTransforms transforms: [Transform]) -> [RendererCommand] { 261| 19| return transforms.map{ renderCommand(forTransform: $0) } 262| 19| } 263| | 264| 4| func renderCommand(forTransform transform: Transform) -> RendererCommand { 265| 4| switch transform { 266| 4| case .matrix(let m): 267| 1| let t = provider.createTransform(from: m) 268| 1| return .concatenate(transform: t) 269| 4| case let .translate(tx, ty): 270| 1| let tx = provider.createFloat(from: tx) 271| 1| let ty = provider.createFloat(from: ty) 272| 1| return .translate(tx: tx, ty: ty) 273| 4| case let .scale(sx, sy): 274| 1| let sx = provider.createFloat(from: sx) 275| 1| let sy = provider.createFloat(from: sy) 276| 1| return .scale(sx: sx, sy: sy) 277| 4| case .rotate(let r): 278| 1| let radians = provider.createFloat(from: r) 279| 1| return .rotate(angle: radians) 280| 4| } 281| 4| } 282| | 283| 19| func renderCommands(forClip shapes: [Shape]) -> [RendererCommand] { 284| 19| guard !shapes.isEmpty else { return [] } 285| 1| 286| 2| let paths = shapes.map { provider.createPath(from: $0) } 287| 1| let clipPath = provider.createPath(from: paths) 288| 1| 289| 1| return [.setClip(path: clipPath)] 290| 19| } 291| | 292| 0| func makeMask(forMask layer: Layer?) -> P.Types.Mask? { 293| 0| guard let layer = layer else { return nil } 294| 0| 295| 0| var commands = layer.contents.flatMap { 296| 0| renderCommands(for: $0, colorConverter: GrayscaleMaskColorConverter()) 297| 0| } 298| 0| guard commands.isEmpty == false else { return nil } 299| 0| 300| 0| commands.append(.scale(sx: provider.createFloat(from: scale), sy: provider.createFloat(from: scale))) 301| 0| return provider.createMask(from: commands, size: LayerTree.Size(size.width*scale, size.height*scale)) 302| 0| } 303| | 304| 18| func renderCommands(forMask layer: Layer?) -> [RendererCommand] { 305| 18| guard let layer = layer else { return [] } 306| 0| 307| 0| let copy = provider.createBlendMode(from: .copy) 308| 0| let destinationIn = provider.createBlendMode(from: .destinationIn) 309| 0| 310| 0| var commands = [RendererCommand]() 311| 0| commands.append(.setBlend(mode: destinationIn)) 312| 0| commands.append(.pushTransparencyLayer) 313| 0| commands.append(.setBlend(mode: copy)) 314| 0| //commands.append(contentsOf: renderCommands(forClip: layer.clip)) 315| 0| let drawMask = layer.contents.flatMap{ 316| 0| renderCommands(for: $0, colorConverter: LuminanceColorConverter()) 317| 0| } 318| 0| commands.append(contentsOf: drawMask) 319| 0| commands.append(.popTransparencyLayer) 320| 0| return commands 321| 18| } 322| | } 323| | 324| |} 325| | 326| |private extension LayerTree.Rect { 327| | 328| 0| func getPoint(offset: LayerTree.Point) -> LayerTree.Point { 329| 0| return LayerTree.Point(origin.x + size.width * offset.x, 330| 0| origin.y + size.height * offset.y) 331| 0| } 332| |} 333| | 334| 0|private func apply(colorConverter: ColorConverter, to gradient: LayerTree.Gradient) -> LayerTree.Gradient { 335| 0| let converted = LayerTree.Gradient(start: gradient.start, end: gradient.end) 336| 0| converted.stops = gradient.stops.map { apply(colorConverter: colorConverter, to: $0) } 337| 0| return converted 338| 0|} 339| | 340| 0|private func apply(colorConverter: ColorConverter, to stop: LayerTree.Gradient.Stop) -> LayerTree.Gradient.Stop { 341| 0| var stop = stop 342| 0| stop.color = colorConverter.createColor(from: stop.color).withMultiplyingAlpha(stop.opacity) 343| 0| return stop 344| 0|} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/LayerTree.Gradient.swift: 1| |// 2| |// LayerTree.Gradient.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 28/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 Gradient: Equatable { 35| | var start: Point 36| | var end: Point 37| | var stops: [Stop] 38| | 39| 1| init(start: Point, end: Point) { 40| 1| self.start = start 41| 1| self.end = end 42| 1| self.stops = [] 43| 1| } 44| | 45| 0| static func == (lhs: LayerTree.Gradient, rhs: LayerTree.Gradient) -> Bool { 46| 0| return 47| 0| lhs.start == rhs.start && 48| 0| lhs.end == rhs.end && 49| 0| lhs.stops == rhs.stops 50| 0| } 51| | 52| | struct Stop: Equatable { 53| | var offset: Float 54| | var color: Color 55| | var opacity: Float 56| | 57| 0| init(offset: Float, color: Color, opacity: Float) { 58| 0| self.offset = offset 59| 0| self.color = color 60| 0| self.opacity = opacity 61| 0| } 62| | } 63| | } 64| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| | enum Image: Equatable { 36| | case jpeg(data: Data) 37| | case png(data: Data) 38| | 39| 12| init?(mimeType: String, data: Data) { 40| 12| guard data.count > 0 else { return nil } 41| 11| 42| 11| switch mimeType { 43| 11| case "image/png": 44| 6| self = .png(data: data) 45| 11| case "image/jpeg": 46| 2| self = .jpeg(data: data) 47| 11| case "image/jpg": 48| 2| self = .jpeg(data: data) 49| 11| default: 50| 1| return nil 51| 11| } 52| 11| } 53| | } 54| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/LayerTree.Layer.swift: 1| |// 2| |// LayerTree.Layer.swift 3| |// SwiftDraw 4| |// 5| |// Distributed under the permissive zlib license 6| |// Get the latest version from here: 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| |extension LayerTree { 30| | final class Layer: Equatable { 31| 33| var contents: [Contents] = [] 32| | var opacity: Float = 1.0 33| 33| var transform: [Transform] = [] 34| 33| var clip: [Shape] = [] 35| | var mask: Layer? 36| | 37| | enum Contents: Equatable { 38| | case shape(Shape, StrokeAttributes, FillAttributes) 39| | case image(Image) 40| | case text(String, Point, TextAttributes) 41| | case layer(Layer) 42| | } 43| | 44| 6| func appendContents(_ contents: Contents) { 45| 6| switch contents { 46| 6| case .layer(let l): 47| 4| guard l.contents.isEmpty == false else { return } 48| 4| 49| 4| //if layer is simple, we can ignore all other properties 50| 4| if let simple = l.simpleContents { 51| 3| self.contents.append(simple) 52| 4| } else { 53| 1| self.contents.append(.layer(l)) 54| 6| } 55| 6| default: 56| 2| self.contents.append(contents) 57| 6| } 58| 6| } 59| | 60| 4| var simpleContents: Contents? { 61| 4| guard self.contents.count == 1, 62| 4| let first = self.contents.first, 63| 4| opacity == 1.0, 64| 4| transform == [], 65| 4| clip == [], 66| 4| mask == nil else { return nil } 67| 3| 68| 3| return first 69| 4| } 70| | 71| 2| static func ==(lhs: Layer, rhs: Layer) -> Bool { 72| 2| return lhs.contents == rhs.contents && 73| 2| lhs.opacity == rhs.opacity && 74| 2| lhs.transform == rhs.transform && 75| 2| lhs.clip == rhs.clip && 76| 2| lhs.mask == rhs.mask 77| 2| } 78| | } 79| | 80| | struct StrokeAttributes: Equatable { 81| | var color: Color 82| | var width: Float 83| | var cap: LineCap 84| | var join: LineJoin 85| | var miterLimit: Float 86| | } 87| | 88| | struct FillAttributes: Equatable { 89| 21| var fill: Fill = .color(.none) 90| | var opacity: Float = 1.0 91| | var rule: FillRule 92| | 93| 21| init(color: Color, rule: FillRule) { 94| 21| self.fill = .color(color) 95| 21| self.rule = rule 96| 21| } 97| | 98| 0| init(pattern: Pattern, rule: FillRule, opacity: Float) { 99| 0| self.fill = .pattern(pattern) 100| 0| self.rule = rule 101| 0| self.opacity = opacity 102| 0| } 103| | 104| 0| init(gradient: Gradient, rule: FillRule, opacity: Float) { 105| 0| self.fill = .gradient(gradient) 106| 0| self.rule = rule 107| 0| self.opacity = opacity 108| 0| } 109| | 110| | enum Fill: Equatable { 111| | case color(Color) 112| | case pattern(Pattern) 113| | case gradient(Gradient) 114| | } 115| | } 116| | 117| | struct TextAttributes: Equatable { 118| | var color: Color 119| | var fontName: String 120| | var size: Float 121| | 122| 10| static var normal: TextAttributes { 123| 10| return TextAttributes(color: .black, fontName: "Helvetica", size: 12.0) 124| 10| } 125| | } 126| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/LayerTree.Path.swift: 1| |// 2| |// LayerTree.Path.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| |extension LayerTree { 33| | final class Path: Hashable { 34| | var segments: [Segment] 35| | 36| 7| init(_ segments: [Segment] = []) { 37| 7| self.segments = segments 38| 7| } 39| | 40| | enum Segment: Hashable { 41| | case move(to: Point) 42| | case line(to: Point) 43| | case cubic(to: Point, control1: Point, control2: Point) 44| | case close 45| | } 46| | 47| 2| func hash(into hasher: inout Hasher) { 48| 2| hasher.combine(self.segments) 49| 2| } 50| | 51| 3| static func ==(lhs: LayerTree.Path, rhs: LayerTree.Path) -> Bool { 52| 3| return lhs.segments == rhs.segments 53| 3| } 54| | } 55| |} 56| | 57| |extension LayerTree.Path { 58| 2| var lastControl: LayerTree.Point? { 59| 2| guard let lastSegment = segments.last else { return nil } 60| 1| switch lastSegment { 61| 1| case .cubic(_, _, let p): return p 62| 1| default: return nil 63| 1| } 64| 1| } 65| | 66| 7| var location: LayerTree.Point? { 67| 7| guard let location = segments.last?.location else { 68| 3| return lastStart 69| 4| } 70| 4| 71| 4| return location 72| 7| } 73| | 74| 3| var lastStart: LayerTree.Point? { 75| 3| let rev = segments.reversed().dropFirst() 76| 3| guard 77| 3| let closeIdx = rev.firstIndex(where: { $0.isClose }), 78| 3| closeIdx != rev.startIndex else { 79| 3| return segments.first?.location 80| 3| } 81| 0| 82| 0| let point = rev.index(before: closeIdx) 83| 0| return rev[point].location 84| 3| } 85| |} 86| | 87| |private extension LayerTree.Path.Segment { 88| | 89| 3| var isClose: Bool { 90| 3| guard case .close = self else { 91| 3| return false 92| 3| } 93| 0| return true 94| 3| } 95| | 96| 6| var location: LayerTree.Point? { 97| 6| switch self { 98| 6| case .move(to: let p): return p 99| 6| case .line(let p): return p 100| 6| case .cubic(let p, _, _): return p 101| 6| case .close: return nil 102| 6| } 103| 6| } 104| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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: Equatable { 35| | 36| | var frame: LayerTree.Rect 37| | var contents: [LayerTree.Layer.Contents] 38| | 39| 2| init(frame: LayerTree.Rect) { 40| 2| self.frame = frame 41| 2| self.contents = [] 42| 2| } 43| | 44| 0| static func == (lhs: LayerTree.Pattern, rhs: LayerTree.Pattern) -> Bool { 45| 0| return lhs.contents == rhs.contents 46| 0| } 47| | } 48| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/LayerTree.Transform.swift: 1| |// 2| |// LayerTree.Transform.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| |#if canImport(Darwin) 33| |import Darwin 34| |#else 35| |import Glibc 36| |#endif 37| | 38| |extension LayerTree { 39| | 40| | enum Transform: Equatable { 41| | case matrix(Matrix) 42| | case scale(sx: Float, sy: Float) 43| | case translate(tx: Float, ty: Float) 44| | case rotate(radians: Float) 45| | 46| 6| static var identity: Transform { 47| 6| let m = Matrix(a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0) 48| 6| return .matrix(m) 49| 6| } 50| | 51| 3| static func skewX(angle radians: Float) -> Transform { 52| 3| let m = Matrix(a: 1, b: 0, c: tan(radians), d: 1, tx: 0, ty: 0) 53| 3| return .matrix(m) 54| 3| } 55| | 56| 3| static func skewY(angle radians: Float) -> Transform { 57| 3| let m = Matrix(a: 1, b: tan(radians), c: 0, d: 1, tx: 0, ty: 0) 58| 3| return .matrix(m) 59| 3| } 60| | 61| | struct Matrix: Equatable { 62| | var a: Float 63| | var b: Float 64| | var c: Float 65| | var d: Float 66| | var tx: Float 67| | var ty: Float 68| | } 69| | } 70| |} 71| | 72| |extension LayerTree.Transform { 73| 9| func toMatrix() -> Matrix { 74| 9| switch self { 75| 9| case .matrix(let m): 76| 6| return m 77| 9| case .scale(let sx, let sy): 78| 1| return Matrix(a: sx, b: 0, c: 0, d: sy, tx: 0, ty: 0) 79| 9| case .translate(let tx, let ty): 80| 1| return Matrix(a: 1, b: 0, c: 0, d: 1, tx: tx, ty: ty) 81| 9| case .rotate(let radians): 82| 1| let sine = sin(radians) 83| 1| let cosine = cos(radians) 84| 1| return Matrix(a: cosine, b: sine, c: -sine, d: cosine, tx: 0, ty: 0) 85| 9| } 86| 9| } 87| |} 88| | 89| |extension LayerTree.Transform.Matrix { 90| 1| func concatenated(_ other: LayerTree.Transform.Matrix) -> LayerTree.Transform.Matrix { 91| 1| let (t, m) = (self, other) 92| 1| return LayerTree.Transform.Matrix(a: (t.a * m.a) + (t.b * m.c), 93| 1| b: (t.a * m.b) + (t.b * m.d), 94| 1| c: (t.c * m.a) + (t.d * m.c), 95| 1| d: (t.c * m.b) + (t.d * m.d), 96| 1| tx: (t.tx * m.a) + (t.ty * m.c) + m.tx, 97| 1| ty: (t.tx * m.b) + (t.ty * m.d) + m.ty) 98| 1| } 99| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/LayerTree.swift: 1| |// 2| |// LayerTree.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| |enum LayerTree { /* namespace */ } 33| | 34| |extension LayerTree { 35| | 36| | typealias Float = Swift.Float 37| | typealias LineCap = DOM.LineCap 38| | typealias LineJoin = DOM.LineJoin 39| | typealias FillRule = DOM.FillRule 40| | 41| | enum Error: Swift.Error { 42| | case unsupported(Any) 43| | case invalid(String) 44| | } 45| | 46| | struct Point: Hashable { 47| | var x: Float 48| | var y: Float 49| | 50| 184| init(_ x: Float, _ y: Float) { 51| 184| self.x = x 52| 184| self.y = y 53| 184| } 54| | 55| 117| init(_ x: Int, _ y: Int) { 56| 117| self.x = Float(x) 57| 117| self.y = Float(y) 58| 117| } 59| | 60| 27| static var zero: Point { 61| 27| return Point(0, 0) 62| 27| } 63| | } 64| | 65| | struct Size: Hashable { 66| | var width: Float 67| | var height: Float 68| | 69| 22| init(_ width: Float, _ height: Float) { 70| 22| self.width = width 71| 22| self.height = height 72| 22| } 73| | 74| 47| init(_ width: Int, _ height: Int) { 75| 47| self.width = Float(width) 76| 47| self.height = Float(height) 77| 47| } 78| | 79| 8| static var zero: Size { 80| 8| return Size(0, 0) 81| 8| } 82| | } 83| | 84| | struct Rect: Hashable { 85| | var origin: Point 86| | var size: Size 87| | 88| 17| init(x: Float, y: Float, width: Float, height: Float) { 89| 17| self.origin = Point(x, y) 90| 17| self.size = Size(width, height) 91| 17| } 92| | 93| 26| init(x: Int, y: Int, width: Int, height: Int) { 94| 26| self.origin = Point(x, y) 95| 26| self.size = Size(width, height) 96| 26| } 97| | 98| 6| var x: Float { 99| 6| return origin.x 100| 6| } 101| | 102| 6| var y: Float { 103| 6| return origin.y 104| 6| } 105| | 106| 6| var width: Float { 107| 6| return size.width 108| 6| } 109| | 110| 6| var height: Float { 111| 6| return size.height 112| 6| } 113| | 114| 8| static var zero: Rect { 115| 8| return Rect(x: 0, y: 0, width: 0, height: 0) 116| 8| } 117| | } 118| | 119| | enum BlendMode { 120| | case normal 121| | case copy 122| | case sourceIn /* R = S*Da */ 123| | case destinationIn /* R = D*Sa */ 124| | } 125| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/NSImage+Image.swift: 1| |// 2| |// NSImage+Image.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 AppKit 33| |import CoreGraphics 34| | 35| |public extension NSImage { 36| 3| convenience init?(svgNamed name: String, in bundle: Bundle = Bundle.main) { 37| 3| guard let image = Image(named: name, in: bundle) else { return nil } 38| 2| 39| 2| self.init(size: image.size, flipped: true) { rect in 40| 1| guard let ctx = NSGraphicsContext.current?.cgContext else { return false } 41| 1| ctx.draw(image, in: CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height)) 42| 1| return true 43| 1| } 44| 2| } 45| |} 46| | 47| |public extension Image { 48| 1| func rasterize() -> NSImage { 49| 1| return rasterize(with: size) 50| 1| } 51| | 52| 3| func rasterize(with size: CGSize) -> NSImage { 53| 3| let imageSize = NSSize(width: size.width, height: size.height) 54| 3| 55| 3| let image = NSImage(size: imageSize, flipped: true) { rect in 56| 1| guard let ctx = NSGraphicsContext.current?.cgContext else { return false } 57| 1| ctx.draw(self, in: CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height)) 58| 1| return true 59| 1| } 60| 3| 61| 3| return image 62| 3| } 63| | 64| 4| func createBitmap(size: CGSize? = nil, scale: CGFloat = 1, isOpaque: Bool = false) -> NSBitmapImageRep? { 65| 4| 66| 4| let defaultScale = NSScreen.main?.backingScaleFactor ?? 1.0 67| 4| let renderScale = scale == 0 ? defaultScale : scale 68| 4| let renderSize = size ?? self.size 69| 4| 70| 4| let width = Int(ceil(renderSize.width * renderScale)) 71| 4| let height = Int(ceil(renderSize.height * renderScale)) 72| 4| 73| 4| return NSBitmapImageRep(bitmapDataPlanes: nil, 74| 4| pixelsWide: max(width, 0), 75| 4| pixelsHigh: max(height, 0), 76| 4| bitsPerSample: 8, 77| 4| samplesPerPixel: isOpaque ? 3 : 4, 78| 4| hasAlpha: !isOpaque, 79| 4| isPlanar: false, 80| 4| colorSpaceName: NSColorSpaceName.deviceRGB, 81| 4| bytesPerRow: 0, 82| 4| bitsPerPixel: 32) 83| 4| } 84| | 85| 2| func pngData(size: CGSize? = nil, scale: CGFloat = 1) -> Data? { 86| 2| guard let bitmap = createBitmap(size: size, scale: scale, isOpaque: false), 87| 2| let ctx = NSGraphicsContext(bitmapImageRep: bitmap)?.cgContext else { return nil } 88| 2| 89| 2| let rect = CGRect(x: 0, y: 0, width: CGFloat(bitmap.pixelsWide), height: CGFloat(bitmap.pixelsHigh)) 90| 2| let flip = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: rect.size.height) 91| 2| ctx.concatenate(flip) 92| 2| ctx.draw(self, in: rect) 93| 2| 94| 2| return bitmap.representation(using: .png, properties: [:]) 95| 2| } 96| | 97| 2| func jpegData(size: CGSize? = nil, scale: CGFloat = 1, compressionQuality quality: CGFloat = 1) -> Data? { 98| 2| guard let bitmap = createBitmap(size: size, scale: scale, isOpaque: true), 99| 2| let ctx = NSGraphicsContext(bitmapImageRep: bitmap)?.cgContext else { return nil } 100| 2| 101| 2| let rect = CGRect(x: 0, y: 0, width: CGFloat(bitmap.pixelsWide), height: CGFloat(bitmap.pixelsHigh)) 102| 2| 103| 2| let flip = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: rect.size.height) 104| 2| ctx.concatenate(flip) 105| 2| ctx.setFillColor(.white) 106| 2| ctx.fill(rect) 107| 2| ctx.draw(self, in: rect) 108| 2| 109| 2| return bitmap.representation(using: .jpeg, properties: [NSBitmapImageRep.PropertyKey.compressionFactor: quality]) 110| 2| } 111| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Parser.XML.Attributes.swift: 1| |// 2| |// Parser.XML.Attributes.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| | // Storage for merging XMLElement attibutes and style properties; 36| | // style properties have precedence over element attibutes 37| | // 38| | // attributes["stoke"] == "2" 39| | // attributes["fill"] == "red" 40| | final class Attributes: AttributeParser { 41| | 42| | let parser: AttributeValueParser 43| | let options: XMLParser.Options 44| | 45| | let element: [String: String] 46| | let style: [String: String] 47| | 48| | init(parser: AttributeValueParser, 49| | options: XMLParser.Options = [], 50| | element: [String: String], 51| 83| style: [String: String]) { 52| 83| self.parser = parser 53| 83| self.options = options 54| 83| self.element = element 55| 83| self.style = style 56| 83| } 57| | 58| 838| func parse(_ key: String, _ exp: (String) throws -> T) throws -> T { 59| 838| do { 60| 838| return try parse(style[key], with: exp, for: key) 61| 838| } catch XMLParser.Error.missingAttribute(_) { 62| 809| return try parse(element[key], with: exp, for: key) 63| 809| } catch let error { 64| 3| guard options.contains(.skipInvalidAttributes) else { throw error } 65| 18.4E| } 66| 18.4E| return try parse(element[key], with: exp, for: key) 67| 838| } 68| | 69| 1.64k| func parse(_ value: String?, with expression: (String) throws -> T, for key: String) throws -> T { 70| 1.64k| guard let value = value else { throw XMLParser.Error.missingAttribute(name: key) } 71| 235| guard let result = try? expression(value) else { 72| 5| throw XMLParser.Error.invalidAttribute(name: key, value: value) 73| 230| } 74| 230| return result 75| 235| } 76| | } 77| |} 78| | 79| | 80| |extension XMLParser { 81| | 82| | struct ValueParser: AttributeValueParser { 83| | 84| 16| func parseFloat(_ value: String) throws -> DOM.Float { 85| 16| var scanner = XMLParser.Scanner(text: value) 86| 16| return try scanner.scanFloat() 87| 16| } 88| | 89| 9| func parseFloats(_ value: String) throws -> [DOM.Float] { 90| 9| var array = [DOM.Float]() 91| 9| var scanner = XMLParser.Scanner(text: value) 92| 9| 93| 27| while !scanner.isEOF { 94| 18| let vx = try scanner.scanFloat() 95| 18| scanner.scanStringIfPossible(",") 96| 18| array.append(vx) 97| 18| } 98| 9| 99| 9| return array 100| 9| } 101| | 102| | 103| 34| func parsePercentage(_ value: String) throws -> DOM.Float { 104| 34| var scanner = XMLParser.Scanner(text: value) 105| 34| guard let pc = try? scanner.scanPercentage() else { 106| 6| //try a value between 0.0, 1.0 107| 6| return try scanner.scanPercentageFloat() 108| 28| } 109| 28| return pc 110| 34| } 111| | 112| 196| func parseCoordinate(_ value: String) throws -> DOM.Coordinate { 113| 196| var scanner = XMLParser.Scanner(text: value) 114| 196| return try scanner.scanCoordinate() 115| 196| } 116| | 117| 10| func parseLength(_ value: String) throws -> DOM.Length { 118| 10| var scanner = XMLParser.Scanner(text: value) 119| 10| return try scanner.scanLength() 120| 10| } 121| | 122| 11| func parseBool(_ value: String) throws -> DOM.Bool { 123| 11| var scanner = XMLParser.Scanner(text: value) 124| 11| return try scanner.scanBool() 125| 11| } 126| | 127| 59| func parseFill(_ value: String) throws -> DOM.Fill { 128| 59| return try XMLParser().parseFill(value) 129| 59| } 130| | 131| 14| func parseUrl(_ value: String) throws -> DOM.URL { 132| 14| guard let url = URL(maybeData: value) else { throw XMLParser.Error.invalid } 133| 13| return url 134| 14| 135| 14| } 136| 8| func parseUrlSelector(_ value: String) throws -> DOM.URL { 137| 8| var scanner = XMLParser.Scanner(text: value) 138| 8| 139| 8| try scanner.scanString("url(") 140| 8| let urlText = try scanner.scanString(upTo: ")") 141| 8| _ = try? scanner.scanString(")") 142| 8| 143| 8| let url = urlText.trimmingCharacters(in: .whitespaces) 144| 8| 145| 8| guard !url.isEmpty, scanner.isEOF else { 146| 1| throw XMLParser.Error.invalid 147| 7| } 148| 7| 149| 7| return try parseUrl(url) 150| 8| } 151| | 152| 13| func parsePoints(_ value: String) throws -> [DOM.Point] { 153| 13| var points = [DOM.Point]() 154| 13| var scanner = XMLParser.Scanner(text: value) 155| 13| let delimeter = CharacterSet(charactersIn: ",;") 156| 13| 157| 47| while !scanner.isEOF { 158| 36| let px = try? scanner.scanCoordinate() 159| 36| _ = try? scanner.scanCharacter(matchingAny: delimeter) 160| 36| let py = try? scanner.scanCoordinate() 161| 36| _ = try? scanner.scanCharacter(matchingAny: delimeter) 162| 36| 163| 36| guard let x = px, 164| 36| let y = py else { throw XMLParser.Error.invalid } 165| 34| 166| 34| points.append(DOM.Point(x, y)) 167| 34| } 168| 11| 169| 11| return points 170| 13| } 171| | 172| 33| func parseRaw(_ value: String) throws -> T where T.RawValue == String { 173| 33| guard let obj = T(rawValue: value.trimmingCharacters(in: .whitespaces)) else { 174| 7| throw XMLParser.Error.invalid 175| 26| } 176| 26| return obj 177| 33| } 178| | } 179| | 180| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Parser.XML.Color.swift: 1| |// 2| |// Parser.XML.Color.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| |import Foundation 32| | 33| |extension XMLParser { 34| | 35| 78| func parseFill(_ data: String) throws -> DOM.Fill { 36| 78| if let c = try parseColorRGB(data: data) { 37| 8| return .color(c) 38| 70| } else if let c = try parseColorHex(data: data) { 39| 20| return .color(c) 40| 50| } else if let c = parseColorKeyword(data: data) { 41| 30| return .color(c) 42| 30| } else if let c = parseColorNone(data: data) { 43| 4| return .color(c) 44| 16| } else if let c = parseColorNone(data: data) { 45| 0| return .color(c) 46| 16| } else if let url = try parseURLSelector(data: data) { 47| 9| return .url(url) 48| 9| } 49| 7| 50| 7| throw Error.invalid 51| 16| } 52| | 53| 34| private func parseColorNone(data: String) -> DOM.Color? { 54| 34| if data.trimmingCharacters(in: .whitespaces) == "none" { 55| 4| return DOM.Color.none // .none resolves to Optional.none 56| 30| } 57| 30| return nil 58| 34| } 59| | 60| 49| private func parseColorKeyword(data: String) -> DOM.Color? { 61| 49| let raw = data.trimmingCharacters(in: .whitespaces) 62| 49| guard let keyword = DOM.Color.Keyword(rawValue: raw) else { 63| 19| return nil 64| 30| } 65| 30| return .keyword(keyword) 66| 49| } 67| | 68| 78| private func parseColorRGB(data: String) throws -> DOM.Color? { 69| 78| var scanner = XMLParser.Scanner(text: data) 70| 78| guard scanner.scanStringIfPossible("rgb(") else { return nil } 71| 8| 72| 8| if let c = try? parseColorRGBf(data: data) { 73| 4| return c 74| 4| } 75| 4| 76| 4| return try parseColorRGBi(data: data) 77| 8| } 78| | 79| 15| private func parseURLSelector(data: String) throws -> DOM.URL? { 80| 15| var scanner = XMLParser.Scanner(text: data) 81| 15| guard (try? scanner.scanString("url(")) == true else { 82| 5| return nil 83| 10| } 84| 10| 85| 10| let urlText = try scanner.scanString(upTo: ")") 86| 10| _ = try? scanner.scanString(")") 87| 10| 88| 10| let urlTrimmed = urlText.trimmingCharacters(in: .whitespaces) 89| 10| 90| 10| guard scanner.isEOF, let url = URL(string: urlTrimmed) else { 91| 0| throw XMLParser.Error.invalid 92| 10| } 93| 10| 94| 10| return url 95| 10| } 96| | 97| 4| private func parseColorRGBi(data: String) throws -> DOM.Color { 98| 4| var scanner = XMLParser.Scanner(text: data) 99| 4| try scanner.scanString("rgb(") 100| 4| 101| 4| let r = try scanner.scanUInt8() 102| 4| scanner.scanStringIfPossible(",") 103| 4| let g = try scanner.scanUInt8() 104| 4| scanner.scanStringIfPossible(",") 105| 4| let b = try scanner.scanUInt8() 106| 4| try scanner.scanString(")") 107| 4| return .rgbi(r, g, b) 108| 4| } 109| | 110| 8| private func parseColorRGBf(data: String) throws -> DOM.Color { 111| 8| var scanner = XMLParser.Scanner(text: data) 112| 8| try scanner.scanString("rgb(") 113| 8| 114| 8| let r = try scanner.scanPercentage() 115| 8| scanner.scanStringIfPossible(",") 116| 8| let g = try scanner.scanPercentage() 117| 8| scanner.scanStringIfPossible(",") 118| 8| let b = try scanner.scanPercentage() 119| 8| try scanner.scanString(")") 120| 8| 121| 8| return .rgbf(r, g, b) 122| 8| } 123| | 124| | // #a5F should be parsed as #a050F0 125| 20| private func padHex(_ data: String) -> String? { 126| 117| let chars = data.unicodeScalars.map({ $0 }) 127| 20| guard chars.count == 3 else { return data } 128| 1| 129| 1| return "\(chars[0])0\(chars[1])0\(chars[2])0" 130| 20| } 131| | 132| 70| private func parseColorHex(data: String) throws -> DOM.Color? { 133| 70| var scanner = XMLParser.Scanner(text: data) 134| 70| guard scanner.scanStringIfPossible("#") else { return nil } 135| 21| let hexadecimal = Foundation.CharacterSet(charactersIn: "0123456789ABCDEFabcdef") 136| 21| let code = try scanner.scanString(matchingAny: hexadecimal) 137| 21| guard 138| 21| let paddedCode = padHex(code), 139| 21| let hex = Int(paddedCode, radix: 16) else { 140| 0| throw Error.invalid 141| 21| } 142| 21| 143| 21| let r = UInt8((hex >> 16) & 0xff) 144| 21| let g = UInt8((hex >> 8) & 0xff) 145| 21| let b = UInt8(hex & 0xff) 146| 21| 147| 21| return .hex(r, g, b) 148| 21| } 149| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Parser.XML.Element.swift: 1| |// 2| |// Parser.XML.Element.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| 8| func parseLine(_ att: AttributeParser) throws -> DOM.Line { 35| 8| let x1: DOM.Coordinate = try att.parseCoordinate("x1") 36| 8| let y1: DOM.Coordinate = try att.parseCoordinate("y1") 37| 8| let x2: DOM.Coordinate = try att.parseCoordinate("x2") 38| 8| let y2: DOM.Coordinate = try att.parseCoordinate("y2") 39| 8| return DOM.Line(x1: x1, y1: y1, x2: x2, y2: y2) 40| 8| } 41| | 42| 10| func parseCircle(_ att: AttributeParser) throws -> DOM.Circle { 43| 10| let cx: DOM.Coordinate = try att.parseCoordinate("cx") 44| 10| let cy: DOM.Coordinate = try att.parseCoordinate("cy") 45| 10| let r: DOM.Coordinate = try att.parseCoordinate("r") 46| 10| return DOM.Circle(cx: cx, cy: cy, r: r) 47| 10| } 48| | 49| 1| func parseEllipse(_ att: AttributeParser) throws -> DOM.Ellipse { 50| 1| let cx: DOM.Coordinate = try att.parseCoordinate("cx") 51| 1| let cy: DOM.Coordinate = try att.parseCoordinate("cy") 52| 1| let rx: DOM.Coordinate = try att.parseCoordinate("rx") 53| 1| let ry: DOM.Coordinate = try att.parseCoordinate("ry") 54| 1| return DOM.Ellipse(cx: cx, cy: cy, rx: rx, ry: ry) 55| 1| } 56| | 57| 11| func parseRect(_ att: AttributeParser) throws -> DOM.Rect { 58| 11| let width: DOM.Coordinate = try att.parseCoordinate("width") 59| 11| let height: DOM.Coordinate = try att.parseCoordinate("height") 60| 11| let rect = DOM.Rect(width: width, height: height) 61| 11| 62| 11| rect.x = try att.parseCoordinate("x") 63| 11| rect.y = try att.parseCoordinate("y") 64| 11| rect.rx = try att.parseCoordinate("rx") 65| 11| rect.ry = try att.parseCoordinate("ry") 66| 11| 67| 11| return rect 68| 11| } 69| | 70| 1| func parsePolyline(_ att: AttributeParser) throws -> DOM.Polyline { 71| 1| return DOM.Polyline(points: try att.parsePoints("points")) 72| 1| } 73| | 74| 6| func parsePolygon(_ att: AttributeParser) throws -> DOM.Polygon { 75| 6| return DOM.Polygon(points: try att.parsePoints("points")) 76| 6| } 77| | 78| 40| func parseGraphicsElement(_ e: XML.Element) throws -> DOM.GraphicsElement? { 79| 40| var ge: DOM.GraphicsElement 80| 40| 81| 40| let att = try parseAttributes(e) 82| 40| 83| 40| switch e.name { 84| 40| case "g": ge = try parseGroup(e) 85| 40| case "line": ge = try parseLine(att) 86| 40| case "circle": ge = try parseCircle(att) 87| 40| case "ellipse": ge = try parseEllipse(att) 88| 40| case "rect": ge = try parseRect(att) 89| 40| case "polyline": ge = try parsePolyline(att) 90| 40| case "polygon": ge = try parsePolygon(att) 91| 40| case "path": ge = try parsePath(att) 92| 40| case "text": 93| 0| guard let text = try parseText(att, element: e) else { return nil } 94| 0| ge = text 95| 40| case "use": ge = try parseUse(att) 96| 40| case "switch": ge = try parseSwitch(e) 97| 40| case "image": ge = try parseImage(att) 98| 40| default: return nil 99| 40| } 100| 40| 101| 40| ge.id = e.attributes["id"] 102| 40| 103| 40| let presentation = try parsePresentationAttributes(att) 104| 40| ge.updateAttributes(from: presentation) 105| 40| 106| 40| return ge 107| 40| } 108| | 109| 17| func parseContainerChildren(_ e: XML.Element) throws -> [DOM.GraphicsElement] { 110| 17| guard e.name == "svg" || 111| 17| e.name == "clipPath" || 112| 17| e.name == "pattern" || 113| 17| e.name == "mask" || 114| 17| e.name == "defs" || 115| 17| e.name == "switch" || 116| 17| e.name == "g" else { 117| 0| throw Error.invalid 118| 17| } 119| 17| 120| 17| var children = [DOM.GraphicsElement]() 121| 17| 122| 36| for n in e.children { 123| 36| do { 124| 36| if let ge = try parseGraphicsElement(n) { 125| 26| children.append(ge) 126| 36| } 127| 36| } catch let error { 128| 0| if let parseError = XMLParser.parseError(for: error, parsing: n, with: options) { 129| 0| throw parseError 130| 0| } 131| 36| } 132| 36| } 133| 17| 134| 17| return children 135| 17| } 136| | 137| 3| static func parseError(for error: Swift.Error, parsing element: XML.Element, with options: Options) -> XMLParser.Error? { 138| 3| guard options.contains(.skipInvalidElements) == false else { 139| 1| return nil 140| 2| } 141| 2| 142| 2| switch error { 143| 2| case let XMLParser.Error.invalidElement(name, error, line, column): 144| 1| return .invalidElement(name: name, 145| 1| error: error, 146| 1| line: line, 147| 1| column: column) 148| 2| default: 149| 1| return .invalidElement(name: element.name, 150| 1| error: error, 151| 1| line: element.parsedLocation?.line, 152| 1| column: element.parsedLocation?.column) 153| 2| } 154| 2| } 155| | 156| 0| func parseGroup(_ e: XML.Element) throws -> DOM.Group { 157| 0| guard e.name == "g" else { 158| 0| throw Error.invalid 159| 0| } 160| 0| 161| 0| let group = DOM.Group() 162| 0| group.childElements = try parseContainerChildren(e) 163| 0| return group 164| 0| } 165| | 166| 0| func parseSwitch(_ e: XML.Element) throws -> DOM.Switch { 167| 0| guard e.name == "switch" else { 168| 0| throw Error.invalid 169| 0| } 170| 0| 171| 0| let node = DOM.Switch() 172| 0| node.childElements = try parseContainerChildren(e) 173| 0| return node 174| 0| } 175| | 176| 81| func parseAttributes(_ e: XML.Element) throws -> Attributes { 177| 81| guard let styleText = e.attributes["style"] else { 178| 74| return Attributes(parser: ValueParser(), 179| 74| options: options, 180| 74| element: e.attributes, 181| 74| style: [:]) 182| 74| } 183| 7| 184| 7| let style = try parseStyleAttributes(styleText) 185| 7| var element = e.attributes 186| 7| element["style"] = nil 187| 7| return Attributes(parser: ValueParser(), 188| 7| options: options, 189| 7| element: element, 190| 7| style: style) 191| 81| } 192| | 193| 13| func parseStyleAttributes(_ data: String) throws -> [String: String] { 194| 13| var scanner = XMLParser.Scanner(text: data) 195| 13| var style = [String: String]() 196| 13| 197| 43| while !scanner.isEOF { 198| 30| let att = try parseStyleAttribute(&scanner) 199| 30| style[att.0] = att.1 200| 30| } 201| 13| return style 202| 13| } 203| | 204| 30| private func parseStyleAttribute(_ scanner: inout XMLParser.Scanner) throws -> (String, String) { 205| 30| let key = try scanner.scanString(upTo: ":") 206| 30| _ = try? scanner.scanString(":") 207| 30| let value = try scanner.scanString(upTo: ";") 208| 30| _ = try? scanner.scanString(";") 209| 30| 210| 30| return (key.trimmingCharacters(in: .whitespaces), 211| 30| value.trimmingCharacters(in: .whitespaces)) 212| 30| } 213| | 214| 39| func parsePresentationAttributes(_ att: AttributeParser) throws -> PresentationAttributes { 215| 39| let el = DOM.GraphicsElement() 216| 39| 217| 39| el.opacity = try att.parsePercentage("opacity") 218| 39| el.display = try att.parseRaw("display") 219| 39| 220| 39| el.stroke = try att.parseColor("stroke") 221| 39| el.strokeWidth = try att.parseFloat("stroke-width") 222| 39| el.strokeOpacity = try att.parsePercentage("stroke-opacity") 223| 39| el.strokeLineCap = try att.parseRaw("stroke-linecap") 224| 39| el.strokeLineJoin = try att.parseRaw("stroke-linejoin") 225| 39| 226| 39| //maybe handle this better 227| 39| // att.parseDashArray? 228| 39| if let dash = try att.parseString("stroke-dasharray") as String?, 229| 39| dash.trimmingCharacters(in: .whitespaces) == "none" { 230| 0| el.strokeDashArray = nil 231| 39| } else { 232| 39| el.strokeDashArray = try att.parseFloats("stroke-dasharray") 233| 39| } 234| 39| 235| 39| do { 236| 39| el.fill = try att.parseFill("fill") 237| 39| } catch { 238| 0| print(error) 239| 39| } 240| 39| 241| 39| 242| 39| el.fillOpacity = try att.parsePercentage("fill-opacity") 243| 39| el.fillRule = try att.parseRaw("fill-rule") 244| 39| 245| 39| if let val = try? att.parseString("transform") { 246| 1| el.transform = try parseTransform(val) 247| 39| } 248| 39| 249| 39| el.clipPath = try att.parseUrlSelector("clip-path") 250| 39| el.mask = try att.parseUrlSelector("mask") 251| 39| 252| 39| return el 253| 39| } 254| | 255| | 256| |} 257| | 258| |extension PresentationAttributes { 259| | 260| 36| mutating func updateAttributes(from attributes: PresentationAttributes) { 261| 36| opacity = attributes.opacity 262| 36| display = attributes.display 263| 36| stroke = attributes.stroke 264| 36| strokeWidth = attributes.strokeWidth 265| 36| strokeOpacity = attributes.strokeOpacity 266| 36| strokeLineCap = attributes.strokeLineCap 267| 36| strokeLineJoin = attributes.strokeLineJoin 268| 36| strokeDashArray = attributes.strokeDashArray 269| 36| fill = attributes.fill 270| 36| fillOpacity = attributes.fillOpacity 271| 36| fillRule = attributes.fillRule 272| 36| transform = attributes.transform 273| 36| clipPath = attributes.clipPath 274| 36| mask = attributes.mask 275| 36| } 276| | 277| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Parser.XML.Gradient.swift: 1| |// 2| |// Parser.XML.Gradient.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| 45| func parseLinearGradients(_ e: XML.Element) throws -> [DOM.LinearGradient] { 35| 45| var gradients = [DOM.LinearGradient]() 36| 45| 37| 45| for n in e.children { 38| 45| if n.name == "linearGradient" { 39| 10| gradients.append(try parseLinearGradient(n)) 40| 45| } else { 41| 35| gradients.append(contentsOf: try parseLinearGradients(n)) 42| 45| } 43| 45| } 44| 45| return gradients 45| 45| } 46| | 47| 11| func parseLinearGradient(_ e: XML.Element) throws -> DOM.LinearGradient { 48| 11| guard e.name == "linearGradient" else { 49| 0| throw Error.invalid 50| 11| } 51| 11| 52| 11| let nodeAtt: AttributeParser = try parseAttributes(e) 53| 11| let node = DOM.LinearGradient(id: try nodeAtt.parseString("id")) 54| 11| node.x1 = try nodeAtt.parseCoordinate("x1") 55| 11| node.y1 = try nodeAtt.parseCoordinate("y1") 56| 11| node.x2 = try nodeAtt.parseCoordinate("x2") 57| 11| node.y2 = try nodeAtt.parseCoordinate("y2") 58| 11| 59| 15| for n in e.children where n.name == "stop" { 60| 15| let att: AttributeParser = try parseAttributes(n) 61| 15| node.stops.append(try parseLinearGradientStop(att)) 62| 15| } 63| 11| 64| 11| return node 65| 11| } 66| | 67| 19| func parseLinearGradientStop(_ att: AttributeParser) throws -> DOM.LinearGradient.Stop { 68| 19| let offset: DOM.Float = try att.parsePercentage("offset") 69| 19| let color: DOM.Color = try att.parseFill("stop-color").getColor() 70| 19| let opacity: DOM.Float? = try att.parsePercentage("stop-opacity") 71| 19| return DOM.LinearGradient.Stop(offset: offset, color: color, opacity: opacity ?? 1.0) 72| 19| } 73| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| 2| func parseImage(_ att: AttributeParser) throws -> DOM.Image { 35| 2| let href: DOM.URL = try att.parseUrl("xlink:href") 36| 2| let width: DOM.Coordinate = try att.parseCoordinate("width") 37| 2| let height: DOM.Coordinate = try att.parseCoordinate("height") 38| 2| 39| 2| let use = DOM.Image(href: href, width: width, height: height) 40| 2| use.x = try att.parseCoordinate("x") 41| 2| use.y = try att.parseCoordinate("y") 42| 2| 43| 2| return use 44| 2| } 45| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Parser.XML.Path.swift: 1| |// 2| |// Parser.XML.Path.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| | typealias PathScanner = Foundation.Scanner 37| | 38| | typealias Segment = DOM.Path.Segment 39| | typealias Command = DOM.Path.Command 40| | typealias CoordinateSpace = DOM.Path.Segment.CoordinateSpace 41| | 42| 4| func parsePath(_ att: AttributeParser) throws -> DOM.Path { 43| 4| let path = DOM.Path(x: 0, y: 0) 44| 4| path.segments = try parsePathSegments(try att.parseString("d")) 45| 4| return path 46| 4| } 47| | 48| 57| func parsePathSegments(_ data: String) throws -> [Segment] { 49| 57| 50| 57| var segments = Array() 51| 57| 52| 57| var scanner = PathScanner(string: data) 53| 57| 54| 57| scanner.charactersToBeSkipped = Foundation.CharacterSet.whitespacesAndNewlines 55| 57| 56| 57| var lastCommand: Command? 57| 57| 58| 74| repeat { 59| 74| if let cmd = parseCommand(&scanner) { 60| 71| lastCommand = cmd 61| 74| } 62| 74| guard let command = lastCommand else { throw Error.invalid } 63| 74| segments.append(try parsePathSegment(for: command, with: &scanner)) 64| 74| } while !scanner.isAtEnd 65| 57| 66| 57| return segments 67| 57| } 68| | 69| 74| func parsePathSegment(for command: Command, with scanner: inout PathScanner) throws -> Segment { 70| 74| switch command { 71| 74| case .move, .moveRelative: 72| 13| return try parseMoveSegment(for: command, with: &scanner) 73| 74| case .line, .lineRelative: 74| 9| return try parseLineSegment(for: command, with: &scanner) 75| 74| case .horizontal, .horizontalRelative: 76| 10| return try parseHorizontalSegment(for: command, with: &scanner) 77| 74| case .vertical, .verticalRelative: 78| 9| return try parseVerticalSegment(for: command, with: &scanner) 79| 74| case .cubic, .cubicRelative: 80| 5| return try parseCubicSegment(for: command, with: &scanner) 81| 74| case .cubicSmooth, .cubicSmoothRelative: 82| 5| return try parseCubicSmoothSegment(for: command, with: &scanner) 83| 74| case .quadratic, .quadraticRelative: 84| 5| return try parseQuadraticSegment(for: command, with: &scanner) 85| 74| case .quadraticSmooth, .quadraticSmoothRelative: 86| 5| return try parseQuadraticSmoothSegment(for: command, with: &scanner) 87| 74| case .arc, .arcRelative: 88| 7| return try parseArcSegment(for: command, with: &scanner) 89| 74| case .close, .closeAlias: 90| 6| return .close 91| 74| } 92| 74| } 93| | 94| 74| func parseCommand(_ scanner: inout PathScanner) -> Command? { 95| 74| guard let char = scanner.scan(first: .commands), 96| 74| let command = Command(rawValue: char) else { 97| 3| return nil 98| 71| } 99| 71| return command 100| 74| } 101| | 102| 13| func parseMoveSegment(for command: Command, with scanner: inout PathScanner) throws -> Segment { 103| 13| let x = try scanner.scanCoordinate() 104| 13| _ = scanner.scan(first: .delimeter) 105| 13| let y = try scanner.scanCoordinate() 106| 13| _ = scanner.scan(first: .delimeter) 107| 13| 108| 13| return .move(x: x, y: y, space: command.coordinateSpace) 109| 13| } 110| | 111| 9| func parseLineSegment(for command: Command, with scanner: inout PathScanner) throws -> Segment { 112| 9| let x = try scanner.scanCoordinate() 113| 9| _ = scanner.scan(first: .delimeter) 114| 9| let y = try scanner.scanCoordinate() 115| 9| _ = scanner.scan(first: .delimeter) 116| 9| 117| 9| return .line(x: x, y: y, space: command.coordinateSpace) 118| 9| } 119| | 120| 10| func parseHorizontalSegment(for command: Command, with scanner: inout PathScanner) throws -> Segment { 121| 10| let x = try scanner.scanCoordinate() 122| 10| _ = scanner.scan(first: .delimeter) 123| 10| 124| 10| return .horizontal(x: x, space: command.coordinateSpace) 125| 10| } 126| | 127| 9| func parseVerticalSegment(for command: Command, with scanner: inout PathScanner) throws -> Segment { 128| 9| let y = try scanner.scanCoordinate() 129| 9| _ = scanner.scan(first: .delimeter) 130| 9| 131| 9| return .vertical(y: y, space: command.coordinateSpace) 132| 9| } 133| | 134| 5| func parseCubicSegment(for command: Command, with scanner: inout PathScanner) throws -> Segment { 135| 5| let x1 = try scanner.scanCoordinate() 136| 5| _ = scanner.scan(first: .delimeter) 137| 5| let y1 = try scanner.scanCoordinate() 138| 5| _ = scanner.scan(first: .delimeter) 139| 5| let x2 = try scanner.scanCoordinate() 140| 5| _ = scanner.scan(first: .delimeter) 141| 5| let y2 = try scanner.scanCoordinate() 142| 5| _ = scanner.scan(first: .delimeter) 143| 5| let x = try scanner.scanCoordinate() 144| 5| _ = scanner.scan(first: .delimeter) 145| 5| let y = try scanner.scanCoordinate() 146| 5| _ = scanner.scan(first: .delimeter) 147| 5| 148| 5| return .cubic(x1: x1, y1: y1, x2: x2, y2: y2, x: x, y: y, space: command.coordinateSpace) 149| 5| } 150| | 151| 5| func parseCubicSmoothSegment(for command: Command, with scanner: inout PathScanner) throws -> Segment { 152| 5| let x2 = try scanner.scanCoordinate() 153| 5| _ = scanner.scan(first: .delimeter) 154| 5| let y2 = try scanner.scanCoordinate() 155| 5| _ = scanner.scan(first: .delimeter) 156| 5| let x = try scanner.scanCoordinate() 157| 5| _ = scanner.scan(first: .delimeter) 158| 5| let y = try scanner.scanCoordinate() 159| 5| _ = scanner.scan(first: .delimeter) 160| 5| 161| 5| return .cubicSmooth(x2: x2, y2: y2, x: x, y: y, space: command.coordinateSpace) 162| 5| } 163| | 164| 5| func parseQuadraticSegment(for command: Command, with scanner: inout PathScanner) throws -> Segment { 165| 5| let x1 = try scanner.scanCoordinate() 166| 5| _ = scanner.scan(first: .delimeter) 167| 5| let y1 = try scanner.scanCoordinate() 168| 5| _ = scanner.scan(first: .delimeter) 169| 5| let x = try scanner.scanCoordinate() 170| 5| _ = scanner.scan(first: .delimeter) 171| 5| let y = try scanner.scanCoordinate() 172| 5| _ = scanner.scan(first: .delimeter) 173| 5| 174| 5| return .quadratic(x1: x1, y1: y1, x: x, y: y, space: command.coordinateSpace) 175| 5| } 176| | 177| 5| func parseQuadraticSmoothSegment(for command: Command, with scanner: inout PathScanner) throws -> Segment { 178| 5| let x = try scanner.scanCoordinate() 179| 5| _ = scanner.scan(first: .delimeter) 180| 5| let y = try scanner.scanCoordinate() 181| 5| _ = scanner.scan(first: .delimeter) 182| 5| 183| 5| return .quadraticSmooth(x: x, y: y, space: command.coordinateSpace) 184| 5| } 185| | 186| 7| func parseArcSegment(for command: Command, with scanner: inout PathScanner) throws -> Segment { 187| 7| let rx = try scanner.scanCoordinate() 188| 7| _ = scanner.scan(first: .delimeter) 189| 7| let ry = try scanner.scanCoordinate() 190| 7| _ = scanner.scan(first: .delimeter) 191| 7| let rotate = try scanner.scanCoordinate() 192| 7| _ = scanner.scan(first: .delimeter) 193| 7| let large = try scanner.scanBool() 194| 7| _ = scanner.scan(first: .delimeter) 195| 7| let sweep = try scanner.scanBool() 196| 7| _ = scanner.scan(first: .delimeter) 197| 7| let x = try scanner.scanCoordinate() 198| 7| _ = scanner.scan(first: .delimeter) 199| 7| let y = try scanner.scanCoordinate() 200| 7| _ = scanner.scan(first: .delimeter) 201| 7| 202| 7| return .arc(rx: rx, ry: ry, rotate: rotate, 203| 7| large: large, sweep: sweep, 204| 7| x: x, y: y, space: command.coordinateSpace) 205| 7| } 206| |} 207| | 208| |private extension CharacterSet { 209| | static let delimeter = CharacterSet(charactersIn: ",;") 210| | static let commands = CharacterSet(charactersIn: "MmLlHhVvCcSsQqTtAaZz") 211| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| 15| func parsePattern(_ att: AttributeParser) throws -> DOM.Pattern { 38| 15| 39| 15| let id: String = try att.parseString("id") 40| 15| let width: DOM.Coordinate = try att.parseCoordinate("width") 41| 15| let height: DOM.Coordinate = try att.parseCoordinate("height") 42| 15| 43| 15| var pattern = DOM.Pattern(id: id, width: width, height: height) 44| 15| pattern.x = try att.parseCoordinate("x") 45| 15| pattern.y = try att.parseCoordinate("y") 46| 15| 47| 15| pattern.patternUnits = try att.parseRaw("patternUnits") 48| 15| pattern.patternContentUnits = try att.parseRaw("patternContentUnits") 49| 15| 50| 15| return pattern 51| 15| } 52| | 53| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Parser.XML.SVG.swift: 1| |// 2| |// Parser.XML.SVG.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| 10| func parseSVG(_ e: XML.Element) throws -> DOM.SVG { 35| 10| guard e.name == "svg" else { 36| 1| throw Error.invalid 37| 9| } 38| 9| 39| 9| let att = try parseAttributes(e) 40| 9| var width: DOM.Coordinate? = try att.parseCoordinate("width") 41| 9| var height: DOM.Coordinate? = try att.parseCoordinate("height") 42| 9| let viewBox: DOM.SVG.ViewBox? = try parseViewBox(try att.parseString("viewBox")) 43| 9| 44| 9| width = width ?? viewBox?.width 45| 9| height = height ?? viewBox?.height 46| 9| 47| 9| guard let w = width else { throw XMLParser.Error.missingAttribute(name: "width") } 48| 8| guard let h = height else { throw XMLParser.Error.missingAttribute(name: "height") } 49| 7| 50| 7| var svg = DOM.SVG(width: DOM.Length(w), height: DOM.Length(h)) 51| 7| svg.childElements = try parseContainerChildren(e) 52| 7| svg.viewBox = try parseViewBox(try att.parseString("viewBox")) 53| 7| 54| 7| svg.defs = try parseSVGDefs(e) 55| 7| 56| 7| let presentation = try parsePresentationAttributes(att) 57| 7| svg.updateAttributes(from: presentation) 58| 7| 59| 7| return svg 60| 8| } 61| | 62| 21| func parseViewBox(_ data: String?) throws -> DOM.SVG.ViewBox? { 63| 21| guard let data = data else { return nil } 64| 7| var scanner = XMLParser.Scanner(text: data) 65| 7| 66| 7| let x = try scanner.scanCoordinate() 67| 7| let y = try scanner.scanCoordinate() 68| 7| let width = try scanner.scanCoordinate() 69| 7| let height = try scanner.scanCoordinate() 70| 7| 71| 7| guard scanner.isEOF else { 72| 2| throw Error.invalid 73| 5| } 74| 5| 75| 5| return DOM.SVG.ViewBox(x: x, y: y, width: width, height: height) 76| 7| } 77| | 78| | 79| | // search all nodes within document for any defs 80| | // not just the node 81| 8| func parseSVGDefs(_ e: XML.Element) throws -> DOM.SVG.Defs { 82| 8| var defs = DOM.SVG.Defs() 83| 8| defs.clipPaths = try parseClipPaths(e) 84| 8| defs.linearGradients = try parseLinearGradients(e) 85| 8| defs.masks = try parseMasks(e) 86| 8| defs.patterns = try parsePatterns(e) 87| 8| 88| 8| defs.elements = try findDefElements(within: e).reduce(into: [String: DOM.GraphicsElement]()) { 89| 5| let defs = try parseDefsElements($1) 90| 5| $0.merge(defs, uniquingKeysWith: { lhs, _ in lhs }) 91| 5| } 92| 8| 93| 8| return defs 94| 8| } 95| | 96| 25| func findDefElements(within element: XML.Element) -> [XML.Element] { 97| 25| return element.children.reduce(into: [XML.Element]()) { 98| 22| if $1.name == "defs" { 99| 5| $0.append($1) 100| 22| } else { 101| 17| $0.append(contentsOf: findDefElements(within: $1)) 102| 22| } 103| 22| } 104| 25| } 105| | 106| 5| func parseDefsElements(_ e: XML.Element) throws -> [String: DOM.GraphicsElement] { 107| 5| guard e.name == "defs" else { 108| 0| throw Error.invalid 109| 5| } 110| 5| 111| 5| var defs = Dictionary() 112| 5| let elements = try parseContainerChildren(e) 113| 5| 114| 5| for e in elements { 115| 3| guard let id = e.id else { 116| 0| throw Error.invalid 117| 3| } 118| 3| defs[id] = e 119| 5| } 120| 5| 121| 5| return defs 122| 5| } 123| | 124| | 125| 58| func parseClipPaths(_ e: XML.Element) throws -> [DOM.ClipPath] { 126| 58| var clipPaths = [DOM.ClipPath]() 127| 58| 128| 58| for n in e.children { 129| 50| if n.name == "clipPath" { 130| 0| clipPaths.append(try parseClipPath(n)) 131| 50| } else { 132| 50| clipPaths.append(contentsOf: try parseClipPaths(n)) 133| 50| } 134| 58| } 135| 58| return clipPaths 136| 58| } 137| | 138| 2| func parseClipPath(_ e: XML.Element) throws -> DOM.ClipPath { 139| 2| guard e.name == "clipPath" else { throw Error.invalid } 140| 2| 141| 2| let att = try parseAttributes(e) 142| 2| let id: String = try att.parseString("id") 143| 2| 144| 2| let children = try parseContainerChildren(e) 145| 2| return DOM.ClipPath(id: id, childElements: children) 146| 2| } 147| | 148| 58| func parseMasks(_ e: XML.Element) throws -> [DOM.Mask] { 149| 58| var masks = [DOM.Mask]() 150| 58| 151| 58| for n in e.children { 152| 50| if n.name == "mask" { 153| 0| masks.append(try parseMask(n)) 154| 50| } else { 155| 50| masks.append(contentsOf: try parseMasks(n)) 156| 50| } 157| 58| } 158| 58| return masks 159| 58| } 160| | 161| 0| func parseMask(_ e: XML.Element) throws -> DOM.Mask { 162| 0| guard e.name == "mask" else { throw Error.invalid } 163| 0| 164| 0| let att = try parseAttributes(e) 165| 0| let id: String = try att.parseString("id") 166| 0| 167| 0| let children = try parseContainerChildren(e) 168| 0| return DOM.Mask(id: id, childElements: children) 169| 0| } 170| | 171| 49| func parsePatterns(_ e: XML.Element) throws -> [DOM.Pattern] { 172| 49| var patterns = [DOM.Pattern]() 173| 49| 174| 49| for n in e.children { 175| 44| if n.name == "pattern" { 176| 3| patterns.append(try parsePattern(n)) 177| 44| } else { 178| 41| patterns.append(contentsOf: try parsePatterns(n)) 179| 44| } 180| 49| } 181| 49| return patterns 182| 49| } 183| | 184| 3| func parsePattern(_ e: XML.Element) throws -> DOM.Pattern { 185| 3| guard e.name == "pattern" else { throw Error.invalid } 186| 3| 187| 3| let att = try parseAttributes(e) 188| 3| var pattern = try parsePattern(att) 189| 3| pattern.childElements = try parseContainerChildren(e) 190| 3| return pattern 191| 3| } 192| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| 3| func parseText(_ att: AttributeParser, element: XML.Element) throws -> DOM.Text? { 37| 3| guard 38| 3| let text = element.innerText?.trimmingCharacters(in: .whitespacesAndNewlines), 39| 3| !text.isEmpty else { 40| 2| return nil 41| 2| } 42| 1| 43| 1| return try parseText(att, value: text) 44| 3| } 45| | 46| 4| func parseText(_ att: AttributeParser, value: String) throws -> DOM.Text { 47| 4| let element = DOM.Text(value: value) 48| 4| element.x = try att.parseCoordinate("x") 49| 4| element.y = try att.parseCoordinate("y") 50| 4| element.fontFamily = (try att.parseString("font-family"))?.trimmingCharacters(in: .whitespacesAndNewlines) 51| 4| element.fontSize = try att.parseFloat("font-size") 52| 4| 53| 4| return element 54| 4| } 55| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Parser.XML.Transform.swift: 1| |// 2| |// Parser.XML.Transform.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| 50| func parseTransform(_ data: String) throws -> [DOM.Transform] { 35| 50| 36| 50| var scanner = XMLParser.Scanner(text: data) 37| 50| var transforms = [DOM.Transform]() 38| 50| 39| 50| while let transform = try parseTransform(&scanner) { 40| 24| transforms.append(transform) 41| 50| } 42| 50| 43| 50| guard scanner.isEOF else { 44| 7| // expecting EOF 45| 7| throw Error.invalid 46| 43| } 47| 43| 48| 43| return transforms 49| 50| } 50| | 51| 74| private func parseTransform(_ scanner: inout XMLParser.Scanner) throws -> DOM.Transform? { 52| 74| 53| 74| if let t = try parseMatrix(&scanner) { 54| 3| return t 55| 71| } else if let t = try parseTranslate(&scanner) { 56| 5| return t 57| 66| } else if let t = try parseScale(&scanner) { 58| 7| return t 59| 59| } else if let t = try parseRotate(&scanner) { 60| 3| return t 61| 56| } else if let t = try parseSkewX(&scanner) { 62| 3| return t 63| 53| } else if let t = try parseSkewY(&scanner) { 64| 3| return t 65| 50| } 66| 50| return nil 67| 53| } 68| | 69| 74| private func parseMatrix(_ scanner: inout XMLParser.Scanner) throws -> DOM.Transform? { 70| 74| guard (try? scanner.scanString("matrix(")) == true else { 71| 68| return nil 72| 68| } 73| 6| 74| 6| let a = try scanner.scanFloat() 75| 6| _ = try? scanner.scanString(",") 76| 6| let b = try scanner.scanFloat() 77| 6| _ = try? scanner.scanString(",") 78| 6| let c = try scanner.scanFloat() 79| 6| _ = try? scanner.scanString(",") 80| 6| let d = try scanner.scanFloat() 81| 6| _ = try? scanner.scanString(",") 82| 6| let e = try scanner.scanFloat() 83| 6| _ = try? scanner.scanString(",") 84| 6| let f = try scanner.scanFloat() 85| 6| _ = try scanner.scanString(")") 86| 6| 87| 6| return .matrix(a: a, b: b, c: c, d: d, e: e, f: f) 88| 74| } 89| | 90| 68| private func parseTranslate(_ scanner: inout XMLParser.Scanner) throws -> DOM.Transform? { 91| 68| guard (try? scanner.scanString("translate(")) == true else { 92| 60| return nil 93| 60| } 94| 8| 95| 8| let tx = try scanner.scanFloat() 96| 8| if scanner.scanStringIfPossible(")") { 97| 2| return .translate(tx: tx, ty: 0) 98| 6| } 99| 6| 100| 6| scanner.scanStringIfPossible(",") 101| 6| let ty = try scanner.scanFloat() 102| 6| try scanner.scanString(")") 103| 6| 104| 6| return .translate(tx: tx, ty: ty) 105| 8| } 106| | 107| 60| private func parseScale(_ scanner: inout XMLParser.Scanner) throws -> DOM.Transform? { 108| 60| guard (try? scanner.scanString("scale(")) == true else { 109| 50| return nil 110| 50| } 111| 10| 112| 10| let sx = try scanner.scanFloat() 113| 10| if scanner.scanStringIfPossible(")") { 114| 3| return .scale(sx: sx, sy: sx) 115| 7| } 116| 7| 117| 7| scanner.scanStringIfPossible(",") 118| 7| let sy = try scanner.scanFloat() 119| 7| try scanner.scanString(")") 120| 7| 121| 7| return .scale(sx: sx, sy: sy) 122| 10| } 123| | 124| 50| private func parseRotate(_ scanner: inout XMLParser.Scanner) throws -> DOM.Transform? { 125| 50| guard (try? scanner.scanString("rotate(")) == true else { 126| 41| return nil 127| 41| } 128| 9| 129| 9| let angle = try scanner.scanFloat() 130| 9| if scanner.scanStringIfPossible(")") { 131| 1| return .rotate(angle: angle) 132| 8| } 133| 8| 134| 8| scanner.scanStringIfPossible(",") 135| 8| let cx = try scanner.scanFloat() 136| 8| scanner.scanStringIfPossible(",") 137| 8| let cy = try scanner.scanFloat() 138| 8| try scanner.scanString(")") 139| 8| 140| 8| return .rotatePoint(angle: angle, cx: cx, cy: cy) 141| 9| } 142| | 143| 41| private func parseSkewX(_ scanner: inout XMLParser.Scanner) throws -> DOM.Transform? { 144| 41| guard (try? scanner.scanString("skewX(")) == true else { 145| 35| return nil 146| 35| } 147| 6| 148| 6| let angle = try scanner.scanFloat() 149| 6| _ = try scanner.scanString(")") 150| 6| return .skewX(angle: angle) 151| 41| } 152| | 153| 35| private func parseSkewY(_ scanner: inout XMLParser.Scanner) throws -> DOM.Transform? { 154| 35| guard (try? scanner.scanString("skewY(")) == true else { 155| 29| return nil 156| 29| } 157| 6| 158| 6| let angle = try scanner.scanFloat() 159| 6| _ = try scanner.scanString(")") 160| 6| return .skewY(angle: angle) 161| 35| } 162| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| 2| func parseUse(_ att: AttributeParser) throws -> DOM.Use { 35| 2| let use = DOM.Use(href: try att.parseUrl("xlink:href")) 36| 2| use.x = try att.parseCoordinate("x") 37| 2| use.y = try att.parseCoordinate("y") 38| 2| 39| 2| return use 40| 2| } 41| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Parser.XML.swift: 1| |// 2| |// Parser.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| |struct XMLParser { 33| | enum Error: Swift.Error { 34| | case invalid 35| | case missingAttribute(name: String) 36| | case invalidAttribute(name: String, value: Any) 37| | case invalidElement(name: String, error: Swift.Error, line: Int?, column: Int?) 38| | } 39| | 40| 245| var options: Options = [] 41| | 42| | struct Options: OptionSet { 43| | let rawValue: Int 44| 313| init(rawValue: Int) { 45| 313| self.rawValue = rawValue 46| 313| } 47| | 48| | static let skipInvalidAttributes = Options(rawValue: 1) 49| | static let skipInvalidElements = Options(rawValue: 2) 50| | } 51| |} 52| | 53| |protocol AttributeValueParser { 54| | func parseFloat(_ value: String) throws -> DOM.Float 55| | func parseFloats(_ value: String) throws -> [DOM.Float] 56| | func parsePercentage(_ value: String) throws -> DOM.Float 57| | func parseCoordinate(_ value: String) throws -> DOM.Coordinate 58| | func parseLength(_ value: String) throws -> DOM.Length 59| | func parseBool(_ value: String) throws -> DOM.Bool 60| | func parseFill(_ value: String) throws -> DOM.Fill 61| | func parseUrl(_ value: String) throws -> DOM.URL 62| | func parseUrlSelector(_ value: String) throws -> DOM.URL 63| | func parsePoints(_ value: String) throws -> [DOM.Point] 64| | 65| | func parseRaw(_ value: String) throws -> T where T.RawValue == String 66| |} 67| | 68| |protocol AttributeParser { 69| | var parser: AttributeValueParser { get } 70| | var options: XMLParser.Options { get } 71| | 72| | // either parse and return T or 73| | // throw Error.missingAttribute when key cannot resolve to a value 74| | // throw Error.invalidAttribute when value cannot be parsed into T 75| | func parse(_ key: String, _ exp: (String) throws -> T) throws -> T 76| |} 77| | 78| |extension AttributeParser { 79| | 80| 34| func parseString(_ key: String) throws -> String { 81| 34| return try parse(key) { $0 } 82| 34| } 83| | 84| 9| func parseFloat(_ key: String) throws -> DOM.Float { 85| 9| return try parse(key) { return try parser.parseFloat($0) } 86| 9| } 87| | 88| 2| func parseFloats(_ key: String) throws -> [DOM.Float] { 89| 2| return try parse(key) { return try parser.parseFloats($0) } 90| 2| } 91| | 92| 19| func parsePercentage(_ key: String) throws -> DOM.Float { 93| 19| return try parse(key) { return try parser.parsePercentage($0) } 94| 19| } 95| | 96| 123| func parseCoordinate(_ key: String) throws -> DOM.Coordinate { 97| 123| return try parse(key) { return try parser.parseCoordinate($0) } 98| 123| } 99| | 100| 2| func parseLength(_ key: String) throws -> DOM.Length { 101| 2| return try parse(key) { return try parser.parseLength($0) } 102| 2| } 103| | 104| 2| func parseBool(_ key: String) throws -> DOM.Bool { 105| 2| return try parse(key) { return try parser.parseBool($0) } 106| 2| } 107| | 108| 25| func parseFill(_ key: String) throws -> DOM.Fill { 109| 25| return try parse(key) { return try parser.parseFill($0) } 110| 25| } 111| | 112| 6| func parseColor(_ key: String) throws -> DOM.Color { 113| 6| return try parseFill(key).getColor() 114| 6| } 115| | 116| 5| func parseUrl(_ key: String) throws -> DOM.URL { 117| 5| return try parse(key) { return try parser.parseUrl($0) } 118| 5| } 119| | 120| 1| func parseUrlSelector(_ key: String) throws -> DOM.URL { 121| 1| return try parse(key) { return try parser.parseUrlSelector($0) } 122| 1| } 123| | 124| 9| func parsePoints(_ key: String) throws -> [DOM.Point] { 125| 9| return try parse(key) { return try parser.parsePoints($0) } 126| 9| } 127| | 128| 1| func parseRaw(_ key: String) throws -> T where T.RawValue == String { 129| 1| return try parse(key) { return try parser.parseRaw($0) } 130| 1| } 131| |} 132| | 133| |extension AttributeParser { 134| | 135| | typealias Options = XMLParser.Options 136| | 137| 808| func parse(_ key: String, exp: (String) throws -> T) throws -> T? { 138| 808| do { 139| 808| return try parse(key, exp) 140| 808| } catch XMLParser.Error.missingAttribute(_) { 141| 672| return nil 142| 672| } catch let error { 143| 7| guard options.contains(.skipInvalidAttributes) else { throw error } 144| 18.4E| } 145| 18.4E| return nil 146| 808| } 147| | 148| 97| func parseString(_ key: String) throws -> String? { 149| 97| return try parse(key) { $0 } 150| 97| } 151| | 152| 47| func parseFloat(_ key: String) throws -> DOM.Float? { 153| 47| return try parse(key) { return try parser.parseFloat($0) } 154| 47| } 155| | 156| 39| func parseFloats(_ key: String) throws -> [DOM.Float]? { 157| 39| return try parse(key) { return try parser.parseFloats($0) } 158| 39| } 159| | 160| 135| func parsePercentage(_ key: String) throws -> DOM.Float? { 161| 135| return try parse(key) { return try parser.parsePercentage($0) } 162| 135| } 163| | 164| 146| func parseCoordinate(_ key: String) throws -> DOM.Coordinate? { 165| 146| return try parse(key) { return try parser.parseCoordinate($0) } 166| 146| } 167| | 168| 2| func parseLength(_ key: String) throws -> DOM.Length? { 169| 2| return try parse(key) { return try parser.parseLength($0) } 170| 2| } 171| | 172| 2| func parseBool(_ key: String) throws -> DOM.Bool? { 173| 2| return try parse(key) { return try parser.parseBool($0) } 174| 2| } 175| | 176| 79| func parseFill(_ key: String) throws -> DOM.Fill? { 177| 79| return try parse(key) { return try parser.parseFill($0) } 178| 79| } 179| | 180| 40| func parseColor(_ key: String) throws -> DOM.Color? { 181| 40| return try parseFill(key)?.getColor() 182| 40| } 183| | 184| 2| func parseUrl(_ key: String) throws -> DOM.URL? { 185| 2| return try parse(key) { return try parser.parseUrl($0) } 186| 2| } 187| | 188| 78| func parseUrlSelector(_ key: String) throws -> DOM.URL? { 189| 78| return try parse(key) { return try parser.parseUrlSelector($0) } 190| 78| } 191| | 192| 2| func parsePoints(_ key: String) throws -> [DOM.Point]? { 193| 2| return try parse(key) { return try parser.parsePoints($0) } 194| 2| } 195| | 196| 179| func parseRaw(_ key: String) throws -> T? where T.RawValue == String { 197| 179| return try parse(key) { return try parser.parseRaw($0) } 198| 179| } 199| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Renderer.CoreGraphics.swift: 1| |// 2| |// Renderer.CoreGraphics.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 4/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 CoreGraphics 33| |import Foundation 34| |import CoreText 35| |#if os(macOS) 36| |import AppKit 37| |#elseif os(iOS) 38| |import UIKit 39| |#endif 40| | 41| |struct CGTypes: RendererTypes { 42| | typealias Float = CGFloat 43| | typealias Point = CGPoint 44| | typealias Size = CGSize 45| | typealias Rect = CGRect 46| | typealias Color = CGColor 47| | typealias Gradient = CGGradient 48| | typealias Mask = CGImage 49| | typealias Path = CGPath 50| | typealias Pattern = CGPattern 51| | typealias Transform = CGAffineTransform 52| | typealias BlendMode = CGBlendMode 53| | typealias FillRule = CGPathFillRule 54| | typealias LineCap = CGLineCap 55| | typealias LineJoin = CGLineJoin 56| | typealias Image = CGImage 57| |} 58| | 59| |struct CGProvider: RendererTypeProvider { 60| | typealias Types = CGTypes 61| | 62| | var supportsTransparencyLayers: Bool 63| | 64| 35| init(supportsTransparencyLayers: Bool = true) { 65| 35| self.supportsTransparencyLayers = supportsTransparencyLayers 66| 35| } 67| | 68| 23| func createFloat(from float: LayerTree.Float) -> CGFloat { 69| 23| return CGFloat(float) 70| 23| } 71| | 72| 34| func createPoint(from point: LayerTree.Point) -> CGPoint { 73| 34| return CGPoint(x: CGFloat(point.x), y: CGFloat(point.y)) 74| 34| } 75| | 76| 2| func createSize(from size: LayerTree.Size) -> CGSize { 77| 2| return CGSize(width: CGFloat(size.width), height: CGFloat(size.height)) 78| 2| } 79| | 80| 6| func createRect(from rect: LayerTree.Rect) -> CGRect { 81| 6| return CGRect(x: CGFloat(rect.x), 82| 6| y: CGFloat(rect.y), 83| 6| width: CGFloat(rect.width), 84| 6| height: CGFloat(rect.height)) 85| 6| } 86| | 87| 20| func createColor(from color: LayerTree.Color) -> CGColor { 88| 20| switch color { 89| 20| case .none: return createColor(r: 0, g: 0, b: 0, a: 0) 90| 20| case let .rgba(r, g, b, a): return createColor(r: CGFloat(r), 91| 19| g: CGFloat(g), 92| 19| b: CGFloat(b), 93| 19| a: CGFloat(a)) 94| 20| case .gray(white: let w, a: let a): 95| 0| return createColor(w: CGFloat(w), a: CGFloat(a)) 96| 20| } 97| 20| } 98| | 99| 0| func createGradient(from gradient: LayerTree.Gradient) -> CGGradient { 100| 0| let colors = gradient.stops.map { createColor(from: $0.color) } as CFArray 101| 0| var points = gradient.stops.map { createFloat(from: $0.offset) } 102| 0| 103| 0| return CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), 104| 0| colors: colors, 105| 0| locations: &points)! 106| 0| } 107| | 108| 20| private func createColor(r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) -> CGColor { 109| 20| return CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), 110| 20| components: [r, g, b, a])! 111| 20| } 112| | 113| 0| private func createColor(w: CGFloat, a: CGFloat) -> CGColor { 114| 0| return CGColor(colorSpace: CGColorSpaceCreateExtendedGray(), 115| 0| components: [w, a])! 116| 0| } 117| | 118| 0| func createMask(from contents: [RendererCommand], size: LayerTree.Size) -> CGImage { 119| 0| 120| 0| return CGImage.makeMask(size: createSize(from: size)) { ctx in 121| 0| let renderer = CGRenderer(context: ctx) 122| 0| renderer.perform(contents) 123| 0| } 124| 0| } 125| | 126| 3| func createBlendMode(from mode: LayerTree.BlendMode) -> CGBlendMode { 127| 3| switch mode { 128| 3| case .normal: return .normal 129| 3| case .copy: return .copy 130| 3| case .sourceIn: return .sourceIn 131| 3| case .destinationIn: return .destinationIn 132| 3| } 133| 3| } 134| 1| func createTransform(from transform: LayerTree.Transform.Matrix) -> CGAffineTransform { 135| 1| return CGAffineTransform(a: CGFloat(transform.a), 136| 1| b: CGFloat(transform.b), 137| 1| c: CGFloat(transform.c), 138| 1| d: CGFloat(transform.d), 139| 1| tx: CGFloat(transform.tx), 140| 1| ty: CGFloat(transform.ty)) 141| 1| } 142| | 143| 17| func createPath(from shape: LayerTree.Shape) -> CGPath { 144| 17| switch shape { 145| 17| case .line(let points): 146| 11| let path = CGMutablePath() 147| 23| path.addLines(between: points.map{ createPoint(from: $0) }) 148| 11| return path 149| 17| case .rect(let frame, let radii): 150| 3| return CGPath(roundedRect: createRect(from: frame), 151| 3| cornerWidth: createFloat(from: radii.width), 152| 3| cornerHeight: createFloat(from: radii.height), 153| 3| transform: nil) 154| 17| case .ellipse(let frame): 155| 1| return CGPath(ellipseIn: createRect(from: frame), transform: nil) 156| 17| case .polygon(let points): 157| 1| let path = CGMutablePath() 158| 4| path.addLines(between: points.map{ createPoint(from: $0) }) 159| 1| path.closeSubpath() 160| 1| return path 161| 17| case .path(let path): 162| 1| return createPath(from: path) 163| 17| } 164| 17| } 165| | 166| 1| private func createPath(from path: LayerTree.Path) -> CGPath { 167| 1| let cgPath = CGMutablePath() 168| 4| for s in path.segments { 169| 4| switch s { 170| 4| case .move(let p): 171| 1| cgPath.move(to: createPoint(from: p)) 172| 4| case .line(let p): 173| 1| cgPath.addLine(to: createPoint(from: p)) 174| 4| case .cubic(let p, let cp1, let cp2): 175| 1| cgPath.addCurve(to: createPoint(from: p), 176| 1| control1: createPoint(from: cp1), 177| 1| control2: createPoint(from: cp2)) 178| 4| case .close: 179| 1| cgPath.closeSubpath() 180| 4| } 181| 4| } 182| 1| return cgPath 183| 1| } 184| | 185| 1| func createPath(from subPaths: [CGPath]) -> CGPath { 186| 1| let cgPath = CGMutablePath() 187| 1| 188| 2| for path in subPaths { 189| 2| cgPath.addPath(path) 190| 2| } 191| 1| 192| 1| return cgPath 193| 1| } 194| | 195| 1| func createPath(from text: String, at origin: LayerTree.Point, with attributes: LayerTree.TextAttributes) -> Types.Path? { 196| 1| let font = CTFontCreateWithName(attributes.fontName as CFString, 197| 1| createFloat(from: attributes.size), 198| 1| nil) 199| 1| guard let path = text.toPath(font: font) else { return nil } 200| 1| 201| 1| var transform = CGAffineTransform(translationX: createFloat(from: origin.x), y: createFloat(from: origin.y)) 202| 1| return path.copy(using: &transform) 203| 1| } 204| | 205| 0| func createPattern(from pattern: LayerTree.Pattern, contents: [RendererCommand]) -> CGPattern { 206| 0| let bounds = createRect(from: pattern.frame) 207| 0| return CGPattern.make(bounds: bounds, 208| 0| matrix: .identity, 209| 0| step: bounds.size, 210| 0| tiling: .constantSpacing, 211| 0| isColored: true) { ctx in 212| 0| let renderer = CGRenderer(context: ctx) 213| 0| renderer.perform(contents) 214| 0| } 215| 0| } 216| | 217| 14| func createFillRule(from rule: LayerTree.FillRule) -> CGPathFillRule { 218| 14| switch rule { 219| 14| case .nonzero: 220| 1| return .winding 221| 14| case .evenodd: 222| 13| return .evenOdd 223| 14| } 224| 14| } 225| | 226| 9| func createLineCap(from cap: LayerTree.LineCap) -> CGLineCap { 227| 9| switch cap { 228| 9| case .butt: return .butt 229| 9| case .round: return .round 230| 9| case .square: return .square 231| 9| } 232| 9| } 233| | 234| 9| func createLineJoin(from join: LayerTree.LineJoin) -> CGLineJoin { 235| 9| switch join { 236| 9| case .bevel: return .bevel 237| 9| case .round: return .round 238| 9| case .miter: return .miter 239| 9| } 240| 9| } 241| | 242| 0| func createImage(from image: LayerTree.Image) -> CGImage? { 243| 0| switch image { 244| 0| case .jpeg(data: let d): 245| 0| return CGImage.from(data: d) 246| 0| case .png(data: let d): 247| 0| return CGImage.from(data: d) 248| 0| } 249| 0| } 250| | 251| 0| func getBounds(from path: CGPath) -> LayerTree.Rect { 252| 0| let bounds = path.boundingBoxOfPath 253| 0| return LayerTree.Rect(x: LayerTree.Float(bounds.origin.x), 254| 0| y: LayerTree.Float(bounds.origin.y), 255| 0| width: LayerTree.Float(bounds.width), 256| 0| height: LayerTree.Float(bounds.height)) 257| 0| } 258| |} 259| | 260| |//TODO: replace with CG implementation 261| |private extension CGImage { 262| 0| static func from(data: Data) -> CGImage? { 263| 0| #if os(iOS) 264| 0| return UIImage(data: data)?.cgImage 265| 0| #elseif os(macOS) 266| 0| guard let image = NSImage(data: data) else { return nil } 267| 0| var rect = NSRect(x: 0, y: 0, width: image.size.width, height: image.size.height) 268| 0| return image.cgImage(forProposedRect: &rect, context: nil, hints: nil) 269| 0| #endif 270| 0| } 271| |} 272| | 273| |struct CGRenderer: Renderer { 274| | typealias Types = CGTypes 275| | 276| | let ctx: CGContext 277| | 278| 11| init(context: CGContext) { 279| 11| self.ctx = context 280| 11| } 281| | 282| 0| func pushState() { 283| 0| ctx.saveGState() 284| 0| } 285| | 286| 0| func popState() { 287| 0| ctx.restoreGState() 288| 0| } 289| | 290| 0| func pushTransparencyLayer() { 291| 0| ctx.beginTransparencyLayer(auxiliaryInfo: nil) 292| 0| } 293| | 294| 0| func popTransparencyLayer() { 295| 0| ctx.endTransparencyLayer() 296| 0| } 297| | 298| 0| func concatenate(transform: CGAffineTransform) { 299| 0| ctx.concatenate(transform) 300| 0| } 301| | 302| 0| func translate(tx: CGFloat, ty: CGFloat) { 303| 0| ctx.translateBy(x: tx, y: ty) 304| 0| } 305| | 306| 0| func rotate(angle: CGFloat) { 307| 0| ctx.rotate(by: angle) 308| 0| } 309| | 310| 0| func scale(sx: CGFloat, sy: CGFloat) { 311| 0| ctx.scaleBy(x: sx, y: sy) 312| 0| } 313| | 314| 16| func setFill(color: CGColor) { 315| 16| ctx.setFillColor(color) 316| 16| } 317| | 318| 0| func setFill(pattern: CGPattern) { 319| 0| let patternSpace = CGColorSpace(patternBaseSpace: nil)! 320| 0| ctx.setFillColorSpace(patternSpace) 321| 0| var alpha : CGFloat = 1.0 322| 0| ctx.setFillPattern(pattern, colorComponents: &alpha) 323| 0| } 324| | 325| 2| func setStroke(color: CGColor) { 326| 2| ctx.setStrokeColor(color) 327| 2| } 328| | 329| 2| func setLine(width: CGFloat) { 330| 2| ctx.setLineWidth(width) 331| 2| } 332| | 333| 2| func setLine(cap: CGLineCap) { 334| 2| ctx.setLineCap(cap) 335| 2| } 336| | 337| 2| func setLine(join: CGLineJoin) { 338| 2| ctx.setLineJoin(join) 339| 2| } 340| | 341| 2| func setLine(miterLimit: CGFloat) { 342| 2| ctx.setMiterLimit(miterLimit) 343| 2| } 344| | 345| 0| func setClip(path: CGPath) { 346| 0| ctx.addPath(path) 347| 0| ctx.clip() 348| 0| } 349| | 350| 0| func setClip(mask: CGImage, frame: CGRect) { 351| 0| // let rect = CGRect(x: 0, y: 0, width: mask.width, height: mask.height) 352| 0| // ctx.draw(mask, in: rect) 353| 0| ctx.clip(to: frame, mask: mask) 354| 0| } 355| | 356| 0| func setAlpha(_ alpha: CGFloat) { 357| 0| ctx.setAlpha(alpha) 358| 0| } 359| | 360| 0| func setBlend(mode: CGBlendMode) { 361| 0| ctx.setBlendMode(mode) 362| 0| } 363| | 364| 2| func stroke(path: CGPath) { 365| 2| ctx.addPath(path) 366| 2| ctx.strokePath() 367| 2| } 368| | 369| 16| func fill(path: CGPath, rule: CGPathFillRule) { 370| 16| ctx.addPath(path) 371| 16| ctx.fillPath(using: rule) 372| 16| } 373| | 374| 0| func draw(image: CGImage) { 375| 0| let rect = CGRect(x: 0, y: 0, width: image.width, height: image.height) 376| 0| ctx.draw(image, in: rect) 377| 0| } 378| | 379| 0| func draw(gradient: CGGradient, from start: CGPoint, to end: CGPoint) { 380| 0| ctx.drawLinearGradient(gradient, 381| 0| start: start, 382| 0| end: end, 383| 0| options: [.drawsAfterEndLocation, .drawsBeforeStartLocation]) 384| 0| } 385| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Renderer.LayerTree.swift: 1| |// 2| |// Renderer.LayerTree.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 14/6/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| |import Foundation 32| | 33| |struct LayerTreeTypes: RendererTypes { 34| | typealias Float = LayerTree.Float 35| | typealias Point = LayerTree.Point 36| | typealias Size = LayerTree.Size 37| | typealias Rect = LayerTree.Rect 38| | typealias Color = LayerTree.Color 39| | typealias Gradient = LayerTree.Gradient 40| | typealias Mask = [Any] 41| | typealias Path = [LayerTree.Shape] 42| | typealias Pattern = LayerTree.Pattern 43| | typealias Transform = LayerTree.Transform 44| | typealias BlendMode = LayerTree.BlendMode 45| | typealias FillRule = LayerTree.FillRule 46| | typealias LineCap = LayerTree.LineCap 47| | typealias LineJoin = LayerTree.LineJoin 48| | typealias Image = LayerTree.Image 49| |} 50| | 51| |struct LayerTreeProvider: RendererTypeProvider { 52| | 53| | typealias Types = LayerTreeTypes 54| | 55| | var supportsTransparencyLayers: Bool = true 56| | 57| 6| func createFloat(from float: LayerTree.Float) -> LayerTree.Float { 58| 6| return float 59| 6| } 60| | 61| 1| func createPoint(from point: LayerTree.Point) -> LayerTree.Point { 62| 1| return point 63| 1| } 64| | 65| 1| func createSize(from size: LayerTree.Size) -> LayerTree.Size { 66| 1| return size 67| 1| } 68| | 69| 1| func createRect(from rect: LayerTree.Rect) -> LayerTree.Rect { 70| 1| return rect 71| 1| } 72| | 73| 1| func createColor(from color: LayerTree.Color) -> LayerTree.Color { 74| 1| return color 75| 1| } 76| | 77| 0| func createGradient(from gradient: LayerTree.Gradient) -> LayerTree.Gradient { 78| 0| return gradient 79| 0| } 80| | 81| 0| func createMask(from contents: [RendererCommand], size: LayerTree.Size) -> [Any] { 82| 0| return [] 83| 0| } 84| | 85| 1| func createBlendMode(from mode: LayerTree.BlendMode) -> LayerTree.BlendMode { 86| 1| return mode 87| 1| } 88| | 89| 2| func createTransform(from transform: LayerTree.Transform.Matrix) -> LayerTree.Transform { 90| 2| return .matrix(transform) 91| 2| } 92| | 93| 5| func createPath(from shape: LayerTree.Shape) -> [LayerTree.Shape] { 94| 5| return [shape] 95| 5| } 96| | 97| 0| func createPattern(from pattern: LayerTree.Pattern, contents: [RendererCommand]) -> LayerTreeTypes.Pattern { 98| 0| return pattern 99| 0| } 100| | 101| 2| func createPath(from subPaths: [[LayerTree.Shape]]) -> [LayerTree.Shape] { 102| 4| return subPaths.flatMap { $0 } 103| 2| } 104| | 105| 1| func createPath(from text: String, at origin: LayerTree.Point, with attributes: LayerTree.TextAttributes) -> [LayerTree.Shape]? { 106| 1| return nil 107| 1| } 108| | 109| 1| func createFillRule(from rule: LayerTree.FillRule) -> LayerTree.FillRule { 110| 1| return rule 111| 1| } 112| | 113| 1| func createLineCap(from cap: LayerTree.LineCap) -> LayerTree.LineCap { 114| 1| return cap 115| 1| } 116| | 117| 1| func createLineJoin(from join: LayerTree.LineJoin) -> LayerTree.LineJoin { 118| 1| return join 119| 1| } 120| | 121| 1| func createImage(from image: LayerTree.Image) -> LayerTree.Image? { 122| 1| return image 123| 1| } 124| | 125| 0| func getBounds(from path: Types.Path) -> LayerTree.Rect { 126| 0| return LayerTree.Rect(x: 0, y: 0, width: 0, height: 0) 127| 0| } 128| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/Renderer.swift: 1| |// 2| |// Renderer.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 4/6/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| |import Foundation 32| | 33| |protocol RendererTypes { 34| | associatedtype Float 35| | associatedtype Point 36| | associatedtype Size 37| | associatedtype Rect 38| | associatedtype Color 39| | associatedtype Gradient 40| | associatedtype Mask 41| | associatedtype Path 42| | associatedtype Pattern 43| | associatedtype Transform 44| | associatedtype BlendMode 45| | associatedtype FillRule 46| | associatedtype LineCap 47| | associatedtype LineJoin 48| | associatedtype Image 49| |} 50| | 51| |protocol RendererTypeProvider { 52| | associatedtype Types: RendererTypes 53| | 54| | var supportsTransparencyLayers: Bool { get } 55| | 56| | func createFloat(from float: LayerTree.Float) -> Types.Float 57| | func createPoint(from point: LayerTree.Point) -> Types.Point 58| | func createSize(from size: LayerTree.Size) -> Types.Size 59| | func createRect(from rect: LayerTree.Rect) -> Types.Rect 60| | func createColor(from color: LayerTree.Color) -> Types.Color 61| | func createGradient(from gradient: LayerTree.Gradient) -> Types.Gradient 62| | func createBlendMode(from mode: LayerTree.BlendMode) -> Types.BlendMode 63| | func createTransform(from transform: LayerTree.Transform.Matrix) -> Types.Transform 64| | func createMask(from contents: [RendererCommand], size: LayerTree.Size) -> Types.Mask 65| | func createPath(from shape: LayerTree.Shape) -> Types.Path 66| | func createPath(from subPaths: [Types.Path]) -> Types.Path 67| | func createPath(from text: String, at origin: LayerTree.Point, with attributes: LayerTree.TextAttributes) -> Types.Path? 68| | func createPattern(from pattern: LayerTree.Pattern, contents: [RendererCommand]) -> Types.Pattern 69| | func createFillRule(from rule: LayerTree.FillRule) -> Types.FillRule 70| | func createLineCap(from cap: LayerTree.LineCap) -> Types.LineCap 71| | func createLineJoin(from join: LayerTree.LineJoin) -> Types.LineJoin 72| | func createImage(from image: LayerTree.Image) -> Types.Image? 73| | 74| | func getBounds(from path: Types.Path) -> LayerTree.Rect 75| |} 76| | 77| |protocol Renderer { 78| | associatedtype Types: RendererTypes 79| | 80| | func pushState() 81| | func popState() 82| | func pushTransparencyLayer() 83| | func popTransparencyLayer() 84| | 85| | func concatenate(transform: Types.Transform) 86| | func translate(tx: Types.Float, ty: Types.Float) 87| | func rotate(angle: Types.Float) 88| | func scale(sx: Types.Float, sy: Types.Float) 89| | 90| | func setFill(color: Types.Color) 91| | func setFill(pattern: Types.Pattern) 92| | func setStroke(color: Types.Color) 93| | func setLine(width: Types.Float) 94| | func setLine(cap: Types.LineCap) 95| | func setLine(join: Types.LineJoin) 96| | func setLine(miterLimit: Types.Float) 97| | func setClip(path: Types.Path) 98| | func setClip(mask: Types.Mask, frame: Types.Rect) 99| | func setAlpha(_ alpha: Types.Float) 100| | func setBlend(mode: Types.BlendMode) 101| | 102| | func stroke(path: Types.Path) 103| | func fill(path: Types.Path, rule: Types.FillRule) 104| | func draw(image: Types.Image) 105| | func draw(gradient: Types.Gradient, from start: Types.Point, to end: Types.Point) 106| |} 107| | 108| |extension Renderer { 109| 63| func perform(_ command: RendererCommand) { 110| 63| switch command { 111| 63| case .pushState: 112| 1| pushState() 113| 63| case .popState: 114| 1| popState() 115| 63| case .pushTransparencyLayer: 116| 1| pushTransparencyLayer() 117| 63| case .popTransparencyLayer: 118| 1| popTransparencyLayer() 119| 63| case .concatenate(transform: let t): 120| 1| concatenate(transform: t) 121| 63| case .translate(tx: let x, ty: let y): 122| 1| translate(tx: x, ty: y) 123| 63| case .scale(sx: let x, sy: let y): 124| 1| scale(sx: x, sy: y) 125| 63| case .rotate(angle: let a): 126| 1| rotate(angle: a) 127| 63| case .setFill(color: let c): 128| 15| setFill(color: c) 129| 63| case .setFillPattern(let p): 130| 1| setFill(pattern: p) 131| 63| case .setStroke(color: let c): 132| 3| setStroke(color: c) 133| 63| case .setLine(width: let w): 134| 3| setLine(width: w) 135| 63| case .setLineCap(let c): 136| 3| setLine(cap: c) 137| 63| case .setLineJoin(let j): 138| 3| setLine(join: j) 139| 63| case .setLineMiter(limit: let l): 140| 3| setLine(miterLimit: l) 141| 63| case .setClip(path: let p): 142| 1| setClip(path: p) 143| 63| case .setClipMask(let m, frame: let f): 144| 1| setClip(mask: m, frame: f) 145| 63| case .setAlpha(let a): 146| 1| setAlpha(a) 147| 63| case .setBlend(mode: let m): 148| 1| setBlend(mode: m) 149| 63| case .stroke(let p): 150| 3| stroke(path: p) 151| 63| case .fill(let p, let r): 152| 15| fill(path: p, rule: r) 153| 63| case .draw(image: let i): 154| 1| draw(image: i) 155| 63| case .drawGradient(let g, let start, let end): 156| 1| draw(gradient: g, from: start, to: end) 157| 63| } 158| 63| } 159| | 160| 8| func perform(_ commands: [RendererCommand]) { 161| 63| for cmd in commands { 162| 63| perform(cmd) 163| 63| } 164| 8| } 165| |} 166| | 167| |enum RendererCommand { 168| | case pushState 169| | case popState 170| | 171| | case concatenate(transform: Types.Transform) 172| | case translate(tx: Types.Float, ty: Types.Float) 173| | case rotate(angle: Types.Float) 174| | case scale(sx: Types.Float, sy: Types.Float) 175| | 176| | case setFill(color: Types.Color) 177| | case setFillPattern(Types.Pattern) 178| | case setStroke(color: Types.Color) 179| | case setLine(width: Types.Float) 180| | case setLineCap(Types.LineCap) 181| | case setLineJoin(Types.LineJoin) 182| | case setLineMiter(limit: Types.Float) 183| | case setClip(path: Types.Path) 184| | case setClipMask(Types.Mask, frame: Types.Rect) 185| | case setAlpha(Types.Float) 186| | case setBlend(mode: Types.BlendMode) 187| | 188| | case stroke(Types.Path) 189| | case fill(Types.Path, rule: Types.FillRule) 190| | 191| | case draw(image: Types.Image) 192| | case drawGradient(Types.Gradient, from: Types.Point, to: Types.Point) 193| | 194| | case pushTransparencyLayer 195| | case popTransparencyLayer 196| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| 4| init(root: Element) { 37| 4| self.root = root 38| 4| storage = [Element]() 39| 4| } 40| | 41| | var top: Element { 42| 16| get { 43| 16| guard let last = storage.last else { return root } 44| 11| return last 45| 16| } 46| 5| set { 47| 5| guard storage.isEmpty else { 48| 3| storage.removeLast() 49| 3| storage.append(newValue) 50| 3| return 51| 3| } 52| 2| root = newValue 53| 2| } 54| | } 55| | 56| 10| mutating func push(_ element: Element) { 57| 10| storage.append(element) 58| 10| } 59| | 60| | @discardableResult 61| 8| mutating func pop() -> Bool { 62| 8| guard !storage.isEmpty else { return false } 63| 6| storage.removeLast() 64| 6| return true 65| 8| } 66| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/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| |extension URL { 35| | 36| 26| init?(maybeData string: String) { 37| 26| guard string.hasPrefix("data:") else { 38| 15| self.init(string: string) 39| 15| return 40| 15| } 41| 11| 42| 11| var removed = string.replacingOccurrences(of: "\t", with: "") 43| 11| removed = removed.replacingOccurrences(of: "\n", with: "") 44| 11| removed = removed.replacingOccurrences(of: " ", with: "") 45| 11| 46| 11| self.init(string: removed) 47| 11| } 48| | 49| | 50| 7| var isDataURL: Bool { 51| 7| return scheme == "data" 52| 7| } 53| | 54| 12| var decodedData: (mimeType: String, data: Data)? { 55| 12| let txt = absoluteString 56| 12| guard let schemeRange = txt.range(of: "data:"), 57| 12| let mimeRange = txt.range(of: ";", options: [], range: schemeRange.upperBound.. Bool { 51| 739| return try self.scanString(matchingAny: [token]) == token 52| 739| } 53| | 54| | @discardableResult 55| 232| mutating func scanStringIfPossible(_ token: String) -> Bool { 56| 232| return (try? self.scanString(token)) == true 57| 232| } 58| | 59| 739| mutating func scanString(matchingAny tokens: Set) throws -> String { 60| 739| scanner.scanLocation = scanLocation 61| 739| guard let match = tokens.first(where: { scanner.scanString($0, into: nil) }) else { 62| 498| throw Error.invalid 63| 498| } 64| 241| scanLocation = scanner.scanLocation 65| 241| return match 66| 739| } 67| | 68| 20| mutating func scanCase(from type: T.Type) throws -> T where T.RawValue == String { 69| 20| scanner.scanLocation = scanLocation 70| 20| 71| 69| guard let match = type.allCases.first(where: { scanner.scanString($0.rawValue, into: nil) }) else { 72| 6| throw Error.invalid 73| 14| } 74| 14| scanLocation = scanner.scanLocation 75| 14| return match 76| 20| } 77| | 78| 177| mutating func scanString(matchingAny characters: Foundation.CharacterSet) throws -> String { 79| 177| scanner.scanLocation = scanLocation 80| 177| var result: NSString? 81| 177| guard 82| 177| scanner.scanCharacters(from: characters, into: &result), 83| 177| let match = result.map({ $0 as String }), 84| 177| match.isEmpty == false else { 85| 41| throw Error.invalid 86| 136| } 87| 136| 88| 136| scanLocation = scanner.scanLocation 89| 136| return match 90| 177| } 91| | 92| 76| mutating func scanString(upTo token: String) throws -> String { 93| 76| scanner.scanLocation = scanLocation 94| 76| var result: NSString? 95| 76| guard 96| 76| scanner.scanUpTo(token, into: &result), 97| 76| let match = result.map({ $0 as String }) else { 98| 3| throw Error.invalid 99| 73| } 100| 73| 101| 73| scanLocation = scanner.scanLocation 102| 73| return match 103| 76| } 104| | 105| 84| mutating func scanCharacter(matchingAny characters: Foundation.CharacterSet) throws -> Character { 106| 84| let match = try scanString(matchingAny: characters) 107| 84| scanLocation = scanner.scanLocation - (match.count - 1) 108| 84| return match[match.startIndex] 109| 84| } 110| | 111| 17| mutating func scanUInt8() throws -> UInt8 { 112| 17| scanner.scanLocation = scanLocation 113| 17| var longVal: UInt64 = 0 114| 17| guard 115| 17| scanner.scanUnsignedLongLong(&longVal), 116| 17| let val = UInt8(exactly: longVal) else { 117| 2| throw Error.invalid 118| 15| } 119| 15| scanLocation = scanner.scanLocation 120| 15| return val 121| 17| } 122| | 123| 148| mutating func scanFloat() throws -> Float { 124| 148| scanner.scanLocation = scanLocation 125| 148| var val: Float = 0 126| 148| guard scanner.scanFloat(&val) else { 127| 22| throw Error.invalid 128| 126| } 129| 126| scanLocation = scanner.scanLocation 130| 126| return val 131| 148| } 132| | 133| 306| mutating func scanDouble() throws -> Double { 134| 306| scanner.scanLocation = scanLocation 135| 306| var val: Double = 0 136| 306| guard scanner.scanDouble(&val) else { 137| 8| throw Error.invalid 138| 298| } 139| 298| scanLocation = scanner.scanLocation 140| 298| return val 141| 306| } 142| | 143| 15| mutating func scanLength() throws -> DOM.Length { 144| 15| scanner.scanLocation = scanLocation 145| 15| var int64: Int64 = 0 146| 15| guard 147| 15| scanner.scanInt64(&int64), 148| 15| let val = DOM.Length(exactly: int64), 149| 15| val >= 0 else { 150| 7| throw Error.invalid 151| 8| } 152| 8| scanLocation = scanner.scanLocation 153| 8| return val 154| 15| } 155| | 156| 17| mutating func scanBool() throws -> Bool { 157| 17| return try self.scanCase(from: Boolean.self).boolValue 158| 17| } 159| | 160| 301| mutating func scanCoordinate() throws -> DOM.Coordinate { 161| 301| return DOM.Coordinate(try scanDouble()) 162| 301| } 163| | 164| 15| mutating func scanPercentageFloat() throws -> Float { 165| 15| scanner.scanLocation = scanLocation 166| 15| let val = try scanFloat() 167| 15| guard val >= 0.0, val <= 1.0 else { 168| 5| throw Error.invalid 169| 10| } 170| 10| scanLocation = scanner.scanLocation 171| 10| return val 172| 15| } 173| | 174| 61| mutating func scanPercentage() throws -> Float { 175| 61| let initialLocation = scanLocation 176| 61| scanner.scanLocation = scanLocation 177| 61| 178| 61| let numeric = Foundation.CharacterSet(charactersIn: "+-0123456789.Ee") 179| 61| let numericString = try scanString(matchingAny: numeric) 180| 61| 181| 61| guard 182| 61| let val = Double(numericString), 183| 61| val >= 0, val <= 100, 184| 61| scanner.scanString("%", into: nil) || val == 0 else { 185| 10| scanLocation = initialLocation 186| 10| throw Error.invalid 187| 51| } 188| 51| scanLocation = scanner.scanLocation 189| 51| return Float(val / 100.0) 190| 61| } 191| | } 192| |} 193| | 194| |private enum Boolean: String, CaseIterable { 195| | case `true` 196| | case `false` 197| | case upperFalse = "FALSE" 198| | case upperTrue = "TRUE" 199| | case zero = "0" 200| | case one = "1" 201| | 202| 30| var boolValue: Bool { 203| 30| switch self { 204| 30| case .true, .upperTrue, .one: 205| 16| return true 206| 30| case .false, .upperFalse, .zero: 207| 14| return false 208| 30| } 209| 30| } 210| |} 211| | 212| |extension Scanner { 213| | 214| | enum Error: Swift.Error { 215| | case invalid 216| | } 217| | 218| 19| func scanBool() throws -> Bool { 219| 98| guard let match = Boolean.allCases.first(where: { self.scanString($0.rawValue, into: nil) }) else { 220| 1| throw Error.invalid 221| 18| } 222| 18| 223| 18| return match.boolValue 224| 19| } 225| | 226| 266| func scan(first set: Foundation.CharacterSet) -> UnicodeScalar? { 227| 266| var val: NSString? 228| 266| let start = scanLocation 229| 266| guard scanCharacters(from: set, into: &val), 230| 266| let string = val, 231| 266| string.length > 0 else { 232| 129| 233| 129| scanLocation = start 234| 129| return nil 235| 137| } 236| 137| 237| 137| if string.length > 1 { 238| 1| scanLocation -= (string.length - 1) 239| 137| } 240| 137| 241| 137| return UnicodeScalar(string.character(at: 0)) 242| 137| } 243| | 244| 181| func scanCoordinate() throws -> DOM.Coordinate { 245| 181| var val: Double = 0 246| 181| guard scanDouble(&val) else { throw XMLParser.Error.invalid } 247| 180| return DOM.Coordinate(val) 248| 181| } 249| |} 250| | 251| | 252| |#if os(Linux) 253| | 254| |private extension Foundation.Scanner { 255| | 256| | func scanUpTo(_ string: String, into result: inout NSString?) -> Bool { 257| | return false 258| | } 259| | 260| | func scanCharacters(from set: CharacterSet, into result: inout NSString?) -> Bool { 261| | return false 262| | } 263| |} 264| | 265| |#endif /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/XML.SAXParser.swift: 1| |// 2| |// XML.SAXParser.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 28/1/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| |#if os(Linux) 35| |import FoundationXML 36| |#endif 37| | 38| |extension XML { 39| | 40| | final class SAXParser: NSObject, XMLParserDelegate { 41| | 42| | #if os(Linux) 43| | typealias XMLParser = FoundationXML.XMLParser 44| | #else 45| | typealias XMLParser = Foundation.XMLParser 46| | #endif 47| | 48| | private let parser: XMLParser 49| | private let namespaceURI = "http://www.w3.org/2000/svg" 50| | 51| | private var rootNode: Element? 52| | private var elements: [Element] 53| | 54| 50| private var currentElement: Element { 55| 50| return elements.last! 56| 50| } 57| | 58| 10| private init(data: Data) { 59| 10| self.parser = XMLParser(data: data) 60| 10| elements = [Element]() 61| 10| super.init() 62| 10| 63| 10| self.parser.delegate = self 64| 10| self.parser.shouldProcessNamespaces = true 65| 10| } 66| | 67| 10| static func parse(data: Data) throws -> Element { 68| 10| let parser = SAXParser(data: data) 69| 10| 70| 10| guard 71| 10| parser.parser.parse(), 72| 10| let rootNode = parser.rootNode else { 73| 3| let error = parser.parser.parserError ?? SwiftDraw.XMLParser.Error.invalid 74| 3| throw error 75| 7| } 76| 7| 77| 7| return rootNode 78| 10| } 79| | 80| 7| static func parse(contentsOf url: URL) throws -> Element { 81| 7| let data = try Data(contentsOf: url) 82| 7| return try parse(data: data) 83| 7| } 84| | 85| 53| func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName _: String?, attributes attributeDict: [String: String] = [:]) { 86| 53| guard 87| 53| self.parser === parser, 88| 53| namespaceURI == self.namespaceURI else { 89| 2| return 90| 51| } 91| 51| 92| 51| let element = Element(name: elementName, attributes: attributeDict) 93| 51| element.parsedLocation = (line: parser.lineNumber, column: parser.columnNumber) 94| 51| 95| 51| elements.last?.children.append(element) 96| 51| elements.append(element) 97| 51| 98| 51| if rootNode == nil { 99| 8| rootNode = element 100| 51| } 101| 51| } 102| | 103| 51| func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName _: String?) { 104| 51| guard 105| 51| namespaceURI == self.namespaceURI, 106| 51| currentElement.name == elementName else { 107| 1| return 108| 50| } 109| 50| 110| 50| elements.removeLast() 111| 50| } 112| | 113| 63| func parser(_ parser: XMLParser, foundCharacters string: String) { 114| 63| guard let element = elements.last else { return } 115| 62| let text = element.innerText.map { $0.appending(string) } 116| 62| element.innerText = text ?? string 117| 62| } 118| | } 119| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDraw/XML.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| |struct XML { 33| | final class Element { 34| | 35| | let name: String 36| | var attributes: [String: String] 37| 83| var children = [Element]() 38| | var innerText: String? 39| | 40| | var parsedLocation: (line: Int, column: Int)? 41| | 42| 83| init(name: String, attributes: [String: String] = [:]) { 43| 83| self.name = name 44| 83| self.attributes = attributes 45| 83| self.innerText = nil 46| 83| } 47| | } 48| |} <<<<<< EOF # path=SwiftDraw-macOSTests.xctest.coverage.txt /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/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| 8| static var test: Bundle { 38| 8| return Bundle(for: Marker.self) 39| 8| } 40| | 41| 0| func url(forResource named: String) throws -> URL { 42| 0| guard let url = self.url(forResource: named, withExtension: nil) else { 43| 0| throw Error.invalid 44| 0| } 45| 0| return url 46| 0| } 47| | 48| | private enum Error: Swift.Error { 49| | case invalid 50| | } 51| | 52| | private class Marker { } 53| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/CGPath+SegmentTests.swift: 1| |// 2| |// CGPath+SegmentTests.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 20/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| |import XCTest 33| |@testable import SwiftDraw 34| | 35| |final class CGPathSegmentTests: XCTestCase { 36| | 37| 1| func testSegments() { 38| 1| let path = CGMutablePath() 39| 1| path.move(to: CGPoint(x: 0, y: 0)) 40| 1| path.addLine(to: CGPoint(x: 0, y: 100)) 41| 1| path.addQuadCurve(to: CGPoint(x: 100, y: 100), 42| 1| control: CGPoint(x: 50, y: 50)) 43| 1| path.addCurve(to: CGPoint(x: 200, y: 100), 44| 1| control1: CGPoint(x: 100, y: 50), 45| 1| control2: CGPoint(x: 200, y: 150)) 46| 1| path.closeSubpath() 47| 1| 48| 1| XCTAssertEqual(path.segments(), 49| 1| [.move(CGPoint(x: 0, y: 0)), 50| 1| .line(CGPoint(x: 0, y: 100)), 51| 1| .quad(CGPoint(x: 50, y: 50), CGPoint(x: 100, y: 100)), 52| 1| .cubic(CGPoint(x: 100, y: 50), CGPoint(x: 200, y: 150), CGPoint(x: 200, y: 100)), 53| 1| .close]) 54| 1| } 55| | 56| 1| func testString() { 57| 1| let font = CTFontCreateWithName("Helvetica" as CFString, 10.0, nil) 58| 1| let path = "_".toPath(font: font)! 59| 1| 60| 1| let segments = path.segments() 61| 1| XCTAssertEqual(segments.count, 5) 62| 1| guard case .move(_) = segments[0] else { XCTFail("expected move"); return } 63| 1| guard case .line(_) = segments[1] else { XCTFail("expected line"); return } 64| 1| guard case .line(_) = segments[2] else { XCTFail("expected line"); return } 65| 1| guard case .line(_) = segments[3] else { XCTFail("expected line"); return } 66| 1| XCTAssertEqual(segments[4], .close) 67| 1| } 68| | 69| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/CGRendererTests.swift: 1| |// 2| |// CGRendererTests.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| |@testable import SwiftDraw 33| |import CoreGraphics 34| |import AppKit 35| |import XCTest 36| | 37| |final class CGRendererTests: XCTestCase { 38| | 39| 1| func testDrawsRect() { 40| 1| let renderer = ImageRenderer(pixelsWide: 2, pixelsHigh: 2) 41| 1| 42| 1| renderer.renderer.setFill(color: .red) 43| 1| renderer.renderer.fill(path: CGPath.rect(), rule: .evenOdd) 44| 1| 45| 1| XCTAssertEqual(renderer.getColor(x: 0, y: 0), .red) 46| 1| XCTAssertEqual(renderer.getColor(x: 1, y: 1), .red) 47| 1| } 48| | 49| 1| func testAlphaClips() { 50| 1| let renderer = ImageRenderer(pixelsWide: 2, pixelsHigh: 2) 51| 1| 52| 1| renderer.renderer.setFill(color: .red) 53| 1| renderer.renderer.fill(path: CGPath.rect(), rule: .evenOdd) 54| 1| 55| 1| XCTAssertEqual(renderer.getColor(x: 0, y: 0), .red) 56| 1| XCTAssertEqual(renderer.getColor(x: 1, y: 1), .red) 57| 1| } 58| |} 59| | 60| |final class ImageRenderer { 61| | 62| | let renderer: CGRenderer 63| | private let bitmap: NSBitmapImageRep 64| | 65| 2| init(pixelsWide: Int, pixelsHigh: Int) { 66| 2| self.bitmap = NSBitmapImageRep(pixelsWide: pixelsWide, 67| 2| pixelsHigh: pixelsHigh) 68| 2| let context = NSGraphicsContext(bitmapImageRep: bitmap)!.cgContext 69| 2| self.renderer = CGRenderer(context: context) 70| 2| } 71| | 72| 4| func getColor(x: Int, y: Int) -> CGColor? { 73| 4| return bitmap.colorAt(x: x, y: y)?.cgColor 74| 4| } 75| |} 76| | 77| |private extension CGPath { 78| | 79| | static func rect(x: CGFloat = 0, 80| | y: CGFloat = 0, 81| | width: CGFloat = 2, 82| 2| height: CGFloat = 2) -> CGPath { 83| 2| 84| 2| let rect = CGRect(x: x, y: y, width: width, height: height) 85| 2| return CGPath(rect: rect, transform: nil) 86| 2| } 87| |} 88| | 89| |private extension CGColor { 90| | 91| 6| static var red: CGColor { 92| 6| //return CGColor(red: 1.0, green: 0, blue: 0, alpha: 1.0) 93| 6| return NSColor(deviceRed: 0.0, green: 0, blue: 1.0, alpha: 1.0).cgColor 94| 6| } 95| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/CommandLine.ArgumentsTests.swift: 1| |// 2| |// CommandLine.ArgumentsTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class CommandLineArgumentsTests: XCTestCase { 36| | 37| 1| func testParseModifiers() throws { 38| 1| let modifiers = try CommandLine.parseModifiers(from: ["--format", "some", "--output", "more", "--scale", "magnify", "--size", "huge"]) 39| 1| XCTAssertEqual(modifiers, [.format: "some", .output: "more", .scale: "magnify", .size: "huge"]) 40| 1| } 41| | 42| 1| func testParseModifiersThrowsForOddPairs() { 43| 1| XCTAssertThrowsError(try CommandLine.parseModifiers(from: ["--format"])) 44| 1| XCTAssertThrowsError(try CommandLine.parseModifiers(from: ["--format", "png", "--output"])) 45| 1| } 46| | 47| 1| func testParseModifiersThrowsForDuplicateModifiers() { 48| 1| XCTAssertThrowsError(try CommandLine.parseModifiers(from: ["--format", "png", "--format", "jpg"])) 49| 1| XCTAssertThrowsError(try CommandLine.parseModifiers(from: ["--format", "png", "--output", "more", "--output", "evenmore"])) 50| 1| } 51| | 52| 1| func testParseModifiersThrowsForUnknownModifiers() { 53| 1| XCTAssertThrowsError(try CommandLine.parseModifiers(from: ["--unknown", "png"])) 54| 1| XCTAssertThrowsError(try CommandLine.parseModifiers(from: ["--format", "png", "--unknown", "more"])) 55| 1| } 56| | 57| 1| func testParseModifiersThrowsForMissingPrefix() { 58| 1| XCTAssertThrowsError(try CommandLine.parseModifiers(from: ["format", "png"])) 59| 1| XCTAssertThrowsError(try CommandLine.parseModifiers(from: ["--format", "png", "output", "more"])) 60| 1| } 61| | 62| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/CommandLine.ConfigurationTests.swift: 1| |// 2| |// CommandLine.ConfigurationTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class CommandLineConfigurationTests: XCTestCase { 36| | 37| 1| func testParseFileURL() throws { 38| 1| let url = try CommandLine.parseFileURL(file: "file", within: URL(directory: "/test")) 39| 1| XCTAssertEqual(url, URL(fileURLWithPath: "/test/file")) 40| 1| 41| 1| let url1 = try CommandLine.parseFileURL(file: "file", within: URL(directory: "/test/subfolder")) 42| 1| XCTAssertEqual(url1, URL(fileURLWithPath: "/test/subfolder/file")) 43| 1| 44| 1| let url2 = try CommandLine.parseFileURL(file: "../file", within: URL(directory: "/test/subfolder")) 45| 1| XCTAssertEqual(url2, URL(fileURLWithPath: "/test/file")) 46| 1| } 47| | 48| 1| func testNewURLForFormat() throws { 49| 1| let svg = URL(fileURLWithPath: "/test/file.svg") 50| 1| XCTAssertEqual(svg.newURL(for: .jpeg, scale: .default), URL(fileURLWithPath: "/test/file.jpg")) 51| 1| XCTAssertEqual(svg.newURL(for: .png, scale: .default), URL(fileURLWithPath: "/test/file.png")) 52| 1| XCTAssertEqual(svg.newURL(for: .pdf, scale: .default), URL(fileURLWithPath: "/test/file.pdf")) 53| 1| 54| 1| let svgExtension = URL(fileURLWithPath: "/test/file") 55| 1| XCTAssertEqual(svgExtension.newURL(for: .jpeg, scale: .default), URL(fileURLWithPath: "/test/file.jpg")) 56| 1| } 57| | 58| 1| func testParseConfiguration() throws { 59| 1| let config = try CommandLine.parseConfiguration(from: ["swiftdraw", "file.svg", "--format", "pdf"], 60| 1| baseDirectory: URL(directory: "/")) 61| 1| 62| 1| XCTAssertEqual(config.input, URL(fileURLWithPath: "/file.svg")) 63| 1| XCTAssertEqual(config.output, URL(fileURLWithPath: "/file.pdf")) 64| 1| XCTAssertEqual(config.scale, .default) 65| 1| XCTAssertEqual(config.format, .pdf) 66| 1| XCTAssertEqual(config.size, .default) 67| 1| } 68| | 69| 1| func testParseConfigurationSize() throws { 70| 1| let config = try CommandLine.parseConfiguration(from: ["swiftdraw", "file.svg", "--format", "png", "--size", "400x300"], 71| 1| baseDirectory: URL(directory: "/")) 72| 1| 73| 1| XCTAssertEqual(config.input, URL(fileURLWithPath: "/file.svg")) 74| 1| XCTAssertEqual(config.output, URL(fileURLWithPath: "/file.png")) 75| 1| XCTAssertEqual(config.format, .png) 76| 1| XCTAssertEqual(config.size, .custom(width: 400, height: 300)) 77| 1| } 78| | 79| 1| func testParseConfigurationScale2x() throws { 80| 1| let config = try CommandLine.parseConfiguration(from: ["swiftdraw", "file.svg", "--format", "png", "--scale", "2x"], 81| 1| baseDirectory: URL(directory: "/")) 82| 1| 83| 1| XCTAssertEqual(config.input, URL(fileURLWithPath: "/file.svg")) 84| 1| XCTAssertEqual(config.output, URL(fileURLWithPath: "/file@2x.png")) 85| 1| XCTAssertEqual(config.scale, .retina) 86| 1| XCTAssertEqual(config.format, .png) 87| 1| } 88| | 89| 1| func testParseConfigurationThrows() { 90| 1| XCTAssertThrowsError(try CommandLine.parseConfiguration(from: [], 91| 1| baseDirectory: URL(directory: "/"))) 92| 1| XCTAssertThrowsError(try CommandLine.parseConfiguration(from: ["swiftdraw", "file.svg"], 93| 1| baseDirectory: URL(directory: "/"))) 94| 1| XCTAssertThrowsError(try CommandLine.parseConfiguration(from: ["swiftdraw", "file.svg", "--format", "unknown"], 95| 1| baseDirectory: URL(directory: "/"))) 96| 1| 97| 1| } 98| |} 99| | 100| |private extension URL { 101| | 102| 9| init(directory: String) { 103| 9| self.init(fileURLWithPath: directory, isDirectory: true) 104| 9| } 105| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/CoordinateTests.swift: 1| |// 2| |// CoordinateTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class CoordinateTests: XCTestCase { 36| | 37| 1| func testPrecisionMax() { 38| 1| var f = XMLFormatter.CoordinateFormatter() 39| 1| f.precision = .maximum 40| 1| 41| 1| XCTAssertEqual(f.format(1.0), "1.0") 42| 1| XCTAssertEqual(f.format(1.01), "1.01") 43| 1| XCTAssertEqual(f.format(1.001), "1.001") 44| 1| XCTAssertEqual(f.format(1.0001), "1.0001") 45| 1| XCTAssertEqual(f.format(1.00001), "1.00001") 46| 1| XCTAssertEqual(f.format(1.000001), "1.000001") 47| 1| XCTAssertEqual(f.format(1.0000001), "1.0000001") 48| 1| XCTAssertEqual(f.format(1e-20), "1e-20") 49| 1| XCTAssertEqual(f.format(12e-20), "1.2e-19") 50| 1| } 51| | 52| 1| func testPrecisionCapped() { 53| 1| 54| 1| var f = XMLFormatter.CoordinateFormatter() 55| 1| f.precision = .capped(max: 4) 56| 1| 57| 1| XCTAssertEqual(f.format(1.0), "1") 58| 1| XCTAssertEqual(f.format(1.01), "1.01") 59| 1| XCTAssertEqual(f.format(1.001), "1.001") 60| 1| XCTAssertEqual(f.format(1.0001), "1.0001") 61| 1| XCTAssertEqual(f.format(1.00001), "1") 62| 1| XCTAssertEqual(f.format(1.000001), "1") 63| 1| XCTAssertEqual(f.format(1.0000001), "1") 64| 1| XCTAssertEqual(f.format(1e-20), "0") 65| 1| XCTAssertEqual(f.format(12e-20), "0") 66| 1| } 67| | 68| 1| func testDelimeterSpace() { 69| 1| var f = XMLFormatter.CoordinateFormatter() 70| 1| f.delimeter = .space 71| 1| 72| 1| XCTAssertEqual(f.format(2.05), "2.05") 73| 1| XCTAssertEqual(f.format(2.05, 4.5), "2.05 4.5") 74| 1| XCTAssertEqual(f.format(2.05, 4.5, 10, 20), "2.05 4.5 10 20") 75| 1| } 76| | 77| 1| func testDelimeterComma() { 78| 1| var f = XMLFormatter.CoordinateFormatter() 79| 1| f.delimeter = .comma 80| 1| 81| 1| XCTAssertEqual(f.format(2.05), "2.05") 82| 1| XCTAssertEqual(f.format(2.05, 4.5), "2.05,4.5") 83| 1| XCTAssertEqual(f.format(2.05, 4.5, 10, 20), "2.05,4.5,10,20") 84| 1| } 85| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/DOM+Extensions.swift: 1| |// 2| |// DOM.Element.Equality.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| |@testable import SwiftDraw 34| | 35| |extension DOM { 36| | 37| 6| static func createLine() -> DOM.Line { 38| 6| return DOM.Line(x1: 0, y1: 1, x2: 3, y2: 4) 39| 6| } 40| | 41| 7| static func createCircle() -> DOM.Circle { 42| 7| return DOM.Circle(cx: 0, cy: 1, r: 2) 43| 7| } 44| | 45| 6| static func createEllipse() -> DOM.Ellipse { 46| 6| return DOM.Ellipse(cx: 0, cy: 1, rx: 2, ry: 3) 47| 6| } 48| | 49| 6| static func createRect() -> DOM.Rect { 50| 6| return DOM.Rect(x: 0, y: 1, width: 2, height: 3) 51| 6| } 52| | 53| 6| static func createPolygon() -> DOM.Polygon { 54| 6| return DOM.Polygon(0, 1, 2, 3, 4, 5) 55| 6| } 56| | 57| 3| static func createPolyline() -> DOM.Polyline { 58| 3| return DOM.Polyline(0, 1, 2, 3, 4, 5) 59| 3| } 60| | 61| 3| static func createText() -> DOM.Text { 62| 3| return DOM.Text(y: 1, value: "The quick brown fox") 63| 3| } 64| | 65| 3| static func createPath() -> DOM.Path { 66| 3| let path = DOM.Path(x: 0, y: 1) 67| 3| path.segments.append(.move(x: 10, y: 10, space: .absolute)) 68| 3| path.segments.append(.horizontal(x: 10, space: .absolute)) 69| 3| return path 70| 3| } 71| | 72| 3| static func createGroup() -> DOM.Group { 73| 3| let group = DOM.Group() 74| 3| group.childElements.append(createLine()) 75| 3| group.childElements.append(createPolygon()) 76| 3| group.childElements.append(createCircle()) 77| 3| group.childElements.append(createPath()) 78| 3| group.childElements.append(createRect()) 79| 3| group.childElements.append(createEllipse()) 80| 3| return group 81| 3| } 82| |} 83| | 84| |// Equatable just for tests 85| | 86| |extension DOM.GraphicsElement: Equatable { 87| 44| public static func ==(lhs: DOM.GraphicsElement, rhs: DOM.GraphicsElement) -> Bool { 88| 88| let toString: (Any) -> String = { var text = ""; dump($0, to: &text); return text } 89| 44| return toString(lhs) == toString(rhs) 90| 44| } 91| |} 92| | 93| |extension DOM.Polyline { 94| | // requires even number of elements 95| 5| convenience init(_ p: DOM.Coordinate...) { 96| 5| 97| 5| var points = [DOM.Point]() 98| 5| 99| 17| for index in stride(from: 0, to: p.count, by: 2) { 100| 17| points.append(DOM.Point(p[index], p[index + 1])) 101| 17| } 102| 5| 103| 5| self.init(points: points) 104| 5| } 105| |} 106| | 107| |extension DOM.Polygon { 108| | // requires even number of elements 109| 8| convenience init(_ p: DOM.Coordinate...) { 110| 8| 111| 8| var points = [DOM.Point]() 112| 8| 113| 26| for index in stride(from: 0, to: p.count, by: 2) { 114| 26| points.append(DOM.Point(p[index], p[index + 1])) 115| 26| } 116| 8| 117| 8| self.init(points: points) 118| 8| } 119| |} 120| | 121| |extension XML.Element { 122| 3| convenience init(_ name: String, style: String) { 123| 3| self.init(name: name, attributes: ["style": style]) 124| 3| } 125| | 126| 3| convenience init(_ name: String, id: String, style: String) { 127| 3| self.init(name: name, attributes: ["id": id, "style": style]) 128| 3| } 129| |} 130| | 131| | 132| |extension DOM.SVG { 133| | 134| 2| static func parse(fileNamed name: String, in bundle: Bundle = .test) throws -> DOM.SVG { 135| 2| guard let url = bundle.url(forResource: name, withExtension: nil) else { 136| 0| throw Error.missing 137| 2| } 138| 2| 139| 2| let parser = XMLParser(options: [.skipInvalidElements]) 140| 2| let element = try XML.SAXParser.parse(contentsOf: url) 141| 2| return try parser.parseSVG(element) 142| 2| } 143| | 144| | enum Error: Swift.Error { 145| | case missing 146| | } 147| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/DOM.ElementTests.swift: 1| |// 2| |// DOM.ElementTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class DOMElementTests: XCTestCase { 36| | 37| 1| func testLine() { 38| 1| let element = DOM.createLine() 39| 1| var another = DOM.createLine() 40| 1| 41| 1| XCTAssertEqual(element, another) 42| 1| 43| 1| another.x1 = 1 44| 1| XCTAssertNotEqual(element, another) 45| 1| 46| 1| another = DOM.createLine() 47| 1| another.fill = .color(.keyword(.black)) 48| 1| XCTAssertNotEqual(element, another) 49| 1| 50| 1| another.fill = nil 51| 1| XCTAssertEqual(element, another) 52| 1| } 53| | 54| 1| func testCircle() { 55| 1| let element = DOM.createCircle() 56| 1| var another = DOM.createCircle() 57| 1| 58| 1| XCTAssertEqual(element, another) 59| 1| 60| 1| another.cx = 1 61| 1| XCTAssertNotEqual(element, another) 62| 1| 63| 1| another = DOM.createCircle() 64| 1| another.fill = .color(.keyword(.black)) 65| 1| XCTAssertNotEqual(element, another) 66| 1| 67| 1| another.fill = nil 68| 1| XCTAssertEqual(element, another) 69| 1| } 70| | 71| 1| func testEllipse() { 72| 1| let element = DOM.createEllipse() 73| 1| var another = DOM.createEllipse() 74| 1| 75| 1| XCTAssertEqual(element, another) 76| 1| 77| 1| another.cx = 1 78| 1| XCTAssertNotEqual(element, another) 79| 1| 80| 1| another = DOM.createEllipse() 81| 1| another.fill = .color(.keyword(.black)) 82| 1| XCTAssertNotEqual(element, another) 83| 1| 84| 1| another.fill = nil 85| 1| XCTAssertEqual(element, another) 86| 1| } 87| | 88| 1| func testRect() { 89| 1| let element = DOM.createRect() 90| 1| var another = DOM.createRect() 91| 1| 92| 1| XCTAssertEqual(element, another) 93| 1| 94| 1| another.x = 1 95| 1| XCTAssertNotEqual(element, another) 96| 1| 97| 1| another = DOM.createRect() 98| 1| another.fill = .color(.keyword(.black)) 99| 1| XCTAssertNotEqual(element, another) 100| 1| 101| 1| another.fill = nil 102| 1| XCTAssertEqual(element, another) 103| 1| } 104| | 105| 1| func testPolygon() { 106| 1| let element = DOM.createPolygon() 107| 1| var another = DOM.createPolygon() 108| 1| 109| 1| XCTAssertEqual(element, another) 110| 1| 111| 1| another.points.append(DOM.Point(6, 7)) 112| 1| XCTAssertNotEqual(element, another) 113| 1| 114| 1| another = DOM.createPolygon() 115| 1| another.fill = .color(.keyword(.black)) 116| 1| XCTAssertNotEqual(element, another) 117| 1| 118| 1| another.fill = nil 119| 1| XCTAssertEqual(element, another) 120| 1| } 121| | 122| 1| func testPolyline() { 123| 1| let element = DOM.createPolyline() 124| 1| var another = DOM.createPolyline() 125| 1| 126| 1| XCTAssertEqual(element, another) 127| 1| 128| 1| another.points.append(DOM.Point(6, 7)) 129| 1| XCTAssertNotEqual(element, another) 130| 1| 131| 1| another = DOM.createPolyline() 132| 1| another.fill = .color(.keyword(.black)) 133| 1| XCTAssertNotEqual(element, another) 134| 1| 135| 1| another.fill = nil 136| 1| XCTAssertEqual(element, another) 137| 1| } 138| | 139| 1| func testText() { 140| 1| let element = DOM.createText() 141| 1| var another = DOM.createText() 142| 1| 143| 1| XCTAssertEqual(element, another) 144| 1| 145| 1| another.value = "Simon" 146| 1| XCTAssertNotEqual(element, another) 147| 1| 148| 1| another = DOM.createText() 149| 1| another.fill = .color(.keyword(.black)) 150| 1| XCTAssertNotEqual(element, another) 151| 1| 152| 1| another.fill = nil 153| 1| XCTAssertEqual(element, another) 154| 1| } 155| | 156| 1| func testGroup() { 157| 1| let group = DOM.createGroup() 158| 1| var another = DOM.createGroup() 159| 1| 160| 1| XCTAssertEqual(group, another) 161| 1| 162| 1| another.childElements.append(DOM.createCircle()) 163| 1| XCTAssertNotEqual(group, another) 164| 1| 165| 1| another = DOM.createGroup() 166| 1| another.fill = .color(.keyword(.black)) 167| 1| XCTAssertNotEqual(group, another) 168| 1| 169| 1| another.fill = nil 170| 1| XCTAssertEqual(group, another) 171| 1| } 172| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/GradientTests.swift: 1| |// 2| |// GradientTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class GradientTests: XCTestCase { 36| | 37| 1| func testLinerGradient() { 38| 1| let node = XML.Element(name: "linearGradient", attributes: ["id": "blue"]) 39| 1| 40| 1| node.children.append(XML.Element(name: "stop", attributes: ["offset": "0%", "stop-color": "black"])) 41| 1| node.children.append(XML.Element(name: "stop", attributes: ["offset": "25%", "stop-color": "red"])) 42| 1| node.children.append(XML.Element(name: "stop", attributes: ["offset": "50%", "stop-color": "black"])) 43| 1| node.children.append(XML.Element(name: "stop", attributes: ["offset": "100%", "stop-color": "red"])) 44| 1| 45| 1| let expected = DOM.LinearGradient(id: "blue") 46| 1| expected.stops.append(DOM.LinearGradient.Stop(offset: 0, color: .keyword(.black))) 47| 1| expected.stops.append(DOM.LinearGradient.Stop(offset: 0.25, color: .keyword(.red))) 48| 1| expected.stops.append(DOM.LinearGradient.Stop(offset: 0.5, color: .keyword(.black))) 49| 1| expected.stops.append(DOM.LinearGradient.Stop(offset: 1, color: .keyword(.red))) 50| 1| 51| 1| let parsed = try? XMLParser().parseLinearGradient(node) 52| 1| XCTAssertEqual(expected, parsed) 53| 1| XCTAssertEqual(expected.stops.count, parsed?.stops.count) 54| 1| } 55| | 56| 1| func testLinerGradientStop() { 57| 1| 58| 1| var node = ["offset": "25.5%", "stop-color": "black"] 59| 1| 60| 1| var parsed = try? XMLParser().parseLinearGradientStop(node) 61| 1| XCTAssertEqual(parsed?.offset, 0.255) 62| 1| XCTAssertEqual(parsed?.color, .keyword(.black)) 63| 1| XCTAssertEqual(parsed?.opacity, 1.0) 64| 1| 65| 1| node["stop-opacity"] = "99%" 66| 1| parsed = try? XMLParser().parseLinearGradientStop(node) 67| 1| XCTAssertEqual(parsed?.opacity, 0.99) 68| 1| 69| 1| // test required properties 70| 1| node = [:] 71| 1| node["offset"] = "10%" 72| 1| XCTAssertThrowsError(try XMLParser().parseLinearGradientStop(node)) 73| 1| node["stop-color"] = "black" 74| 1| XCTAssertNotNil(try XMLParser().parseLinearGradientStop(node)) 75| 1| } 76| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/ImageTests.swift: 1| |// 2| |// ScannerTests.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| |import XCTest 33| |@testable import SwiftDraw 34| | 35| |final class ImageTests: XCTestCase { 36| | 37| | #if SWIFT_PACKAGE 38| | #else 39| 1| func testValidSVGLoads() { 40| 1| XCTAssertNotNil(Image(named: "lines.svg", in: .test)) 41| 1| } 42| | #endif 43| | 44| 1| func testInvalidSVGReturnsNil() { 45| 1| XCTAssertNil(Image(named: "invalid.svg", in: .test)) 46| 1| } 47| | 48| 1| func testMissingSVGReturnsNil() { 49| 1| XCTAssertNil(Image(named: "missing.svg", in: .test)) 50| 1| } 51| | 52| 1| func testImageRasterizes() { 53| 1| let image = Image.makeLines() 54| 1| let rendered = image.rasterize() 55| 1| XCTAssertEqual(rendered.size, image.size) 56| 1| XCTAssertNotNil(image.pngData()) 57| 1| XCTAssertNotNil(image.jpegData()) 58| 1| XCTAssertNotNil(image.pdfData()) 59| 1| } 60| | 61| 1| func testImageRasterizeAndScales() { 62| 1| let image = Image.makeLines() 63| 1| let doubleSize = CGSize(width: 200, height: 200) 64| 1| let rendered = image.rasterize(with: doubleSize) 65| 1| XCTAssertEqual(rendered.size, doubleSize) 66| 1| XCTAssertNotNil(image.pngData(size: doubleSize)) 67| 1| XCTAssertNotNil(image.jpegData(size: doubleSize)) 68| 1| } 69| | 70| |} 71| | 72| |private extension Image { 73| | 74| 2| static func makeLines() -> Image { 75| 2| let svg = DOM.SVG(width: 100, height: 100) 76| 2| svg.childElements.append(DOM.Line(x1: 0, y1: 0, x2: 100, y2: 100)) 77| 2| svg.childElements.append(DOM.Line(x1: 100, y1: 0, x2: 0, y2: 100)) 78| 2| return Image(svg: svg) 79| 2| } 80| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/LayerTree.Builder.LayerTests.swift: 1| |// 2| |// LayerTree.Builder.LayerTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class LayerTreeBuilderLayerTests: XCTestCase { 36| | 37| 1| func testMakeTextContentsFromDOM() { 38| 1| let text = DOM.Text(value: "Hello") 39| 1| let contents = LayerTree.Builder.makeTextContents(from: text, with: .init()) 40| 1| 41| 1| guard case .text(let t, _, _) = contents else { XCTFail(); return } 42| 1| XCTAssertEqual(t, "Hello") 43| 1| } 44| | 45| 1| func testMakeImageContentsFromDOM() throws { 46| 1| let image = DOM.Image(href: URL(maybeData: "data:image/png;base64,f00d")!, 47| 1| width: 50, 48| 1| height: 50) 49| 1| 50| 1| let contents = try LayerTree.Builder.makeImageContents(from: image) 51| 1| XCTAssertEqual(contents, .image(.png(data: Data(base64Encoded: "f00d")!))) 52| 1| 53| 1| let invalid = DOM.Image(href: URL(string: "aa")!, width: 10, height: 20) 54| 1| XCTAssertThrowsError(try LayerTree.Builder.makeImageContents(from: invalid)) 55| 1| } 56| | 57| 1| func testMakeUseContentsThrows() { 58| 1| let builder = LayerTree.Builder(svg: DOM.SVG(width: 10, height: 10)) 59| 1| let use = DOM.Use(href: URL(string: "#circle")!) 60| 1| XCTAssertThrowsError(try builder.makeUseLayerContents(from: use, with: .init())) 61| 1| } 62| | 63| 1| func testMakeUseContentsFromDOM() throws { 64| 1| let circle = DOM.Circle(cx: 5, cy: 5, r: 5) 65| 1| let svg = DOM.SVG(width: 10, height: 10) 66| 1| svg.defs.elements["circle"] = circle 67| 1| let builder = LayerTree.Builder(svg: svg) 68| 1| 69| 1| let use = DOM.Use(href: URL(string: "#circle")!) 70| 1| var contents = try builder.makeUseLayerContents(from: use, with: .init()) 71| 1| guard case .layer(let l) = contents else { XCTFail(); return } 72| 1| XCTAssertEqual(l.contents.count, 1) 73| 1| XCTAssertEqual(l.transform, []) 74| 1| 75| 1| use.x = 10 76| 1| contents = try builder.makeUseLayerContents(from: use, with: .init()) 77| 1| guard case .layer(let l1) = contents else { XCTFail(); return } 78| 1| XCTAssertEqual(l1.contents.count, 1) 79| 1| XCTAssertEqual(l1.transform, [.translate(tx: 10, ty: 0)]) 80| 1| 81| 1| use.x = nil 82| 1| use.y = 20 83| 1| contents = try builder.makeUseLayerContents(from: use, with: .init()) 84| 1| guard case .layer(let l2) = contents else { XCTFail(); return } 85| 1| XCTAssertEqual(l2.contents.count, 1) 86| 1| XCTAssertEqual(l2.transform, [.translate(tx: 0, ty: 20)]) 87| 1| } 88| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class LayerTreeBuilderShapeTests: XCTestCase { 36| | 37| 1| func testDOMRectMakesRectWithDefaultOrigin() { 38| 1| let dom = DOM.Rect(width: 10, height: 20) 39| 1| let rect = LayerTree.Builder.makeRect(from: dom) 40| 1| XCTAssertEqual(rect, LayerTree.Rect(x: 0, y: 0, width: 10, height: 20)) 41| 1| } 42| | 43| 1| func testDOMRectMakesRect() { 44| 1| let dom = DOM.Rect(x: 10, y: 20, width: 30, height: 40) 45| 1| let rect = LayerTree.Builder.makeRect(from: dom) 46| 1| XCTAssertEqual(rect, LayerTree.Rect(x: 10, y: 20, width: 30, height: 40)) 47| 1| } 48| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/LayerTree.BuilderTests.swift: 1| |// 2| |// LayerTree.BuilderTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class LayerTreeBuilderTests: XCTestCase { 36| | 37| | typealias Shape = LayerTree.Shape 38| | typealias Contents = LayerTree.Layer.Contents 39| | 40| 1| func testMakeViewBoxTransform() { 41| 1| var transform = LayerTree.Builder.makeTransform(for: nil, width: 100, height: 200) 42| 1| XCTAssertEqual(transform, []) 43| 1| 44| 1| let viewbox = DOM.SVG.ViewBox(x: 0, y: 0, width: 200, height: 200) 45| 1| transform = LayerTree.Builder.makeTransform(for: viewbox, width: 100, height: 100) 46| 1| XCTAssertEqual(transform, [.scale(sx: 0.5, sy: 0.5)]) 47| 1| 48| 1| let viewbox1 = DOM.SVG.ViewBox(x: 10, y: -10, width: 100, height: 100) 49| 1| transform = LayerTree.Builder.makeTransform(for: viewbox1, width: 100, height: 100) 50| 1| XCTAssertEqual(transform, [.translate(tx: -10, ty: 10)]) 51| 1| } 52| | 53| 1| func testDOMMaskMakesLayer() { 54| 1| let circle = DOM.Circle(cx: 5, cy: 5, r: 5) 55| 1| let line = DOM.Line(x1: 0, y1: 0, x2: 10, y2: 0) 56| 1| let svg = DOM.SVG(width: 10, height: 10) 57| 1| svg.defs.masks.append(DOM.Mask(id: "mask1", childElements: [circle, line])) 58| 1| 59| 1| let builder = LayerTree.Builder(svg: svg) 60| 1| 61| 1| let element = DOM.GraphicsElement() 62| 1| element.mask = URL(string: "#mask1") 63| 1| 64| 1| let layer = builder.createMaskLayer(for: element) 65| 1| 66| 1| XCTAssertEqual(layer?.contents.count, 2) 67| 1| } 68| | 69| 1| func testDOMClipMakesShape() { 70| 1| let circle = DOM.Circle(cx: 5, cy: 5, r: 5) 71| 1| let svg = DOM.SVG(width: 10, height: 10) 72| 1| svg.defs.clipPaths.append(DOM.ClipPath(id: "clip1", childElements: [circle])) 73| 1| let builder = LayerTree.Builder(svg: svg) 74| 1| 75| 1| let element = DOM.GraphicsElement() 76| 1| element.clipPath = URL(string: "#clip1") 77| 1| 78| 1| let shapes = builder.createClipShapes(for: element) 79| 1| XCTAssertEqual(shapes, [.ellipse(within: LayerTree.Rect(x: 0, y: 0, width: 10, height: 10))]) 80| 1| } 81| | 82| 1| func testDOMGroupMakesChildContents() { 83| 1| let builder = LayerTree.Builder(svg: DOM.SVG(width: 10, height: 10)) 84| 1| 85| 1| let group = DOM.Group() 86| 1| group.childElements = [DOM.Circle(cx: 0, cy: 0, r: 5), 87| 1| DOM.Line(x1: 0, y1: 0, x2: 10, y2: 10)] 88| 1| 89| 1| let layer = builder.makeLayer(from: group, inheriting: .init()) 90| 1| XCTAssertEqual(layer.contents.count, 2) 91| 1| } 92| | 93| 1| func testDOMPatternMakesPattern() { 94| 1| let builder = LayerTree.Builder(svg: DOM.SVG(width: 10, height: 10)) 95| 1| 96| 1| var element = DOM.Pattern(id: "hi", width: 5, height: 5) 97| 1| element.childElements = [DOM.Circle(cx: 10, cy: 10, r: 5)] 98| 1| 99| 1| let pattern = builder.makePattern(for: element) 100| 1| 101| 1| let ellipse = Shape.ellipse(within: LayerTree.Rect(x: 5, y: 5, width: 10, height: 10)) 102| 1| let expected = LayerTree.Layer() 103| 1| expected.contents = [Contents.shape(ellipse, .default, .default)] 104| 1| XCTAssertEqual(pattern.contents, [.layer(expected)]) 105| 1| } 106| | 107| 1| func testStrokeAttributes() { 108| 1| var state = LayerTree.Builder.State() 109| 1| state.stroke = .rgbf(1.0, 0.0, 0.0) 110| 1| state.strokeOpacity = 0.5 111| 1| state.strokeWidth = 5.0 112| 1| state.strokeLineCap = .square 113| 1| state.strokeLineJoin = .round 114| 1| state.strokeLineMiterLimit = 10.0 115| 1| 116| 1| let att = LayerTree.Builder.makeStrokeAttributes(with: state) 117| 1| XCTAssertEqual(att.color, .rgba(r: 1.0, g: 0, b: 0, a: 0.5)) 118| 1| XCTAssertEqual(att.width, 5.0) 119| 1| XCTAssertEqual(att.cap, .square) 120| 1| XCTAssertEqual(att.join, .round) 121| 1| XCTAssertEqual(att.miterLimit, 10.0) 122| 1| 123| 1| state.strokeWidth = 0 124| 1| let att2 = LayerTree.Builder.makeStrokeAttributes(with: state) 125| 1| XCTAssertEqual(att2.color, .none) 126| 1| } 127| |} 128| | 129| |private extension LayerTree.StrokeAttributes { 130| | 131| 1| static var `default`: LayerTree.StrokeAttributes { 132| 1| return LayerTree.Builder.makeStrokeAttributes(with: LayerTree.Builder.State()) 133| 1| } 134| |} 135| | 136| |private extension LayerTree.FillAttributes { 137| | 138| 1| static var `default`: LayerTree.FillAttributes { 139| 1| let builder = LayerTree.Builder(svg: DOM.SVG(width: 10, height: 10)) 140| 1| return builder.makeFillAttributes(with: LayerTree.Builder.State()) 141| 1| } 142| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/LayerTree.ColorTests.swift: 1| |// 2| |// ColorTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class LayerTreeColorTests: XCTestCase { 36| | 37| | typealias Color = LayerTree.Color 38| | 39| 6| let noColor = Color.none 40| 6| let someColor = Color.rgba(r: 0.1, g: 0.2, b: 0.3, a: 0.4) 41| 6| let anotherColor = Color.rgba(r: 0.4, g: 0.3, b: 0.2, a: 0.1) 42| | 43| 1| func testStaticColors() { 44| 1| XCTAssertEqual(Color.black, .rgba(r: 0, g: 0, b: 0, a: 1.0)) 45| 1| XCTAssertEqual(Color.white, .rgba(r: 1, g: 1, b: 1, a: 1.0)) 46| 1| } 47| | 48| 1| func testWithAlpha() { 49| 1| //test alpha can be easily adjusted on rgba values 50| 1| //apha 0.0 == .none 51| 1| 52| 1| //.none color cannot change alpha 53| 1| XCTAssertEqual(noColor.withAlpha(1.0).maybeNone(), .none) 54| 1| XCTAssertEqual(noColor.withAlpha(0.5).maybeNone(), .none) 55| 1| XCTAssertEqual(noColor.withAlpha(0.3).maybeNone(), .none) 56| 1| XCTAssertEqual(noColor.withAlpha(0.0).maybeNone(), .none) 57| 1| 58| 1| XCTAssertEqual(someColor.withAlpha(1.0).maybeNone(), .rgba(r: 0.1, g: 0.2, b: 0.3, a: 1.0)) 59| 1| XCTAssertEqual(someColor.withAlpha(0.5).maybeNone(), .rgba(r: 0.1, g: 0.2, b: 0.3, a: 0.5)) 60| 1| XCTAssertEqual(someColor.withAlpha(0.3).maybeNone(), .rgba(r: 0.1, g: 0.2, b: 0.3, a: 0.3)) 61| 1| XCTAssertEqual(someColor.withAlpha(0.0).maybeNone(), .none) 62| 1| 63| 1| XCTAssertEqual(anotherColor.withAlpha(1.0).maybeNone(), .rgba(r: 0.4, g: 0.3, b: 0.2, a: 1.0)) 64| 1| XCTAssertEqual(anotherColor.withAlpha(0.5).maybeNone(), .rgba(r: 0.4, g: 0.3, b: 0.2, a: 0.5)) 65| 1| XCTAssertEqual(anotherColor.withAlpha(0.3).maybeNone(), .rgba(r: 0.4, g: 0.3, b: 0.2, a: 0.3)) 66| 1| XCTAssertEqual(anotherColor.withAlpha(0.0).maybeNone(), .none) 67| 1| } 68| | 69| 1| func testMultiplyingAlpha() { 70| 1| //test alpha can be easily multiplied on rgba values 71| 1| //apha 0.0 == .none 72| 1| 73| 1| XCTAssertEqual(noColor.withMultiplyingAlpha(1.0), .none) 74| 1| XCTAssertEqual(noColor.withMultiplyingAlpha(0.5), .none) 75| 1| XCTAssertEqual(noColor.withMultiplyingAlpha(0.3), .none) 76| 1| XCTAssertEqual(noColor.withMultiplyingAlpha(0.0), .none) 77| 1| 78| 1| XCTAssertEqual(someColor.withMultiplyingAlpha(1.0), .rgba(r: 0.1, g: 0.2, b: 0.3, a: 0.4)) 79| 1| XCTAssertEqual(someColor.withMultiplyingAlpha(0.5), .rgba(r: 0.1, g: 0.2, b: 0.3, a: 0.2)) 80| 1| XCTAssertEqual(someColor.withMultiplyingAlpha(0.0).maybeNone(), .none) 81| 1| 82| 1| XCTAssertEqual(anotherColor.withMultiplyingAlpha(1.0), .rgba(r: 0.4, g: 0.3, b: 0.2, a: 0.1)) 83| 1| XCTAssertEqual(anotherColor.withMultiplyingAlpha(0.5), .rgba(r: 0.4, g: 0.3, b: 0.2, a: 0.05)) 84| 1| XCTAssertEqual(anotherColor.withMultiplyingAlpha(0.0).maybeNone(), .none) 85| 1| } 86| | 87| 1| func testRGBi() { 88| 1| //a color can be created from (UInt8, UInt8, UInt8) 89| 1| 90| 1| XCTAssertEqual(Color(UInt8(102), UInt8(102), UInt8(102)), .rgba(r: 0.4, g: 0.4, b: 0.4, a: 1.0)) 91| 1| XCTAssertEqual(Color(UInt8(102), UInt8(0), UInt8(102)), .rgba(r: 0.4, g: 0.0, b: 0.4, a: 1.0)) 92| 1| XCTAssertEqual(Color(UInt8(102), UInt8(102), UInt8(0)), .rgba(r: 0.4, g: 0.4, b: 0.0, a: 1.0)) 93| 1| 94| 1| XCTAssertEqual(Color(UInt8(204), UInt8(204), UInt8(204)), .rgba(r: 0.8, g: 0.8, b: 0.8, a: 1.0)) 95| 1| XCTAssertEqual(Color(UInt8(204), UInt8(0), UInt8(204)), .rgba(r: 0.8, g: 0.0, b: 0.8, a: 1.0)) 96| 1| XCTAssertEqual(Color(UInt8(204), UInt8(204), UInt8(0)), .rgba(r: 0.8, g: 0.8, b: 0.0, a: 1.0)) 97| 1| } 98| | 99| 1| func testLuminanceConverter() { 100| 1| // svg masks are constructed from 100% black with alpha from the RGB luminance value 101| 1| 102| 1| let white = Color.white 103| 1| let black = Color.black 104| 1| let red = Color.rgba(r: 1.0, g: 0.0, b: 0.0, a: 1.0) 105| 1| let green = Color.rgba(r: 0.0, g: 1.0, b: 0.0, a: 1.0) 106| 1| let blue = Color.rgba(r: 0.0, g: 0.0, b: 1.0, a: 1.0) 107| 1| 108| 1| let converter = LuminanceColorConverter() 109| 1| XCTAssertEqual(converter.createColor(from: white), .gray(white: 0.0, a: 1.0)) 110| 1| XCTAssertEqual(converter.createColor(from: red), .gray(white: 0.0, a: 0.2126)) 111| 1| XCTAssertEqual(converter.createColor(from: green), .gray(white: 0.0, a: 0.7152)) 112| 1| XCTAssertEqual(converter.createColor(from: blue), .gray(white: 0.0, a: 0.0722)) 113| 1| XCTAssertEqual(converter.createColor(from: black), .gray(white: 0.0, a: 0.0)) 114| 1| } 115| | 116| 1| func testFromDOM() { 117| 1| // DOM.Color converts correctly 118| 1| 119| 1| let none = DOM.Color.none 120| 1| let black = DOM.Color.keyword(.black) 121| 1| let white = DOM.Color.keyword(.white) 122| 1| let red = DOM.Color.rgbi(255, 0, 0) 123| 1| let green = DOM.Color.rgbi(0, 255, 0) 124| 1| let blue = DOM.Color.rgbi(0, 0, 255) 125| 1| 126| 1| XCTAssertEqual(Color(none), .none) 127| 1| XCTAssertEqual(Color(black), .rgba(r: 0.0, g: 0.0, b: 0.0, a: 1.0)) 128| 1| XCTAssertEqual(Color(white), .rgba(r: 1.0, g: 1.0, b: 1.0, a: 1.0)) 129| 1| XCTAssertEqual(Color(red), .rgba(r: 1.0, g: 0.0, b: 0.0, a: 1.0)) 130| 1| XCTAssertEqual(Color(green), .rgba(r: 0.0, g: 1.0, b: 0.0, a: 1.0)) 131| 1| XCTAssertEqual(Color(blue), .rgba(r: 0.0, g: 0.0, b: 1.0, a: 1.0)) 132| 1| } 133| | 134| |} 135| | /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/LayerTree.CommandGeneratorTests.swift: 1| |// 2| |// LayerTree.CommandGeneratorTests.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 13/12/18. 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 LayerTreeCommandGeneratorTests: XCTestCase { 33| | 34| 1| func testClip() { 35| 1| let generator = LayerTree.CommandGenerator(provider: LayerTreeProvider(), size: .zero) 36| 1| let circle = LayerTree.Shape.ellipse(within: .init(x: 0, y: 0, width: 10, height: 10)) 37| 1| let rect = LayerTree.Shape.rect(within: .init(x: 20, y: 0, width: 10, height: 10), radii: .zero) 38| 1| 39| 1| let commands = generator.renderCommands(forClip: [circle, rect]) 40| 1| XCTAssertEqual(commands.count, 1) 41| 1| 42| 1| if case .setClip(path: let path) = commands[0] { 43| 1| XCTAssertEqual(path, [circle, rect]) 44| 1| } else { 45| 0| XCTFail("expected clip command") 46| 1| } 47| 1| } 48| | 49| 1| func testTransforms() { 50| 1| let matrix = LayerTree.Transform.matrix(.init(a: 10, b: 20, c: 30, d: 40, tx: 50, ty: 60)) 51| 1| let scale = LayerTree.Transform.scale(sx: 10, sy: 20) 52| 1| let translate = LayerTree.Transform.translate(tx: 10, ty: 20) 53| 1| let rotate = LayerTree.Transform.rotate(radians: 10) 54| 1| 55| 1| let generator = LayerTree.CommandGenerator(provider: LayerTreeProvider(), size: .zero) 56| 1| let commands = generator.renderCommands(forTransforms: [matrix, scale, translate, rotate]) 57| 1| XCTAssertEqual(commands.count, 4) 58| 1| } 59| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/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| 2| let someData = Data(base64Encoded: "8badf00d")! 35| 2| let moreData = Data(base64Encoded: "f00d")! 36| | 37| 1| func testInit() { 38| 1| let i1 = LayerTree.Image(mimeType: "image/png", data: someData) 39| 1| let i2 = LayerTree.Image(mimeType: "image/jpg", data: moreData) 40| 1| let i3 = LayerTree.Image(mimeType: "image/jpeg", data: someData) 41| 1| 42| 1| XCTAssertEqual(i1, .png(data: someData)) 43| 1| XCTAssertEqual(i2, .jpeg(data: moreData)) 44| 1| XCTAssertEqual(i3, .jpeg(data: someData)) 45| 1| 46| 1| XCTAssertNil(LayerTree.Image(mimeType: "image/jpg", data: Data())) 47| 1| XCTAssertNil(LayerTree.Image(mimeType: "image", data: someData)) 48| 1| } 49| | 50| 1| func testImageEquality() { 51| 1| let i1 = LayerTree.Image(mimeType: "image/jpeg", data: someData) 52| 1| let i2 = LayerTree.Image(mimeType: "image/jpg", data: someData) 53| 1| let i3 = LayerTree.Image(mimeType: "image/png", data: someData) 54| 1| 55| 1| XCTAssertEqual(i1, .jpeg(data: someData)) 56| 1| XCTAssertEqual(i1, i1) 57| 1| XCTAssertEqual(i1, i2) 58| 1| 59| 1| XCTAssertEqual(i3, .png(data: someData)) 60| 1| XCTAssertEqual(i3, i3) 61| 1| 62| 1| XCTAssertNotEqual(i1, i3) 63| 1| } 64| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/LayerTree.LayerTests.swift: 1| |// 2| |// LayerTree.LayerTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class LayerTreeLayerTests: XCTestCase { 36| | 37| | private typealias Layer = LayerTree.Layer 38| | private typealias Contents = LayerTree.Layer.Contents 39| | private typealias TextAttributes = LayerTree.TextAttributes 40| | private typealias Point = LayerTree.Point 41| | 42| 1| func testLayersWithSimpleContentsAreAppendedWithoutLayer() { 43| 1| let parent = LayerTree.Layer() 44| 1| let simple = LayerTree.Layer() 45| 1| simple.appendContents(.mockImage) 46| 1| 47| 1| parent.appendContents(.layer(simple)) 48| 1| XCTAssertEqual(parent.contents, [.mockImage]) 49| 1| } 50| | 51| 1| func testLayersWithComplexContentsAreAppendedWithoutLayer() { 52| 1| let parent = LayerTree.Layer() 53| 1| let complex = LayerTree.Layer() 54| 1| complex.appendContents(.mockImage) 55| 1| complex.opacity = 0.5 56| 1| 57| 1| parent.appendContents(.layer(complex)) 58| 1| XCTAssertEqual(parent.contents, [.layer(complex)]) 59| 1| } 60| | 61| 1| func testContentsTextEquality() { 62| 1| let c1 = Contents.text("Charlie", .zero, .normal) 63| 1| let c2 = Contents.text("Ida", .zero, .normal) 64| 1| let c3 = Contents.text("Charlie", Point(10, 20), .normal) 65| 1| 66| 1| var att = TextAttributes.normal 67| 1| att.color = .rgba(r: 1.0, g: 0, b: 0, a: 1.0) 68| 1| let c4 = Contents.text("Charlie", .zero, att) 69| 1| 70| 1| XCTAssertEqual(c1, c1) 71| 1| XCTAssertEqual(c1, .text("Charlie", .zero, .normal)) 72| 1| 73| 1| XCTAssertEqual(c2, c2) 74| 1| XCTAssertEqual(c2, .text("Ida", .zero, .normal)) 75| 1| 76| 1| XCTAssertEqual(c3, c3) 77| 1| XCTAssertEqual(c3, .text("Charlie", Point(10, 20), .normal)) 78| 1| 79| 1| XCTAssertEqual(c4, c4) 80| 1| XCTAssertEqual(c4, .text("Charlie", .zero, att)) 81| 1| 82| 1| XCTAssertNotEqual(c1, c2) 83| 1| XCTAssertNotEqual(c1, c3) 84| 1| XCTAssertNotEqual(c1, c4) 85| 1| XCTAssertNotEqual(c2, c3) 86| 1| XCTAssertNotEqual(c2, c4) 87| 1| XCTAssertNotEqual(c3, c4) 88| 1| } 89| |} 90| | 91| |private extension LayerTree.Layer.Contents { 92| | 93| 3| static var mockImage: LayerTree.Layer.Contents { 94| 3| let image = LayerTree.Image(mimeType: "image/png", data: Data(base64Encoded: "f00d")!)! 95| 3| return .image(image) 96| 3| } 97| | 98| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/LayerTree.PathTests.swift: 1| |// 2| |// LayerTree.PathTests.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 LayerTreePathTests: XCTestCase { 33| | 34| | typealias Float = LayerTree.Float 35| | typealias Point = LayerTree.Point 36| | typealias Path = LayerTree.Path 37| | typealias Segment = LayerTree.Path.Segment 38| | 39| 14| let twoThirds = Float(2.0/3.0) 40| | 41| | 42| 1| func testLocation() { 43| 1| let path = Path() 44| 1| XCTAssertNil(path.location) 45| 1| 46| 1| path.segments.append(move(110, 90)) 47| 1| XCTAssertEqual(path.location, Point(110, 90)) 48| 1| 49| 1| path.segments.append(line(200, 200)) 50| 1| XCTAssertEqual(path.location, Point(200, 200)) 51| 1| 52| 1| path.segments.append(.cubic(to: Point(300, 300), control1: Point(200, 200), control2: Point(250, 250))) 53| 1| XCTAssertEqual(path.location, Point(300, 300)) 54| 1| 55| 1| path.segments.append(.close) 56| 1| XCTAssertEqual(path.location, Point(110, 90)) 57| 1| } 58| | 59| 1| func testMove() { 60| 1| 61| 1| var m = LayerTree.Builder.createMove(from: .move(x: 10, y: 10, space: .absolute), last: .zero) 62| 1| XCTAssertEqual(m, move(10, 10)) 63| 1| 64| 1| m = LayerTree.Builder.createMove(from: .move(x: 10, y: 10, space: .absolute), 65| 1| last: Point(100, 100)) 66| 1| XCTAssertEqual(m, move(10, 10)) 67| 1| 68| 1| m = LayerTree.Builder.createMove(from: .move(x: 10, y: -10, space: .relative), 69| 1| last: Point(100, 100)) 70| 1| XCTAssertEqual(m, move(110, 90)) 71| 1| } 72| | 73| 1| func testLine() { 74| 1| var l = LayerTree.Builder.createLine(from: .line(x: 10, y: 10, space: .absolute), last: .zero) 75| 1| XCTAssertEqual(l, line(10, 10)) 76| 1| 77| 1| l = LayerTree.Builder.createLine(from: .line(x: 10, y: 10, space: .absolute), 78| 1| last: Point(100, 100)) 79| 1| XCTAssertEqual(l, line(10, 10)) 80| 1| 81| 1| l = LayerTree.Builder.createLine(from: .line(x: 10, y: -10, space: .relative), 82| 1| last: Point(100, 100)) 83| 1| XCTAssertEqual(l, line(110, 90)) 84| 1| } 85| | 86| 1| func testHorizontal() { 87| 1| var l = LayerTree.Builder.createHorizontal(from: .horizontal(x: 10, space: .absolute), last: .zero) 88| 1| XCTAssertEqual(l, line(10, 0)) 89| 1| 90| 1| l = LayerTree.Builder.createHorizontal(from: .horizontal(x: 10, space: .absolute), 91| 1| last: Point(100, 100)) 92| 1| XCTAssertEqual(l, line(10, 100)) 93| 1| 94| 1| l = LayerTree.Builder.createHorizontal(from: .horizontal(x: 10, space: .relative), 95| 1| last: Point(100, 100)) 96| 1| XCTAssertEqual(l, line(110, 100)) 97| 1| 98| 1| l = LayerTree.Builder.createHorizontal(from: .horizontal(x: -10, space: .relative), 99| 1| last: Point(100, 100)) 100| 1| XCTAssertEqual(l, line(90, 100)) 101| 1| } 102| | 103| 1| func testVertical() { 104| 1| var l = LayerTree.Builder.createVertical(from: .vertical(y: 10, space: .absolute), last: .zero) 105| 1| XCTAssertEqual(l, line(0, 10)) 106| 1| 107| 1| l = LayerTree.Builder.createVertical(from: .vertical(y: 10, space: .absolute), 108| 1| last: Point(100, 100)) 109| 1| XCTAssertEqual(l, line(100, 10)) 110| 1| 111| 1| l = LayerTree.Builder.createVertical(from: .vertical(y: 10, space: .relative), 112| 1| last: Point(100, 100)) 113| 1| XCTAssertEqual(l, line(100, 110)) 114| 1| 115| 1| l = LayerTree.Builder.createVertical(from: .vertical(y: -10, space: .relative), 116| 1| last: Point(100, 100)) 117| 1| XCTAssertEqual(l, line(100, 90)) 118| 1| } 119| | 120| 1| func testCubic() { 121| 1| var curve: DOM.Path.Segment 122| 1| curve = .cubic(x1: 0, y1: 10, 123| 1| x2: 20, y2: 30, 124| 1| x: 40, y: 50, space: .absolute) 125| 1| var c = LayerTree.Builder.createCubic(from: curve , last: .zero) 126| 1| XCTAssertEqual(c, cubic(40, 50, 0, 10, 20, 30)) 127| 1| 128| 1| curve = .cubic(x1: 100, y1: 0, 129| 1| x2: -10, y2: 10, 130| 1| x: 110, y: -10, space: .relative) 131| 1| c = LayerTree.Builder.createCubic(from: curve , last: Point(100, 100)) 132| 1| XCTAssertEqual(c, cubic(210, 90, 200, 100, 90, 110)) 133| 1| } 134| | 135| 1| func testCubicSmoothAbsolute() { 136| 1| var curve: DOM.Path.Segment 137| 1| curve = .cubicSmooth(x2:80, y2: 40, x: 100, y: 50, space: .absolute) 138| 1| var c = LayerTree.Builder.createCubicSmooth(from: curve, last: .zero, previous: Point.zero) 139| 1| XCTAssertEqual(c, cubic(100, 50, 0, 0, 80, 40)) 140| 1| 141| 1| curve = .cubicSmooth(x2:180, y2: 60, x: 200, y: 50, space: .absolute) 142| 1| c = LayerTree.Builder.createCubicSmooth(from: curve, last: Point(100, 50), previous: Point(80, 40)) 143| 1| XCTAssertEqual(c, cubic(200, 50, 120, 60, 180, 60)) 144| 1| } 145| | 146| 1| func testCubicSmoothRelative() { 147| 1| var curve: DOM.Path.Segment 148| 1| curve = .cubicSmooth(x2:80, y2: -10, x: 100, y: 0, space: .relative) 149| 1| var c = LayerTree.Builder.createCubicSmooth(from: curve, last: Point(0, 50), previous: Point(0, 50)) 150| 1| XCTAssertEqual(c, cubic(100, 50, 0, 50, 80, 40)) 151| 1| 152| 1| curve = .cubicSmooth(x2:80, y2: 10, x: 100, y: 0, space: .relative) 153| 1| c = LayerTree.Builder.createCubicSmooth(from: curve, last: Point(100, 50), previous: Point(80, 40)) 154| 1| XCTAssertEqual(c, cubic(200, 50, 120, 60, 180, 60)) 155| 1| } 156| | 157| 1| func testQuadraticBalanced() { 158| 1| //balanced quad with control point centered on the curve 159| 1| var quad: DOM.Path.Segment 160| 1| quad = .quadratic(x1: 150, y1: 0, 161| 1| x: 300, y: 50, space: .absolute) 162| 1| var c = LayerTree.Builder.createQuadratic(from: quad, last: Point(0, 50)) 163| 1| 164| 1| XCTAssertEqual(c, cubic(300, 50, 100, 16.6666641, 200, 16.6666641)) 165| 1| 166| 1| quad = .quadratic(x1: 150, y1: -50, 167| 1| x: 300, y: 0, space: .relative) 168| 1| c = LayerTree.Builder.createQuadratic(from: quad, last: Point(0, 50)) 169| 1| XCTAssertEqual(c, cubic(300, 50, 100, 16.6666641, 200, 16.6666641)) 170| 1| } 171| | 172| 1| func testQuadraticUnbalanced() { 173| 1| //quad with control point to the left 174| 1| var quad: DOM.Path.Segment 175| 1| quad = .quadratic(x1: 100, y1: 0, 176| 1| x: 300, y: 50, space: .absolute) 177| 1| var c = LayerTree.Builder.createQuadratic(from: quad, last: Point(0, 50)) 178| 1| 179| 1| XCTAssertEqual(c, cubic(300, 50, 100*twoThirds, 16.6666641, 100*twoThirds+150*twoThirds, 16.6666641)) 180| 1| 181| 1| //quad with control point to the right 182| 1| quad = .quadratic(x1: 200, y1: 0, 183| 1| x: 300, y: 50, space: .absolute) 184| 1| c = LayerTree.Builder.createQuadratic(from: quad, last: Point(0, 50)) 185| 1| XCTAssertEqual(c, cubic(300, 50, 200*twoThirds, 16.6666641, 200*twoThirds+150*twoThirds, 16.6666641)) 186| 1| } 187| | 188| 1| func testQuadraticSmoothAbsolute() { 189| 1| var quad: DOM.Path.Segment 190| 1| quad = .quadraticSmooth(x: 100, y: 50, space: .absolute) 191| 1| 192| 1| let c = LayerTree.Builder.createQuadraticSmooth(from: quad, last: Point(0, 50), previous: Point(-50, 0)) 193| 1| XCTAssertEqual(c, cubic(100, 50, 50, 100, 50+50*twoThirds, 100)) 194| 1| } 195| | 196| 1| func testClose() { 197| 1| XCTAssertEqual(LayerTree.Builder.createClose(from: .close), Segment.close) 198| 1| } 199| | 200| 1| func testDOMQuadraticSmooth() { 201| 1| let domSegment = DOM.Path.Segment.quadraticSmooth(x: 10.0, y: 10.0, space: .relative) 202| 1| let segment = LayerTree.Builder.makeSegment(from: domSegment, last: .init(10, 10), previous: nil) 203| 1| 204| 1| XCTAssertEqual(segment, .cubic(to: .init(20.0, 20.0), control1: .init(10, 10), control2: .init(13.333334, 10))) 205| 1| } 206| | 207| 1| func testDOMCubicSmooth() { 208| 1| let domSegment = DOM.Path.Segment.cubicSmooth(x2: 10, y2: 10, x: 10, y: 10, space: .relative) 209| 1| let segment = LayerTree.Builder.makeSegment(from: domSegment, last: .init(10, 10), previous: nil) 210| 1| 211| 1| XCTAssertEqual(segment, .cubic(to: .init(20.0, 20.0), control1: .init(10, 10), control2: .init(20.0, 20.0))) 212| 1| } 213| | 214| | // helpers to create Segments without labels 215| | // splatting of tuple is no longer supported 216| 4| private func move(_ x: Float, _ y: Float) -> Path.Segment { 217| 4| return .move(to: Point(x, y)) 218| 4| } 219| | 220| 12| private func line(_ x: Float, _ y: Float) -> Path.Segment { 221| 12| return .line(to: Point(x, y)) 222| 12| } 223| | 224| | private func cubic(_ x: Float, _ y: Float, 225| | _ x1: Float, _ y1: Float, 226| 11| _ x2: Float, _ y2: Float) -> Path.Segment { 227| 11| return .cubic(to: Point(x, y), control1: Point(x1, y1), control2: Point(x2, y2)) 228| 11| } 229| | 230| |} 231| | 232| | /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/LayerTree.ShapeTests.swift: 1| |// 2| |// LayerTree.ShapeTests.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 LayerTreeShapeTests: XCTestCase { 33| | 34| | typealias Point = LayerTree.Point 35| | typealias Rect = LayerTree.Rect 36| | typealias Size = LayerTree.Size 37| | typealias Path = LayerTree.Path 38| | typealias Shape = LayerTree.Shape 39| | 40| 1| func testShapeEquality() { 41| 1| let s1 = Shape.line(between: [.zero, Point(100, 200)]) 42| 1| let s2 = Shape.rect(within: .zero, radii: Size(10, 20)) 43| 1| let s3 = Shape.ellipse(within: .zero) 44| 1| let s4 = Shape.polygon(between: [.zero, Point(10, 20)]) 45| 1| let s5 = Shape.path(Path()) 46| 1| 47| 1| XCTAssertEqual(s1, s1) 48| 1| XCTAssertEqual(s1, .line(between: [.zero, Point(100, 200)])) 49| 1| XCTAssertNotEqual(s1, .line(between: [])) 50| 1| XCTAssertNotEqual(s1.hashValue, Shape.line(between: []).hashValue) 51| 1| 52| 1| XCTAssertEqual(s2, s2) 53| 1| XCTAssertEqual(s2, .rect(within: .zero, radii: Size(10, 20))) 54| 1| XCTAssertNotEqual(s2, .rect(within: .zero, radii: .zero)) 55| 1| XCTAssertNotEqual(s2.hashValue, Shape.rect(within: .zero, radii: .zero).hashValue) 56| 1| 57| 1| XCTAssertEqual(s3, s3) 58| 1| XCTAssertEqual(s3, .ellipse(within: .zero)) 59| 1| XCTAssertNotEqual(s3, .ellipse(within: Rect(x: 0, y: 0, width: 10, height: 20))) 60| 1| XCTAssertNotEqual(s3.hashValue, Shape.ellipse(within: Rect(x: 0, y: 0, width: 10, height: 20)).hashValue) 61| 1| 62| 1| XCTAssertEqual(s4, s4) 63| 1| XCTAssertEqual(s4, .polygon(between: [.zero, Point(10, 20)])) 64| 1| XCTAssertNotEqual(s4, .polygon(between: [])) 65| 1| XCTAssertNotEqual(s4.hashValue, Shape.polygon(between: []).hashValue) 66| 1| 67| 1| XCTAssertEqual(s5, s5) 68| 1| XCTAssertEqual(s5, .path(Path())) 69| 1| XCTAssertNotEqual(s5.hashValue, Shape.path(Path([.close])).hashValue) 70| 1| 71| 1| XCTAssertNotEqual(s1, s2) 72| 1| XCTAssertNotEqual(s1, s3) 73| 1| XCTAssertNotEqual(s1, s4) 74| 1| XCTAssertNotEqual(s1, s5) 75| 1| 76| 1| XCTAssertNotEqual(s2, s3) 77| 1| XCTAssertNotEqual(s2, s4) 78| 1| XCTAssertNotEqual(s2, s5) 79| 1| 80| 1| XCTAssertNotEqual(s3, s4) 81| 1| XCTAssertNotEqual(s3, s5) 82| 1| 83| 1| XCTAssertNotEqual(s4, s5) 84| 1| } 85| | 86| 1| func testLineBuilder() { 87| 1| let line = DOM.Line(x1: 10, y1: 20, x2: 30, y2: 40) 88| 1| let shape = LayerTree.Builder.makeShape(from: line) 89| 1| 90| 1| XCTAssertEqual(shape, .line(between: [Point(10, 20), Point(30, 40)])) 91| 1| } 92| | 93| 1| func testCircleBuilder() { 94| 1| let cicle = DOM.Circle(cx: 50, cy: 50, r: 25) 95| 1| let shape = LayerTree.Builder.makeShape(from: cicle) 96| 1| 97| 1| XCTAssertEqual(shape, .ellipse(within: Rect(x: 25, y: 25, width: 50, height: 50))) 98| 1| } 99| | 100| 1| func testEllipseBuilder() { 101| 1| let ellipse = DOM.Ellipse(cx: 50, cy: 75, rx: 25, ry: 50) 102| 1| let shape = LayerTree.Builder.makeShape(from: ellipse) 103| 1| 104| 1| XCTAssertEqual(shape, .ellipse(within: Rect(x: 25, y: 25, width: 50, height: 100))) 105| 1| } 106| | 107| 1| func testRectBuilder() { 108| 1| let rect = DOM.Rect(x: 10, y: 20, width: 30, height: 40) 109| 1| let shape = LayerTree.Builder.makeShape(from: rect) 110| 1| XCTAssertEqual(shape, .rect(within: Rect(x: 10, y: 20, width: 30, height: 40), 111| 1| radii: .zero)) 112| 1| 113| 1| //add corner radii 114| 1| rect.rx = 2 115| 1| rect.ry = 4 116| 1| let another = LayerTree.Builder.makeShape(from: rect) 117| 1| XCTAssertEqual(another, .rect(within: Rect(x: 10, y: 20, width: 30, height: 40), 118| 1| radii: Size(2, 4))) 119| 1| } 120| | 121| 1| func testPolylineBuilder() { 122| 1| let line = DOM.Polyline(10,20,30,40,50,60) 123| 1| let shape = LayerTree.Builder.makeShape(from: line) 124| 1| 125| 1| XCTAssertEqual(shape, .line(between: [Point(10, 20), Point(30, 40), Point(50, 60)])) 126| 1| } 127| | 128| 1| func testPolygonBuilder() { 129| 1| let poly = DOM.Polygon(10,20,30,40,50,60) 130| 1| let shape = LayerTree.Builder.makeShape(from: poly) 131| 1| 132| 1| XCTAssertEqual(shape, .polygon(between: [Point(10, 20), Point(30, 40), Point(50, 60)])) 133| 1| } 134| | 135| 1| func testPathBuilder() { 136| 1| let domPath = DOM.Path(x: 10, y: 20) 137| 1| domPath.segments.append(.line(x: 30, y: 40, space: .absolute)) 138| 1| let shape = LayerTree.Builder.makeShape(from: domPath) 139| 1| 140| 1| let path = Path() 141| 1| path.segments = [.move(to: Point(10, 20)), .line(to: Point(30, 40))] 142| 1| XCTAssertEqual(shape, .path(path)) 143| 1| } 144| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/LayerTree.TransformTests.swift: 1| |// 2| |// LayerTree.TransformTests.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 LayerTreeTransformTests: XCTestCase { 33| | 34| | private typealias Transform = LayerTree.Transform 35| | private typealias Matrix = LayerTree.Transform.Matrix 36| | 37| 1| func testSkewXMatrix() { 38| 1| let transform = Transform.skewX(angle: LayerTree.Float.pi/4) 39| 1| XCTAssertEqual(transform.toMatrix(), Matrix(a: 1, b: 0, c: tan(LayerTree.Float.pi/4), d: 1, tx: 0, ty: 0)) 40| 1| } 41| | 42| 1| func testSkewYMatrix() { 43| 1| let transform = Transform.skewY(angle: LayerTree.Float.pi/4) 44| 1| XCTAssertEqual(transform.toMatrix(), Matrix(a: 1, b: tan(LayerTree.Float.pi/4), c: 0, d: 1, tx: 0, ty: 0)) 45| 1| } 46| | 47| 1| func testScaleMatrix() { 48| 1| let transform = Transform.scale(sx: 2.0, sy: 3.0) 49| 1| XCTAssertEqual(transform.toMatrix(), Matrix(a: 2.0, b: 0, c: 0, d: 3.0, tx: 0, ty: 0)) 50| 1| } 51| | 52| 1| func testTranslateMatrix() { 53| 1| let transform = Transform.translate(tx: 2.0, ty: 3.0) 54| 1| XCTAssertEqual(transform.toMatrix(), Matrix(a: 1, b: 0, c: 0, d: 1, tx: 2.0, ty: 3.0)) 55| 1| } 56| | 57| 1| func testRotateMatrix() { 58| 1| let angle = LayerTree.Float.pi/4 59| 1| let transform = Transform.rotate(radians: angle) 60| 1| XCTAssertEqual(transform.toMatrix(), 61| 1| Matrix(a: cos(angle), b: sin(angle), c: -sin(angle), d: cos(angle), tx: 0, ty: 0)) 62| 1| } 63| | 64| 1| func testMatrixConcatenation() { 65| 1| let concatenated = Transform.identity.toMatrix().concatenated(Transform.identity.toMatrix()) 66| 1| XCTAssertEqual(concatenated, Transform.identity.toMatrix()) 67| 1| } 68| | 69| 1| func testDOMMakesLayerTreeTranslate() { 70| 1| let translate = DOM.Transform.translate(tx: 10, ty: 20) 71| 1| let transform = LayerTree.Builder.createTransform(for: translate) 72| 1| 73| 1| XCTAssertEqual(transform, [.translate(tx: 10, ty: 20)]) 74| 1| } 75| | 76| 1| func testDOMMakesScaleTransform() { 77| 1| let scale = DOM.Transform.scale(sx: 10, sy: 20) 78| 1| let transform = LayerTree.Builder.createTransform(for: scale) 79| 1| 80| 1| XCTAssertEqual(transform, [.scale(sx: 10, sy: 20)]) 81| 1| } 82| | 83| 1| func testDOMMakesRotateTransform() { 84| 1| let rotate = DOM.Transform.rotate(angle: 10) 85| 1| let transform = LayerTree.Builder.createTransform(for: rotate) 86| 1| 87| 1| let radians = 10*Float.pi/180.0 88| 1| XCTAssertEqual(transform, [.rotate(radians: radians)]) 89| 1| } 90| | 91| 1| func testDOMMakesRotatePointTransform() { 92| 1| let rotate = DOM.Transform.rotatePoint(angle: 10, cx: 20, cy: 30) 93| 1| let transform = LayerTree.Builder.createTransform(for: rotate) 94| 1| 95| 1| let radians = 10*Float.pi/180.0 96| 1| XCTAssertEqual(transform, [.translate(tx: 20, ty: 30), 97| 1| .rotate(radians: radians), 98| 1| .translate(tx: -20, ty: -30)]) 99| 1| } 100| | 101| 1| func testDOMMakesSkewXTransform() { 102| 1| let skew = DOM.Transform.skewX(angle: 10) 103| 1| let transform = LayerTree.Builder.createTransform(for: skew) 104| 1| 105| 1| let radians = 10*Float.pi/180.0 106| 1| XCTAssertEqual(transform, [.skewX(angle: radians)]) 107| 1| } 108| | 109| 1| func testDOMMakesSkewYTransform() { 110| 1| let skew = DOM.Transform.skewY(angle: 10) 111| 1| let transform = LayerTree.Builder.createTransform(for: skew) 112| 1| 113| 1| let radians = 10*Float.pi/180.0 114| 1| XCTAssertEqual(transform, [.skewY(angle: radians)]) 115| 1| } 116| | 117| 1| func testDOMMakesMatrixTransform() { 118| 1| let matrix = DOM.Transform.matrix(a: 10, b: 20, c: 30, d: 40, e: 50, f: 60) 119| 1| let transform = LayerTree.Builder.createTransform(for: matrix) 120| 1| 121| 1| let expected = Matrix(a: 10, b: 20, c: 30, d: 40, tx: 50, ty: 60) 122| 1| XCTAssertEqual(transform, [.matrix(expected)]) 123| 1| } 124| | 125| 1| func testDOMMakesMultipleTransforms() { 126| 1| let translate = DOM.Transform.translate(tx: 10, ty: 20) 127| 1| let scale = DOM.Transform.scale(sx: 10, sy: 20) 128| 1| let transform = LayerTree.Builder.createTransforms(from: [translate, scale]) 129| 1| 130| 1| XCTAssertEqual(transform, [.translate(tx: 10, ty: 20), 131| 1| .scale(sx: 10, sy: 20)]) 132| 1| } 133| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/MockRenderer.swift: 1| |// 2| |// MockRenderer.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| |import XCTest 33| |@testable import SwiftDraw 34| | 35| |final class MockRenderer: Renderer { 36| | 37| | typealias Types = LayerTreeTypes 38| | 39| 1| var operations = [String]() 40| | 41| 1| func pushState() { 42| 1| operations.append("pushState") 43| 1| } 44| | 45| 1| func popState() { 46| 1| operations.append("popState") 47| 1| } 48| | 49| 1| func pushTransparencyLayer() { 50| 1| operations.append("pushTransparencyLayer") 51| 1| } 52| | 53| 1| func popTransparencyLayer() { 54| 1| operations.append("popTransparencyLayer") 55| 1| } 56| | 57| 1| func concatenate(transform: LayerTree.Transform) { 58| 1| operations.append("concatenateTransform") 59| 1| } 60| | 61| 1| func translate(tx: LayerTree.Float, ty: LayerTree.Float) { 62| 1| operations.append("translate") 63| 1| } 64| | 65| 1| func rotate(angle: LayerTree.Float) { 66| 1| operations.append("rotate") 67| 1| } 68| | 69| 1| func scale(sx: LayerTree.Float, sy: LayerTree.Float) { 70| 1| operations.append("scale") 71| 1| } 72| | 73| 1| func setFill(color: LayerTree.Color) { 74| 1| operations.append("setFillColor") 75| 1| } 76| | 77| 1| func setFill(pattern: LayerTree.Pattern) { 78| 1| operations.append("setFillPattern") 79| 1| } 80| | 81| 1| func setStroke(color: LayerTree.Color) { 82| 1| operations.append("setStrokeColor") 83| 1| } 84| | 85| 1| func setLine(width: LayerTree.Float) { 86| 1| operations.append("setLineWidth") 87| 1| } 88| | 89| 1| func setLine(cap: LayerTree.LineCap) { 90| 1| operations.append("setLineCap") 91| 1| } 92| | 93| 1| func setLine(join: LayerTree.LineJoin) { 94| 1| operations.append("setLineJoin") 95| 1| } 96| | 97| 1| func setLine(miterLimit: LayerTree.Float) { 98| 1| operations.append("setLineMiterLimit") 99| 1| } 100| | 101| 1| func setClip(path: [LayerTree.Shape]) { 102| 1| operations.append("setClip") 103| 1| } 104| | 105| 1| func setClip(mask: [Any], frame: LayerTree.Rect) { 106| 1| operations.append("setClipMask") 107| 1| } 108| | 109| 1| func setAlpha(_ alpha: LayerTree.Float) { 110| 1| operations.append("setAlpha") 111| 1| } 112| | 113| 1| func setBlend(mode: LayerTree.BlendMode) { 114| 1| operations.append("setBlendMode") 115| 1| } 116| | 117| 1| func stroke(path: [LayerTree.Shape]) { 118| 1| operations.append("strokePath") 119| 1| } 120| | 121| 1| func fill(path: [LayerTree.Shape], rule: LayerTree.FillRule) { 122| 1| operations.append("fillPath") 123| 1| } 124| | 125| 1| func draw(image: LayerTree.Image) { 126| 1| operations.append("drawImage") 127| 1| } 128| | 129| 1| func draw(gradient: LayerTree.Gradient, from start: LayerTree.Point, to end: LayerTree.Point) { 130| 1| operations.append("drawGradient") 131| 1| } 132| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/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| |import AppKit 33| | 34| |extension NSBitmapImageRep { 35| | 36| 4| convenience init(pixelsWide width: Int, pixelsHigh height: Int) { 37| 4| self.init(bitmapDataPlanes: nil, 38| 4| pixelsWide: width, 39| 4| pixelsHigh: height, 40| 4| bitsPerSample: 8, 41| 4| samplesPerPixel: 4, 42| 4| hasAlpha: true, 43| 4| isPlanar: false, 44| 4| colorSpaceName: NSColorSpaceName.deviceRGB, 45| 4| bytesPerRow: 0, 46| 4| bitsPerPixel: 32)! 47| 4| } 48| | 49| 2| func lockFocus() { 50| 2| NSGraphicsContext.saveGraphicsState() 51| 2| NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: self) 52| 2| } 53| | 54| 2| func unlockFocus() { 55| 2| NSGraphicsContext.restoreGraphicsState() 56| 2| } 57| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/NSImage+ImageTests.swift: 1| |// 2| |// NSImage+ImageTests.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| |import XCTest 33| |@testable import SwiftDraw 34| | 35| |final class NSImageTests: XCTestCase { 36| | 37| 1| func testImageLoads() { 38| 1| let image = NSImage(svgNamed: "lines.svg", in: .test) 39| 1| XCTAssertNotNil(image) 40| 1| } 41| | 42| 1| func testMissingImageDoesNotLoad() { 43| 1| let image = NSImage(svgNamed: "missing.svg", in: .test) 44| 1| XCTAssertNil(image) 45| 1| } 46| | 47| 1| func testNSImageDraws() { 48| 1| let canvas = NSBitmapImageRep(pixelsWide: 2, pixelsHigh: 2) 49| 1| 50| 1| canvas.lockFocus() 51| 1| NSImage(svgNamed: "lines.svg", in: .test)?.draw(in: NSRect(x: 0, y: 0, width: 2, height: 2)) 52| 1| canvas.unlockFocus() 53| 1| } 54| | 55| 1| func testImageDraws() { 56| 1| let canvas = NSBitmapImageRep(pixelsWide: 2, pixelsHigh: 2) 57| 1| let image = Image.makeQuad().rasterize(with: CGSize(width: 2, height: 2)) 58| 1| 59| 1| canvas.lockFocus() 60| 1| image.draw(in: NSRect(x: 0, y: 0, width: 2, height: 2)) 61| 1| canvas.unlockFocus() 62| 1| 63| 1| XCTAssertEqual(canvas.colorAt(x: 0, y: 0), NSColor(deviceRed: 1.0, green: 0, blue: 0, alpha: 1.0)) 64| 1| XCTAssertEqual(canvas.colorAt(x: 1, y: 1), NSColor(deviceRed: 0.0, green: 0, blue: 1.0, alpha: 1.0)) 65| 1| } 66| |} 67| | 68| |private extension Image { 69| | 70| 1| static func makeQuad() -> Image { 71| 1| let svg = DOM.SVG(width: 2, height: 2) 72| 1| svg.childElements.append(DOM.Rect(x: 0, y: 0, width: 1, height: 1)) 73| 1| svg.childElements.append(DOM.Rect(x: 1, y: 1, width: 1, height: 1)) 74| 1| svg.childElements[0].fill = .color(DOM.Color.rgbi(255, 0, 0)) 75| 1| svg.childElements[1].fill = .color(DOM.Color.rgbi(0, 0, 255)) 76| 1| return Image(svg: svg) 77| 1| } 78| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/Parser.AttributesTests.swift: 1| |// 2| |// AttributeParserTests.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 6/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| | 32| |import XCTest 33| |@testable import SwiftDraw 34| | 35| |final class AttributeParserTests: XCTestCase { 36| | 37| | // func testParser() { 38| | // let parser = XMLParser.Att 39| | // let att = ["x": "20"] 40| | // XCTAssertThrowsError(try att.parse("x", { _ in throw XMLParser.Error.invalid })) 41| | // } 42| | 43| 1| func testParserOrder() { 44| 1| let parser = XMLParser.ValueParser() 45| 1| 46| 1| let att = XMLParser.Attributes(parser: parser, 47| 1| element: ["x": "10", "y": "20.0", "fill": "red"], 48| 1| style: ["x": "d", "fill": "green"]) 49| 1| 50| 1| //parse from style 51| 1| XCTAssertEqual(try att.parseColor("fill"), .keyword(.green)) 52| 1| XCTAssertThrowsError(try att.parseFloat("x")) 53| 1| 54| 1| //missing throws error 55| 1| XCTAssertThrowsError(try att.parseFloat("other")) 56| 1| //missing returns optional 57| 1| XCTAssertNil(try att.parseFloat("other") as DOM.Float?) 58| 1| 59| 1| //fall through to element 60| 1| XCTAssertEqual(try att.parseFloat("y"), 20) 61| 1| 62| 1| //SkipInvalidAttributes 63| 1| let another = XMLParser.Attributes(parser: parser, 64| 1| options: [.skipInvalidAttributes], 65| 1| element: att.element, 66| 1| style: att.style) 67| 1| 68| 1| 69| 1| XCTAssertEqual(try another.parseColor("fill"), .keyword(.green)) 70| 1| XCTAssertEqual(try another.parseFloat("x"), 10) 71| 1| XCTAssertEqual(try another.parseFloat("y"), 20) 72| 1| 73| 1| //missing throws error 74| 1| XCTAssertThrowsError(try another.parseFloat("other")) 75| 1| //missing returns optional 76| 1| XCTAssertNil(try another.parseFloat("other") as DOM.Float?) 77| 1| //invalid returns optional 78| 1| XCTAssertNil(try another.parseColor("x") as DOM.Color?) 79| 1| } 80| | 81| 1| func testDictionary() { 82| 1| let att = ["x": "20", "y": "30", "fill": "#a0a0a0", "display": "none", "some": "random"] 83| 1| 84| 1| XCTAssertEqual(try att.parseCoordinate("x"), 20.0) 85| 1| XCTAssertEqual(try att.parseCoordinate("y"), 30.0) 86| 1| XCTAssertEqual(try att.parseColor("fill"), .hex(160, 160, 160)) 87| 1| XCTAssertEqual(try att.parseRaw("display"), DOM.DisplayMode.none) 88| 1| 89| 1| XCTAssertThrowsError(try att.parseFloat("other")) 90| 1| XCTAssertThrowsError(try att.parseColor("some")) 91| 1| 92| 1| //missing returns optional 93| 1| XCTAssertNil(try att.parseFloat("other") as DOM.Float?) 94| 1| } 95| | 96| 1| func testParseString() { 97| 1| let att = ["x": "20", "some": "random"] 98| 1| XCTAssertEqual(try att.parseString("x"), "20") 99| 1| XCTAssertThrowsError(try att.parseString("missing")) 100| 1| } 101| | 102| 1| func testParseFloat() { 103| 1| let att = ["x": "20", "some": "random"] 104| 1| XCTAssertEqual(try att.parseFloat("x"), 20.0) 105| 1| XCTAssertNil(try att.parseFloat("missing")) 106| 1| XCTAssertThrowsError(try att.parseFloat("some")) 107| 1| } 108| | 109| 1| func testParseFloats() { 110| 1| let att = ["x": "20 30 40", "some": "random"] 111| 1| XCTAssertEqual(try att.parseFloats("x"), [20.0, 30.0, 40.0]) 112| 1| XCTAssertThrowsError(try att.parseFloats("some")) 113| 1| } 114| | 115| 1| func testParsePoints() { 116| 1| let att = ["x": "20 30 40 50", "some": "random"] 117| 1| XCTAssertEqual(try att.parsePoints("x"), [DOM.Point(20, 30), DOM.Point(40, 50)]) 118| 1| XCTAssertNil(try att.parsePoints("missing")) 119| 1| XCTAssertThrowsError(try att.parsePoints("some")) 120| 1| XCTAssertThrowsError(try att.parsePoints("some") as [DOM.Point]?) 121| 1| } 122| | 123| 1| func testParseLength() { 124| 1| let att = ["x": "20", "y": "aa"] 125| 1| XCTAssertEqual(try att.parseLength("x"), 20) 126| 1| XCTAssertNil(try att.parseLength("missing")) 127| 1| XCTAssertThrowsError(try att.parseLength("y")) 128| 1| XCTAssertThrowsError(try att.parseLength("y") as DOM.Length?) 129| 1| } 130| | 131| 1| func testParseBool() { 132| 1| let att = ["x": "true", "y": "5"] 133| 1| XCTAssertEqual(try att.parseBool("x"), true) 134| 1| XCTAssertNil(try att.parseBool("missing")) 135| 1| XCTAssertThrowsError(try att.parseBool("y")) 136| 1| XCTAssertThrowsError(try att.parseBool("y") as Bool?) 137| 1| } 138| | 139| 1| func testParseURL() { 140| 1| let att = ["clip": "http://www.test.com", "mask": "20 twenty"] 141| 1| XCTAssertEqual(try att.parseUrl("clip"), URL(string: "http://www.test.com")) 142| 1| XCTAssertNil(try att.parseUrl("missing")) 143| 1| XCTAssertThrowsError(try att.parseUrl("mask")) 144| 1| } 145| | 146| 1| func testParseURLSelector() { 147| 1| let att = ["clip": "url(#shape)", "mask": "aa"] 148| 1| XCTAssertEqual(try att.parseUrlSelector("clip"), URL(string: "#shape")) 149| 1| XCTAssertNil(try att.parseUrlSelector("missing")) 150| 1| XCTAssertThrowsError(try att.parseUrlSelector("mask")) 151| 1| } 152| | // 153| | // func parseString(_ key: String) throws -> String { 154| | // return try parse(key) { $0 } 155| | // } 156| | // 157| | // func parseFloat(_ key: String) throws -> DOM.Float { 158| | // return try parse(key) { return try parser.parseFloat($0) } 159| | // } 160| | // 161| | // func parseFloats(_ key: String) throws -> [DOM.Float] { 162| | 163| |} 164| | 165| | 166| |//Allow Dictionary to become an attribute parser 167| |extension Dictionary: AttributeParser { 168| 109| public var parser: AttributeValueParser { return XMLParser.ValueParser() } 169| 5| public var options: SwiftDraw.XMLParser.Options { return [] } 170| | 171| 202| public func parse(_ key: String, _ exp: (String) throws -> T) throws -> T { 172| 202| guard let dict = self as? [String: String], 173| 202| let value = dict[key] else { throw XMLParser.Error.missingAttribute(name: key) } 174| 127| 175| 127| return try exp(value) 176| 202| } 177| |} 178| | 179| | 180| | /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/Parser.GraphicAttributeTests.swift: 1| |// 2| |// Parser.GraphicAttributeTests.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| | 33| |import XCTest 34| |@testable import SwiftDraw 35| | 36| |final class ParserGraphicAttributeTests: XCTestCase { 37| | 38| 1| func testPresentationAttributes() throws { 39| 1| var parsed = try XMLParser().parsePresentationAttributes([:]) 40| 1| XCTAssertNil(parsed.opacity) 41| 1| XCTAssertNil(parsed.display) 42| 1| XCTAssertNil(parsed.stroke) 43| 1| XCTAssertNil(parsed.strokeWidth) 44| 1| XCTAssertNil(parsed.strokeOpacity) 45| 1| XCTAssertNil(parsed.strokeLineCap) 46| 1| XCTAssertNil(parsed.strokeLineJoin) 47| 1| XCTAssertNil(parsed.strokeDashArray) 48| 1| XCTAssertNil(parsed.fill) 49| 1| XCTAssertNil(parsed.fillOpacity) 50| 1| XCTAssertNil(parsed.fillRule) 51| 1| XCTAssertNil(parsed.transform) 52| 1| XCTAssertNil(parsed.clipPath) 53| 1| XCTAssertNil(parsed.mask) 54| 1| 55| 1| let att = ["opacity": "95%", 56| 1| "display": "none", 57| 1| "stroke": "green", 58| 1| "stroke-width": "15.0", 59| 1| "stroke-opacity": "75.6%", 60| 1| "stroke-linecap": "butt", 61| 1| "stroke-linejoin": "miter", 62| 1| "stroke-dasharray": "1 5 10", 63| 1| "fill": "purple", 64| 1| "fill-opacity": "25%", 65| 1| "fill-rule": "evenodd", 66| 1| "transform": "scale(15)", 67| 1| "clip-path": "url(#circlePath)", 68| 1| "mask": "url(#fancyMask)"] 69| 1| 70| 1| parsed = try XMLParser().parsePresentationAttributes(att) 71| 1| 72| 1| XCTAssertEqual(parsed.opacity, 0.95) 73| 1| XCTAssertEqual(parsed.display!, .none) 74| 1| XCTAssertEqual(parsed.stroke, .keyword(.green)) 75| 1| XCTAssertEqual(parsed.strokeWidth, 15) 76| 1| XCTAssertEqual(parsed.strokeOpacity, 0.756) 77| 1| XCTAssertEqual(parsed.strokeLineCap, .butt) 78| 1| XCTAssertEqual(parsed.strokeLineJoin, .miter) 79| 1| XCTAssertEqual(parsed.strokeDashArray!, [1, 5, 10]) 80| 1| XCTAssertEqual(parsed.fill, .color(.keyword(.purple))) 81| 1| XCTAssertEqual(parsed.fillOpacity, 0.25) 82| 1| XCTAssertEqual(parsed.fillRule, .evenodd) 83| 1| XCTAssertEqual(parsed.transform!, [.scale(sx: 15, sy: 15)]) 84| 1| XCTAssertEqual(parsed.clipPath?.fragment, "circlePath") 85| 1| XCTAssertEqual(parsed.mask?.fragment, "fancyMask") 86| 1| } 87| | 88| 1| func testCircle() throws { 89| 1| let el = XML.Element("circle", style: "clip-path: url(#cp1); cx:10;cy:10;r:10; fill:black; stroke-width:2") 90| 1| 91| 1| let parsed = try XMLParser().parseGraphicsElement(el) 92| 1| let circle = parsed as? DOM.Circle 93| 1| XCTAssertNotNil(circle) 94| 1| XCTAssertEqual(circle?.clipPath?.fragment, "cp1") 95| 1| XCTAssertEqual(circle?.fill, .color(.keyword(.black))) 96| 1| XCTAssertEqual(circle?.strokeWidth, 2) 97| 1| } 98| | 99| 1| func testDisplayMode() { 100| 1| let parser = XMLParser.ValueParser() 101| 1| 102| 1| XCTAssertEqual(try parser.parseRaw("none"), DOM.DisplayMode.none) 103| 1| XCTAssertEqual(try parser.parseRaw(" none "), DOM.DisplayMode.none) 104| 1| XCTAssertThrowsError(try parser.parseRaw("ds") as DOM.DisplayMode ) 105| 1| } 106| | 107| 1| func testStrokeLineCap() { 108| 1| let parser = XMLParser.ValueParser() 109| 1| 110| 1| XCTAssertEqual(try parser.parseRaw("butt"), DOM.LineCap.butt) 111| 1| XCTAssertEqual(try parser.parseRaw(" round"), DOM.LineCap.round) 112| 1| XCTAssertThrowsError(try parser.parseRaw("squdare") as DOM.LineCap) 113| 1| } 114| | 115| 1| func testStrokeLineJoin() { 116| 1| let parser = XMLParser.ValueParser() 117| 1| 118| 1| XCTAssertEqual(try parser.parseRaw("miter"), DOM.LineJoin.miter) 119| 1| XCTAssertEqual(try parser.parseRaw(" bevel"), DOM.LineJoin.bevel) 120| 1| XCTAssertThrowsError(try parser.parseRaw("ds") as DOM.LineJoin) 121| 1| } 122| |} 123| | /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/Parser.SVGTests.swift: 1| |// 2| |// Parser.SVGTests.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 3/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| | 33| |import XCTest 34| |@testable import SwiftDraw 35| | 36| |final class SVGTests: XCTestCase { 37| | 38| 1| func testSVG() throws { 39| 1| let node = XML.Element(name: "svg", attributes: ["width": "100", "height": "200"]) 40| 1| let parser = XMLParser() 41| 1| 42| 1| var parsed = try parser.parseSVG(node) 43| 1| let expected = DOM.SVG(width: 100, height: 200) 44| 1| XCTAssertEqual(parsed, expected) 45| 1| 46| 1| expected.viewBox = DOM.SVG.ViewBox(x: 10, y: 20, width: 100, height: 200) 47| 1| XCTAssertNotEqual(parsed, expected) 48| 1| 49| 1| node.attributes["viewBox"] = "10 20 100 200" 50| 1| parsed = try parser.parseSVG(node) 51| 1| XCTAssertEqual(parsed, expected) 52| 1| 53| 1| expected.fill = .color(.keyword(.red)) 54| 1| XCTAssertNotEqual(parsed, expected) 55| 1| } 56| | 57| 1| func testParseSVGInvalidNode() { 58| 1| let node = XML.Element(name: "svg2", attributes: ["width": "100", "height": "200"]) 59| 1| XCTAssertThrowsError(try XMLParser().parseSVG(node)) 60| 1| } 61| | 62| 1| func testParseSVGMissingHeightInvalidNode() { 63| 1| let node = XML.Element(name: "svg", attributes: ["width": "100"]) 64| 1| XCTAssertThrowsError(try XMLParser().parseSVG(node)) 65| 1| } 66| | 67| 1| func testParseSVGMissingWidthInvalidNode() { 68| 1| let node = XML.Element(name: "svg", attributes: ["height": "100"]) 69| 1| XCTAssertThrowsError(try XMLParser().parseSVG(node)) 70| 1| } 71| | 72| 1| func testViewBox() { 73| 1| let parsed = (try? XMLParser().parseViewBox(" 10\t20 300.0 5e2")!)! 74| 1| XCTAssertEqual(parsed.x, 10) 75| 1| XCTAssertEqual(parsed.y, 20) 76| 1| XCTAssertEqual(parsed.width, 300) 77| 1| XCTAssertEqual(parsed.height, 500) 78| 1| 79| 1| XCTAssertNotNil(try! XMLParser().parseViewBox("10 10 10 10")) 80| 1| XCTAssertThrowsError(try XMLParser().parseViewBox("10 10 10 10a")) 81| 1| XCTAssertThrowsError(try XMLParser().parseViewBox(" 10\t20 300")) 82| 1| XCTAssertThrowsError(try XMLParser().parseViewBox("10 10 10 10a")) 83| 1| } 84| | 85| 1| func testClipPath() throws { 86| 1| 87| 1| let node = XML.Element(name: "clipPath", attributes: ["id": "hello"]) 88| 1| 89| 1| var parsed = try XMLParser().parseClipPath(node) 90| 1| XCTAssertEqual(parsed.id, "hello") 91| 1| 92| 1| node.children.append(XML.Element("line", style: "x1:0;y1:0;x2:50;y2:60")) 93| 1| node.children.append(XML.Element("circle", style: "cx:0;cy:10;r:20")) 94| 1| 95| 1| parsed = try XMLParser().parseClipPath(node) 96| 1| XCTAssertEqual(parsed.id, "hello") 97| 1| XCTAssertEqual(parsed.childElements.count, 2) 98| 1| } 99| | 100| 1| func testParseDefs() throws { 101| 1| let svg = XML.Element(name: "svg") 102| 1| let defs = XML.Element(name: "defs") 103| 1| let g = XML.Element(name: "g") 104| 1| svg.children.append(defs) 105| 1| svg.children.append(g) 106| 1| 107| 1| g.children.append(XML.Element("circle", id: "c2", style: "cx:0;cy:10;r:20")) 108| 1| let defs1 = XML.Element(name: "defs") 109| 1| g.children.append(defs1) 110| 1| defs1.children.append(XML.Element("circle", id: "c3", style: "cx:0;cy:10;r:20")) 111| 1| 112| 1| defs.children.append(XML.Element("circle", id: "c1", style: "cx:0;cy:10;r:20")) 113| 1| svg.children.append(defs1) 114| 1| 115| 1| let elements = try SwiftDraw.XMLParser().parseSVGDefs(svg).elements 116| 1| XCTAssertEqual(elements.count, 2) 117| 1| } 118| | 119| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/Parser.XML.ColorTests.swift: 1| |// 2| |// Parser.XML.ColorTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class ParserColorTests: XCTestCase { 36| | 37| 1| func testColorNone() { 38| 1| XCTAssertEqual(try XMLParser().parseColor("none"), .none) 39| 1| XCTAssertEqual(try XMLParser().parseColor(" none"), .none) 40| 1| XCTAssertEqual(try XMLParser().parseColor("\t none \t"), .none) 41| 1| } 42| | 43| 1| func testColorKeyword() { 44| 1| XCTAssertEqual(try XMLParser().parseColor("aliceblue"), .keyword(.aliceblue)) 45| 1| XCTAssertEqual(try XMLParser().parseColor("wheat"), .keyword(.wheat)) 46| 1| XCTAssertEqual(try XMLParser().parseColor("cornflowerblue"), .keyword(.cornflowerblue)) 47| 1| XCTAssertEqual(try XMLParser().parseColor(" magenta"), .keyword(.magenta)) 48| 1| XCTAssertEqual(try XMLParser().parseColor("black "), .keyword(.black)) 49| 1| XCTAssertEqual(try XMLParser().parseColor("\t red \t"), .keyword(.red)) 50| 1| } 51| | 52| 1| func testColorRGBi() { 53| 1| // integer 0-255 54| 1| XCTAssertEqual(try XMLParser().parseColor("rgb(0,1,2)"), .rgbi(0, 1, 2)) 55| 1| XCTAssertEqual(try XMLParser().parseColor(" rgb( 0 , 1 , 2) "), .rgbi(0, 1, 2)) 56| 1| XCTAssertEqual(try XMLParser().parseColor("rgb(255,100,78)"), .rgbi(255, 100, 78)) 57| 1| } 58| | 59| 1| func testColorRGBf() { 60| 1| // percentage 0-100% 61| 1| XCTAssertEqual(try XMLParser().parseColor("rgb(0,1%,99%)"), .rgbf(0.0, 0.01, 0.99)) 62| 1| XCTAssertEqual(try XMLParser().parseColor("rgb( 0%, 52% , 100%) "), .rgbf(0.0, 0.52, 1.0)) 63| 1| XCTAssertEqual(try XMLParser().parseColor("rgb(75%,25%,7%)"), .rgbf(0.75, 0.25, 0.07)) 64| 1| } 65| | 66| 1| func testColorHex() { 67| 1| XCTAssertEqual(try XMLParser().parseColor("#a06"), .hex(160, 0, 96)) 68| 1| XCTAssertEqual(try XMLParser().parseColor("#123456"), .hex(18, 52, 86)) 69| 1| XCTAssertEqual(try XMLParser().parseColor("#FF11DD"), .hex(255, 17, 221)) 70| 1| XCTAssertThrowsError(try XMLParser().parseColor("#invalid")) 71| 1| } 72| |} 73| | 74| |private extension SwiftDraw.XMLParser { 75| | 76| 19| func parseColor(_ value: String) throws -> DOM.Color { 77| 19| return try parseFill(value).getColor() 78| 19| } 79| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/Parser.XML.ElementTests.swift: 1| |// 2| |// Parser.XML.ElementTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class XMLParserElementTests: XCTestCase { 36| | 37| 1| func testLine() { 38| 1| let node = ["x1": "0", 39| 1| "y1": "10", 40| 1| "x2": "50", 41| 1| "y2": "60"] 42| 1| 43| 1| let parsed = try? XMLParser().parseLine(node) 44| 1| XCTAssertEqual(DOM.Line(x1: 0, y1: 10, x2: 50, y2: 60), parsed) 45| 1| } 46| | 47| 1| func testCircle() { 48| 1| let node = ["cx": "0", 49| 1| "cy": "10", 50| 1| "r": "20"] 51| 1| 52| 1| let parsed = try? XMLParser().parseCircle(node) 53| 1| XCTAssertEqual(DOM.Circle(cx: 0, cy: 10, r: 20), parsed) 54| 1| } 55| | 56| 1| func testEllipse() { 57| 1| let node = ["cx": "0", 58| 1| "cy": "10", 59| 1| "rx": "20", 60| 1| "ry": "30"] 61| 1| 62| 1| let parsed = try? XMLParser().parseEllipse(node) 63| 1| XCTAssertEqual(DOM.Ellipse(cx: 0, cy: 10, rx: 20, ry: 30), parsed) 64| 1| } 65| | 66| 1| func testRect() { 67| 1| var node = ["x": "0", 68| 1| "y": "10", 69| 1| "width": "20", 70| 1| "height": "30"] 71| 1| 72| 1| let rect = DOM.Rect(x: 0, y: 10, width: 20, height: 30) 73| 1| XCTAssertEqual(rect, try? XMLParser().parseRect(node)) 74| 1| 75| 1| node["rx"] = "3" 76| 1| node["ry"] = "2" 77| 1| rect.rx = 3 78| 1| rect.ry = 2 79| 1| XCTAssertEqual(rect, try? XMLParser().parseRect(node)) 80| 1| } 81| | 82| 1| func testPolyline() throws { 83| 1| let node = ["points": "0,1 2 3; 4;5;6;7;8 9"] 84| 1| 85| 1| let parsed = try? XMLParser().parsePolyline(node) 86| 1| XCTAssertEqual(DOM.Polyline(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), parsed) 87| 1| } 88| | 89| 1| func testPolygon() { 90| 1| let att = ["points": "0, 1,2,3;4;5;6;7;8 9"] 91| 1| let parsed = try? XMLParser().parsePolygon(att) 92| 1| XCTAssertEqual(DOM.Polygon(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), parsed) 93| 1| } 94| | 95| 1| func testPolygonFillRule() { 96| 1| let att = ["points": "0,1,2,3;4;5;6;7;8 9"] 97| 1| XCTAssertNil((try! XMLParser().parsePolygon(att)).fillRule) 98| 1| 99| 1| let node = XML.Element(name: "polygon") 100| 1| node.attributes["points"] = "0,1,2,3" 101| 1| 102| 1| node.attributes["fill-rule"] = "nonzero" 103| 1| XCTAssertEqual(try XMLParser().parseGraphicsElement(node)!.fillRule, .nonzero) 104| 1| 105| 1| node.attributes["fill-rule"] = "evenodd" 106| 1| XCTAssertEqual(try XMLParser().parseGraphicsElement(node)!.fillRule, .evenodd) 107| 1| 108| 1| node.attributes["fill-rule"] = "asdf" 109| 1| XCTAssertThrowsError(try XMLParser().parseGraphicsElement(node)!.fillRule) 110| 1| } 111| | 112| 1| func testElementParserSkipsErrors() { 113| 1| let error = XMLParser.parseError(for: XMLParser.Error.invalid, 114| 1| parsing: XML.Element(name: "polygon"), 115| 1| with: [.skipInvalidElements]) 116| 1| 117| 1| XCTAssertNil(error) 118| 1| } 119| | 120| 1| func testElementParserErrorsPreserveLineNumbers() { 121| 1| let invalidElement = XMLParser.Error.invalidElement(name: "polygon", 122| 1| error: XMLParser.Error.invalid, 123| 1| line: 100, 124| 1| column: 50) 125| 1| 126| 1| let parseError = XMLParser.parseError(for: invalidElement, 127| 1| parsing: XML.Element(name: "polygon"), 128| 1| with: []) 129| 1| 130| 1| switch parseError! { 131| 1| case let .invalidElement(_, _, line, column): 132| 1| XCTAssertEqual(line, 100) 133| 1| XCTAssertEqual(column, 50) 134| 1| default: 135| 0| XCTFail("not forwarderd") 136| 1| } 137| 1| } 138| | 139| 1| func testElementParserErrorsPreserveLineNumbersFromElement() { 140| 1| let element = XML.Element(name: "polygon") 141| 1| element.parsedLocation = (line: 100, column: 50) 142| 1| 143| 1| let parseError = XMLParser.parseError(for: XMLParser.Error.invalid, 144| 1| parsing: element, 145| 1| with: []) 146| 1| 147| 1| switch parseError! { 148| 1| case let .invalidElement(_, _, line, column): 149| 1| XCTAssertEqual(line, 100) 150| 1| XCTAssertEqual(column, 50) 151| 1| default: 152| 0| XCTFail("not forwarderd") 153| 1| } 154| 1| } 155| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/Parser.XML.GradientTests.swift: 1| |// 2| |// Parser.XML.GradientTests.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 10/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| | 33| |import Foundation 34| | 35| |import XCTest 36| |@testable import SwiftDraw 37| | 38| |final class ParserXMLGradientTests: XCTestCase { 39| | 40| 1| func testParseGradients() throws { 41| 1| let child = XML.Element(name: "child") 42| 1| child.children = [XML.Element.makeMockGradient(), XML.Element.makeMockGradient()] 43| 1| 44| 1| let parent = XML.Element(name: "parent") 45| 1| parent.children = [XML.Element.makeMockGradient(), child] 46| 1| 47| 1| XCTAssertEqual(try XMLParser().parseLinearGradients(child).count, 2) 48| 1| XCTAssertEqual(try XMLParser().parseLinearGradients(parent).count, 3) 49| 1| } 50| | 51| | #if XCODE 52| 1| func testParseFile() throws { 53| 1| 54| 1| let dom = try DOM.SVG.parse(fileNamed: "gradient.svg") 55| 1| 56| 1| XCTAssertEqual(dom.defs.linearGradients.count, 5) 57| 1| XCTAssertNotNil(dom.defs.linearGradients.first(where: { $0.id == "snow" })) 58| 2| XCTAssertNotNil(dom.defs.linearGradients.first(where: { $0.id == "blue" })) 59| 3| XCTAssertNotNil(dom.defs.linearGradients.first(where: { $0.id == "purple" })) 60| 4| XCTAssertNotNil(dom.defs.linearGradients.first(where: { $0.id == "salmon" })) 61| 5| XCTAssertNotNil(dom.defs.linearGradients.first(where: { $0.id == "green" })) 62| 1| 63| 1| XCTAssertGreaterThan(dom.childElements.count, 2) 64| 1| XCTAssertEqual(dom.childElements[0].fill, .url(URL(string: "#snow")!)) 65| 1| XCTAssertEqual(dom.childElements[1].fill, .url(URL(string: "#blue")!)) 66| 1| } 67| | #endif 68| |} 69| | 70| |private extension XML.Element { 71| | 72| 3| static func makeMockGradient() -> XML.Element { 73| 3| return XML.Element(name: "linearGradient", attributes: ["id": "mock"]) 74| 3| } 75| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/Parser.XML.ImageTests.swift: 1| |// 2| |// Parser.XML.ImageTests.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 3/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| | 32| | 33| |import Foundation 34| | 35| |import XCTest 36| |@testable import SwiftDraw 37| | 38| |extension CGImage { 39| 2| static func from(data: Data) -> CGImage? { 40| 2| #if os(iOS) 41| 2| return UIImage(data: data)?.cgImage 42| 2| #elseif os(macOS) 43| 2| guard let image = NSImage(data: data) else { return nil } 44| 2| var rect = NSRect(x: 0, y: 0, width: image.size.width, height: image.size.height) 45| 2| return image.cgImage(forProposedRect: &rect, context: nil, hints: nil) 46| 2| #endif 47| 2| } 48| |} 49| | 50| |final class ParserXMLImageTests: XCTestCase { 51| | 52| 1| func testImage() throws { 53| 1| var node = ["xlink:href": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="] 54| 1| node["width"] = "10" 55| 1| node["height"] = "10" 56| 1| 57| 1| let image = try XMLParser().parseImage(node) 58| 1| 59| 1| XCTAssertTrue(image.href.isDataURL) 60| 1| 61| 1| let decode = image.href.decodedData! 62| 1| 63| 1| XCTAssertEqual(decode.mimeType, "image/png") 64| 1| 65| 1| let cgImage = CGImage.from(data: decode.data) 66| 1| 67| 1| XCTAssertNotNil(cgImage) 68| 1| XCTAssertEqual(cgImage?.width, 5) 69| 1| XCTAssertEqual(cgImage?.height, 5) 70| 1| } 71| | 72| 1| func testImageLineBreaks() throws { 73| 1| let base64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblA " + "\n" + 74| 1| " AAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" 75| 1| 76| 1| let node = ["xlink:href": base64, "width": "10", "height": "10"] 77| 1| 78| 1| let image = try XMLParser().parseImage(node) 79| 1| 80| 1| XCTAssertTrue(image.href.isDataURL) 81| 1| 82| 1| let decode = image.href.decodedData! 83| 1| 84| 1| XCTAssertEqual(decode.mimeType, "image/png") 85| 1| 86| 1| let cgImage = CGImage.from(data: decode.data) 87| 1| 88| 1| XCTAssertNotNil(cgImage) 89| 1| XCTAssertEqual(cgImage?.width, 5) 90| 1| XCTAssertEqual(cgImage?.height, 5) 91| 1| } 92| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/Parser.XML.PathTests.swift: 1| |// 2| |// Parser.XML.PathTests.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 8/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| | 32| |import XCTest 33| |@testable import SwiftDraw 34| | 35| |private typealias Coordinate = DOM.Coordinate 36| |private typealias Segment = DOM.Path.Segment 37| |private typealias CoordinateSpace = DOM.Path.Segment.CoordinateSpace 38| | 39| |final class ParserXMLPathTests: XCTestCase { 40| | 41| 1| func testScanBool() { 42| 1| let scanner = XMLParser.PathScanner(string: "true FALSE 0 1") 43| 1| 44| 1| XCTAssertTrue(try scanner.scanBool()) 45| 1| XCTAssertFalse(try scanner.scanBool()) 46| 1| XCTAssertFalse(try scanner.scanBool()) 47| 1| XCTAssertTrue(try scanner.scanBool()) 48| 1| XCTAssertThrowsError(try scanner.scanBool()) 49| 1| } 50| | 51| 1| func testScanCoordinate() { 52| 1| let scanner = XMLParser.PathScanner(string: "10 20.0") 53| 1| 54| 1| XCTAssertEqual(try scanner.scanCoordinate(), 10.0) 55| 1| XCTAssertEqual(try scanner.scanCoordinate(), 20.0) 56| 1| XCTAssertThrowsError(try scanner.scanCoordinate()) 57| 1| } 58| | 59| 1| func testEquality() { 60| 1| XCTAssertEqual(Segment.move(x: 10, y: 20, space: .relative), 61| 1| move(10, 20, .relative)) 62| 1| 63| 1| XCTAssertNotEqual(Segment.move(x: 20, y: 20, space: .absolute), 64| 1| move(10, 20, .absolute)) 65| 1| 66| 1| XCTAssertNotEqual(Segment.move(x: 10, y: 20, space: .relative), 67| 1| move(10, 20, .absolute)) 68| 1| } 69| | 70| 1| func testMove() { 71| 1| AssertSegmentEquals("M 10 20", move(10, 20, .absolute)) 72| 1| AssertSegmentEquals("m 10 20", move(10, 20, .relative)) 73| 1| AssertSegmentEquals("M10,20", move(10, 20, .absolute)) 74| 1| AssertSegmentEquals("M10;20", move(10, 20, .absolute)) 75| 1| AssertSegmentEquals("M 10; 20 ", move(10, 20, .absolute)) 76| 1| AssertSegmentEquals("M10-20", move(10, -20, .absolute)) 77| 1| 78| 1| AssertSegmentsEquals("M10-20 5 1", [move(10, -20, .absolute), 79| 1| move(5, 1, .absolute)]) 80| 1| } 81| | 82| 1| func testLine() { 83| 1| AssertSegmentEquals("L 10 20", line(10, 20, .absolute)) 84| 1| AssertSegmentEquals("l 10 20", line(10, 20, .relative)) 85| 1| AssertSegmentEquals("L10,20", line(10, 20, .absolute)) 86| 1| AssertSegmentEquals("L10;20", line(10, 20, .absolute)) 87| 1| AssertSegmentEquals(" L 10;20 ", line(10, 20, .absolute)) 88| 1| AssertSegmentEquals("L10-20 ", line(10, -20, .absolute)) 89| 1| 90| 1| AssertSegmentsEquals("L10-20 5 1", [line(10, -20, .absolute), 91| 1| line(5, 1, .absolute)]) 92| 1| } 93| | 94| 1| func testHorizontal() { 95| 1| AssertSegmentEquals("H 10", horizontal(10, .absolute)) 96| 1| AssertSegmentEquals("h 10", horizontal(10, .relative)) 97| 1| AssertSegmentEquals("H10", horizontal(10, .absolute)) 98| 1| AssertSegmentEquals("H10;", horizontal(10, .absolute)) 99| 1| AssertSegmentEquals(" H10 ", horizontal(10, .absolute)) 100| 1| 101| 1| AssertSegmentsEquals("h10 5", [horizontal(10, .relative), 102| 1| horizontal(5, .relative)]) 103| 1| } 104| | 105| 1| func testVerical() { 106| 1| AssertSegmentEquals("V 10", vertical(10, .absolute)) 107| 1| AssertSegmentEquals("v 10", vertical(10, .relative)) 108| 1| AssertSegmentEquals("V10", vertical(10, .absolute)) 109| 1| AssertSegmentEquals("V10;", vertical(10, .absolute)) 110| 1| AssertSegmentEquals(" V10 ", vertical(10, .absolute)) 111| 1| } 112| | 113| 1| func testCubic() { 114| 1| AssertSegmentEquals("C 10 20 30 40 50 60", cubic(10, 20, 30, 40, 50, 60, .absolute)) 115| 1| AssertSegmentEquals("c 10 20 30 40 50 60", cubic(10, 20, 30, 40, 50, 60, .relative)) 116| 1| AssertSegmentEquals("C10,20,30,40,50,60", cubic(10, 20, 30, 40, 50, 60, .absolute)) 117| 1| AssertSegmentEquals("C10;20;30;40;50;60", cubic(10, 20, 30, 40, 50, 60, .absolute)) 118| 1| AssertSegmentEquals(" C10; 20; 30 40; 50; 60", cubic(10, 20, 30, 40, 50, 60, .absolute)) 119| 1| } 120| | 121| 1| func testCubicSmooth() { 122| 1| AssertSegmentEquals("S 10 20 50 60", cubicSmooth(10, 20, 50, 60, .absolute)) 123| 1| AssertSegmentEquals("s 10 20 50 60", cubicSmooth(10, 20, 50, 60, .relative)) 124| 1| AssertSegmentEquals("S10,20,50,60", cubicSmooth(10, 20, 50, 60, .absolute)) 125| 1| AssertSegmentEquals("S10;20;50;60", cubicSmooth(10, 20, 50, 60, .absolute)) 126| 1| AssertSegmentEquals(" S10; 20; 50; 60", cubicSmooth(10, 20, 50, 60, .absolute)) 127| 1| } 128| | 129| 1| func testQuadratic() { 130| 1| AssertSegmentEquals("Q 10 20 50 60", quadratic(10, 20, 50, 60, .absolute)) 131| 1| AssertSegmentEquals("q 10 20 50 60", quadratic(10, 20, 50, 60, .relative)) 132| 1| AssertSegmentEquals("Q10,20,50,60", quadratic(10, 20, 50, 60, .absolute)) 133| 1| AssertSegmentEquals("Q10;20;50;60", quadratic(10, 20, 50, 60, .absolute)) 134| 1| AssertSegmentEquals(" Q10; 20; 50; 60", quadratic(10, 20, 50, 60, .absolute)) 135| 1| } 136| | 137| 1| func testQuadraticSmooth() { 138| 1| AssertSegmentEquals("T 10 20", quadraticSmooth(10, 20, .absolute)) 139| 1| AssertSegmentEquals("t 10 20", quadraticSmooth(10, 20, .relative)) 140| 1| AssertSegmentEquals("T10,20", quadraticSmooth(10, 20, .absolute)) 141| 1| AssertSegmentEquals("T10;20", quadraticSmooth(10, 20, .absolute)) 142| 1| AssertSegmentEquals(" T10; 20;", quadraticSmooth(10, 20, .absolute)) 143| 1| } 144| | 145| 1| func testArc() { 146| 1| AssertSegmentEquals("A 10 20 30 1 0 40 50", arc(10, 20, 30, true, false, 40, 50, .absolute)) 147| 1| AssertSegmentEquals("a 10 20 30 1 0 40 50", arc(10, 20, 30, true, false, 40, 50, .relative)) 148| 1| AssertSegmentEquals("A10,20,30,1,0,40,50", arc(10, 20, 30, true, false, 40, 50, .absolute)) 149| 1| AssertSegmentEquals("A10;20;30;1;0;40;50", arc(10, 20, 30, true, false, 40, 50, .absolute)) 150| 1| AssertSegmentEquals(" A10; 20; 30; 1 0;40 50", arc(10, 20, 30, true, false, 40, 50, .absolute)) 151| 1| } 152| | 153| 1| func testClose() { 154| 1| AssertSegmentEquals("Z", .close) 155| 1| AssertSegmentEquals("z", .close) 156| 1| AssertSegmentEquals(" z ", .close) 157| 1| } 158| | 159| 1| func testPath() { 160| 1| let node = ["d": "M 10 10 h 10 v 10 h -10 v -10"] 161| 1| let parser = XMLParser() 162| 1| 163| 1| let path = try! parser.parsePath(node) 164| 1| 165| 1| XCTAssertEqual(path.segments.count, 5) 166| 1| 167| 1| XCTAssertEqual(path.segments[0], .move(x: 10, y: 10, space: .absolute)) 168| 1| XCTAssertEqual(path.segments[1], .horizontal(x: 10, space: .relative)) 169| 1| XCTAssertEqual(path.segments[2], .vertical(y: 10, space: .relative)) 170| 1| XCTAssertEqual(path.segments[3], .horizontal(x: -10, space: .relative)) 171| 1| XCTAssertEqual(path.segments[4], .vertical(y: -10, space: .relative)) 172| 1| } 173| | 174| 1| func testPathLineBreak() { 175| 1| let node = ["d": "M230 520\n \t\t A 45 45, 0, 1, 0, 275 565 \n \t\t L 275 520 Z"] 176| 1| let parser = XMLParser() 177| 1| 178| 1| let path = try? parser.parsePath(node) 179| 1| 180| 1| XCTAssertEqual(path?.segments.count, 4) 181| 1| } 182| | 183| 1| func testPathLong() throws { 184| 1| 185| 1| let node = ["d": "m10,2h-30v-40zm50,60"] 186| 1| let parser = XMLParser() 187| 1| 188| 1| let path = try! parser.parsePath(node) 189| 1| 190| 1| XCTAssertEqual(path.segments.count, 5) 191| 1| 192| 1| XCTAssertEqual(path.segments[0], .move(x: 10, y: 2.0, space: .relative)) 193| 1| XCTAssertEqual(path.segments[1], .horizontal(x: -30, space: .relative)) 194| 1| XCTAssertEqual(path.segments[2], .vertical(y: -40, space: .relative)) 195| 1| XCTAssertEqual(path.segments[3], .close) 196| 1| XCTAssertEqual(path.segments[4], .move(x: 50, y: 60, space: .relative)) 197| 1| } 198| |} 199| | 200| 50|private func AssertSegmentEquals(_ text: String, _ expected: Segment, file: StaticString = #file, line: UInt = #line) { 201| 50| let parsed = try? XMLParser().parsePathSegments(text) 202| 50| XCTAssertEqual(parsed?.count, 1) 203| 50| XCTAssertEqual(parsed![0], expected, file: file, line: line) 204| 50|} 205| | 206| 3|private func AssertSegmentsEquals(_ text: String, _ expected: [Segment], file: StaticString = #file, line: UInt = #line) { 207| 3| guard let parsed = try? XMLParser().parsePathSegments(text) else { 208| 0| XCTFail("could not parse segments", file: file, line: line) 209| 0| return 210| 3| } 211| 3| XCTAssertEqual(parsed, expected, file: file, line: line) 212| 3|} 213| | 214| | 215| |// helpers to create Segments without labels 216| |// splatting of tuple is no longer supported 217| 11|private func move(_ x: Coordinate, _ y: Coordinate, _ space: CoordinateSpace) -> Segment { 218| 11| return .move(x: x, y: y, space: space) 219| 11|} 220| | 221| 8|private func line(_ x: Coordinate, _ y: Coordinate, _ space: CoordinateSpace) -> Segment { 222| 8| return .line(x: x, y: y, space: space) 223| 8|} 224| | 225| 7|private func horizontal(_ x: Coordinate, _ space: CoordinateSpace) -> Segment { 226| 7| return .horizontal(x: x, space: space) 227| 7|} 228| | 229| 5|private func vertical(_ y: Coordinate, _ space: CoordinateSpace) -> Segment { 230| 5| return .vertical(y: y, space: space) 231| 5|} 232| | 233| |private func cubic(_ x1: Coordinate, _ y1: Coordinate, 234| | _ x2: Coordinate, _ y2: Coordinate, 235| 5| _ x: Coordinate, _ y: Coordinate, _ space: CoordinateSpace) -> Segment { 236| 5| return .cubic(x1: x1, y1: y1, x2: x2, y2: y2, x: x, y: y, space: space) 237| 5|} 238| | 239| |private func cubicSmooth(_ x2: Coordinate, _ y2: Coordinate, 240| 5| _ x: Coordinate, _ y: Coordinate, _ space: CoordinateSpace) -> Segment { 241| 5| return .cubicSmooth(x2: x2, y2: y2, x: x, y: y, space: space) 242| 5|} 243| | 244| |private func quadratic(_ x1: Coordinate, _ y1: Coordinate, 245| 5| _ x: Coordinate, _ y: Coordinate, _ space: CoordinateSpace) -> Segment { 246| 5| return .quadratic(x1: x1, y1: y1, x: x, y: y, space: space) 247| 5|} 248| | 249| 5|private func quadraticSmooth(_ x: Coordinate, _ y: Coordinate, _ space: CoordinateSpace) -> Segment { 250| 5| return .quadraticSmooth(x: x, y: y, space: space) 251| 5|} 252| | 253| |private func arc(_ rx: Coordinate, _ ry: Coordinate, _ rotate: Coordinate, 254| | _ large: Bool, _ sweep: Bool, 255| 5| _ x: Coordinate, _ y: Coordinate, _ space: CoordinateSpace) -> Segment { 256| 5| return .arc(rx: rx, ry: ry, rotate: rotate, 257| 5| large: large, sweep: sweep, 258| 5| x: x, y: y, space: space) 259| 5|} 260| | 261| | 262| | 263| |extension Segment: Equatable { 264| 69| public static func ==(lhs: DOM.Path.Segment, rhs: DOM.Path.Segment) -> Bool { 265| 138| let toString: (Any) -> String = { var text = ""; dump($0, to: &text); return text } 266| 69| return toString(lhs) == toString(rhs) 267| 69| } 268| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/Parser.XML.PatternTests.swift: 1| |// 2| |// Parser.XML.PatternTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |private typealias Coordinate = DOM.Coordinate 36| | 37| |final class ParserXMLPatternTests: XCTestCase { 38| | 39| 1| func testPattern() throws { 40| 1| let pattern = try XMLParser().parsePattern(["id": "p1", "width": "10", "height": "20"]) 41| 1| 42| 1| XCTAssertEqual(pattern.id, "p1") 43| 1| XCTAssertEqual(pattern.width, 10) 44| 1| XCTAssertEqual(pattern.height, 20) 45| 1| 46| 1| XCTAssertThrowsError(try XMLParser().parsePattern(["width": "10", "height": "20"])) 47| 1| XCTAssertThrowsError(try XMLParser().parsePattern(["id": "p1", "height": "20"])) 48| 1| XCTAssertThrowsError(try XMLParser().parsePattern(["id": "p1", "width": "10"])) 49| 1| } 50| | 51| 1| func testPatternUnits() throws { 52| 1| var node = ["id": "p1", "width": "10", "height": "20"] 53| 1| 54| 1| var pattern = try XMLParser().parsePattern(node) 55| 1| XCTAssertNil(pattern.patternUnits) 56| 1| 57| 1| node["patternUnits"] = "userSpaceOnUse" 58| 1| pattern = try XMLParser().parsePattern(node) 59| 1| XCTAssertEqual(pattern.patternUnits, .userSpaceOnUse) 60| 1| 61| 1| node["patternUnits"] = "objectBoundingBox" 62| 1| pattern = try XMLParser().parsePattern(node) 63| 1| XCTAssertEqual(pattern.patternUnits, .objectBoundingBox) 64| 1| 65| 1| node["patternUnits"] = "invalid" 66| 1| XCTAssertThrowsError(try XMLParser().parsePattern(node)) 67| 1| } 68| | 69| 1| func testPatternContentUnits() throws { 70| 1| var node = ["id": "p1", "width": "10", "height": "20"] 71| 1| 72| 1| var pattern = try XMLParser().parsePattern(node) 73| 1| XCTAssertNil(pattern.patternContentUnits) 74| 1| 75| 1| node["patternContentUnits"] = "userSpaceOnUse" 76| 1| pattern = try XMLParser().parsePattern(node) 77| 1| XCTAssertEqual(pattern.patternContentUnits, .userSpaceOnUse) 78| 1| 79| 1| node["patternContentUnits"] = "objectBoundingBox" 80| 1| pattern = try XMLParser().parsePattern(node) 81| 1| XCTAssertEqual(pattern.patternContentUnits, .objectBoundingBox) 82| 1| 83| 1| node["patternContentUnits"] = "invalid" 84| 1| XCTAssertThrowsError(try XMLParser().parsePattern(node)) 85| 1| } 86| | 87| | #if XCODE 88| 1| func testParseFile() throws { 89| 1| 90| 1| let dom = try DOM.SVG.parse(fileNamed: "pattern.svg") 91| 1| 92| 1| XCTAssertEqual(dom.defs.patterns.count, 3) 93| 1| XCTAssertNotNil(dom.defs.patterns.first(where: { $0.id == "checkerboard" })) 94| 2| XCTAssertNotNil(dom.defs.patterns.first(where: { $0.id == "pattern1" })) 95| 3| XCTAssertNotNil(dom.defs.patterns.first(where: { $0.id == "pattern2" })) 96| 1| 97| 1| XCTAssertEqual(dom.childElements.count, 3) 98| 1| // XCTAssertNotNil(dom.childElements[0].fill) 99| 1| // XCTAssertNotNil(dom.childElements[1].fill) 100| 1| // XCTAssertNotNil(dom.childElements[2].fill) 101| 1| } 102| | #endif 103| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/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| |import XCTest 33| |@testable import SwiftDraw 34| | 35| |final class ParserXMLTextTests: XCTestCase { 36| | 37| 1| func testParseText() { 38| 1| XCTAssertEqual(try XMLParser().parseText([:], value: "Simon").value, "Simon") 39| 1| 40| 1| var node = ["x": "10", "y": "25"] 41| 1| XCTAssertNotNil(try? XMLParser().parseText(node, value: "Simon")) 42| 1| 43| 1| node["font-family"] = "Futura" 44| 1| node["font-size"] = "12.5" 45| 1| 46| 1| let expected = DOM.Text(x: 10, y: 25, value: "Simon") 47| 1| expected.fontFamily = "Futura" 48| 1| expected.fontSize = 12.5 49| 1| 50| 1| let parsed = try? XMLParser().parseText(node, value: "Simon") 51| 1| XCTAssertEqual(parsed, expected) 52| 1| } 53| | 54| 1| func testTextNodeParses() throws { 55| 1| let el = XML.Element(name: "text", attributes: [:]) 56| 1| el.innerText = "Simon" 57| 1| 58| 1| let node = try XMLParser().parseText(["x": "1", "y": "1"], element: el) 59| 1| XCTAssertEqual(node?.value, "Simon") 60| 1| } 61| | 62| 1| func testEmptyTextNodeReturnsNil() { 63| 1| let el = XML.Element(name: "text", attributes: [:]) 64| 1| XCTAssertNil(try XMLParser().parseText(["x": "1", "y": "1"], element: el)) 65| 1| el.innerText = " " 66| 1| XCTAssertNil(try XMLParser().parseText(["x": "1", "y": "1"], element: el)) 67| 1| } 68| |} 69| | 70| | /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/Parser.XML.TransformTests.swift: 1| |// 2| |// Parser.XML.TransformTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class ParserTransformTests: XCTestCase { 36| | 37| 1| func testMatrix() { 38| 1| XCTAssertEqual(try XMLParser().parseTransform("matrix(0 1 2 3 4 5)"), 39| 1| [.matrix(a: 0, b: 1, c: 2, d: 3, e: 4, f: 5)]) 40| 1| XCTAssertEqual(try XMLParser().parseTransform("matrix(0,1,2,3,4,5)"), 41| 1| [.matrix(a: 0, b: 1, c: 2, d: 3, e: 4, f: 5)]) 42| 1| XCTAssertEqual(try XMLParser().parseTransform("matrix(1.1,1.2,1.3,1.4,1.5,1.6)"), 43| 1| [.matrix(a: 1.1, b: 1.2, c: 1.3, d: 1.4, e: 1.5, f: 1.6)]) 44| 1| 45| 1| XCTAssertThrowsError(try XMLParser().parseTransform("matrix(0 1 a b 4 5)")) 46| 1| XCTAssertThrowsError(try XMLParser().parseTransform("matrix(0 1 2)")) 47| 1| XCTAssertThrowsError(try XMLParser().parseTransform("matrix(0 1 2 3 4 5")) 48| 1| XCTAssertThrowsError(try XMLParser().parseTransform("matrix 0 1 2 3 4 5)")) 49| 1| } 50| | 51| 1| func testTranslate() { 52| 1| XCTAssertEqual(try XMLParser().parseTransform("translate(5)"), 53| 1| [.translate(tx: 5, ty: 0)]) 54| 1| XCTAssertEqual(try XMLParser().parseTransform("translate(5, 6)"), 55| 1| [.translate(tx: 5, ty: 6)]) 56| 1| XCTAssertEqual(try XMLParser().parseTransform("translate(5 6)"), 57| 1| [.translate(tx: 5, ty: 6)]) 58| 1| XCTAssertEqual(try XMLParser().parseTransform("translate(1.3, 4.5)"), 59| 1| [.translate(tx: 1.3, ty: 4.5)]) 60| 1| 61| 1| XCTAssertThrowsError(try XMLParser().parseTransform("translate(5 a)")) 62| 1| XCTAssertThrowsError(try XMLParser().parseTransform("translate(0 1 2)")) 63| 1| XCTAssertThrowsError(try XMLParser().parseTransform("translate(0 1")) 64| 1| XCTAssertThrowsError(try XMLParser().parseTransform("translate 0 1)")) 65| 1| } 66| | 67| 1| func testScale() { 68| 1| XCTAssertEqual(try XMLParser().parseTransform("scale(5)"), 69| 1| [.scale(sx: 5, sy: 5)]) 70| 1| XCTAssertEqual(try XMLParser().parseTransform("scale(5, 6)"), 71| 1| [.scale(sx: 5, sy: 6)]) 72| 1| XCTAssertEqual(try XMLParser().parseTransform("scale(5 6)"), 73| 1| [.scale(sx: 5, sy: 6)]) 74| 1| XCTAssertEqual(try XMLParser().parseTransform("scale(1.3, 4.5)"), 75| 1| [.scale(sx: 1.3, sy: 4.5)]) 76| 1| 77| 1| XCTAssertThrowsError(try XMLParser().parseTransform("scale(5 a)")) 78| 1| XCTAssertThrowsError(try XMLParser().parseTransform("scale(0 1 2)")) 79| 1| XCTAssertThrowsError(try XMLParser().parseTransform("scale(0 1")) 80| 1| XCTAssertThrowsError(try XMLParser().parseTransform("scale 0 1)")) 81| 1| } 82| | 83| 1| func testRotate() { 84| 1| XCTAssertEqual(try XMLParser().parseTransform("rotate(5)"), 85| 1| [.rotate(angle: 5)]) 86| 1| 87| 1| XCTAssertThrowsError(try XMLParser().parseTransform("rotate(a)")) 88| 1| XCTAssertThrowsError(try XMLParser().parseTransform("rotate()")) 89| 1| XCTAssertThrowsError(try XMLParser().parseTransform("rotate(1")) 90| 1| XCTAssertThrowsError(try XMLParser().parseTransform("rotate 1)")) 91| 1| } 92| | 93| 1| func testRotatePoint() { 94| 1| XCTAssertEqual(try XMLParser().parseTransform("rotate(5, 10, 20)"), 95| 1| [.rotatePoint(angle: 5, cx: 10, cy: 20)]) 96| 1| XCTAssertEqual(try XMLParser().parseTransform("rotate(5 10 20)"), 97| 1| [.rotatePoint(angle: 5, cx: 10, cy: 20)]) 98| 1| 99| 1| XCTAssertThrowsError(try XMLParser().parseTransform("rotate(5 10 a)")) 100| 1| XCTAssertThrowsError(try XMLParser().parseTransform("rotate(5 10)")) 101| 1| XCTAssertThrowsError(try XMLParser().parseTransform("rotate(5 10 20")) 102| 1| XCTAssertThrowsError(try XMLParser().parseTransform("rotate 5 10 20)")) 103| 1| } 104| | 105| 1| func testSkewX() { 106| 1| XCTAssertEqual(try XMLParser().parseTransform("skewX(5)"), 107| 1| [.skewX(angle: 5)]) 108| 1| XCTAssertEqual(try XMLParser().parseTransform("skewX(6.7)"), 109| 1| [.skewX(angle: 6.7)]) 110| 1| XCTAssertEqual(try XMLParser().parseTransform("skewX(0)"), 111| 1| [.skewX(angle: 0)]) 112| 1| 113| 1| XCTAssertThrowsError(try XMLParser().parseTransform("skewX(a)")) 114| 1| XCTAssertThrowsError(try XMLParser().parseTransform("skewX()")) 115| 1| XCTAssertThrowsError(try XMLParser().parseTransform("skewX(1")) 116| 1| XCTAssertThrowsError(try XMLParser().parseTransform("skewX 1)")) 117| 1| } 118| | 119| 1| func testSkewY() { 120| 1| XCTAssertEqual(try XMLParser().parseTransform("skewY(5)"), 121| 1| [.skewY(angle: 5)]) 122| 1| XCTAssertEqual(try XMLParser().parseTransform("skewY(6.7)"), 123| 1| [.skewY(angle: 6.7)]) 124| 1| XCTAssertEqual(try XMLParser().parseTransform("skewY(0)"), 125| 1| [.skewY(angle: 0)]) 126| 1| 127| 1| XCTAssertThrowsError(try XMLParser().parseTransform("skewY(a)")) 128| 1| XCTAssertThrowsError(try XMLParser().parseTransform("skewY()")) 129| 1| XCTAssertThrowsError(try XMLParser().parseTransform("skewY(1")) 130| 1| XCTAssertThrowsError(try XMLParser().parseTransform("skewY 1)")) 131| 1| } 132| | 133| 1| func testTransform() { 134| 1| XCTAssertEqual(try XMLParser().parseTransform("scale(2) translate(4) scale(5, 5) "), 135| 1| [.scale(sx: 2, sy: 2), 136| 1| .translate(tx: 4, ty: 0), 137| 1| .scale(sx: 5, sy: 5)]) 138| 1| } 139| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/Renderer.CoreGraphicsTypesTests.swift: 1| |// 2| |// Renderer.CoreGraphicsTypesTests.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 4/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| |import CoreGraphics 31| |@testable import SwiftDraw 32| | 33| |final class RendererCoreGraphicsTypesTests: XCTestCase { 34| | 35| | typealias Float = LayerTree.Float 36| | typealias Point = LayerTree.Point 37| | typealias Rect = LayerTree.Rect 38| | typealias Size = LayerTree.Size 39| | 40| 1| func testFloat() { 41| 1| XCTAssertEqual(CGProvider().createFloat(from: Float(10)), 10.0) 42| 1| XCTAssertEqual(CGProvider().createFloat(from: Float(200.5)), 200.5) 43| 1| } 44| | 45| 1| func testPoint() { 46| 1| XCTAssertEqual(CGProvider().createPoint(from: LayerTree.Point(10, 20)), CGPoint(x: 10, y: 20)) 47| 1| XCTAssertEqual(CGProvider().createPoint(from: LayerTree.Point(200.5, 300.5)), CGPoint(x: 200.5, y: 300.5)) 48| 1| } 49| | 50| 1| func testSize() { 51| 1| XCTAssertEqual(CGProvider().createSize(from: LayerTree.Size(10, 20)), CGSize(width: 10, height: 20)) 52| 1| XCTAssertEqual(CGProvider().createSize(from: LayerTree.Size(200.5, 300.5)), CGSize(width: 200.5, height: 300.5)) 53| 1| } 54| | 55| 1| func testRect() { 56| 1| let r1 = LayerTree.Rect(x: 10, y: 20, width: 30, height: 40) 57| 1| XCTAssertEqual(CGProvider().createRect(from: r1), 58| 1| CGRect(x: 10, y: 20, width: 30, height: 40)) 59| 1| 60| 1| let r2 = LayerTree.Rect(x: 200.5, y: 300.5, width: 400.5, height: 500.5) 61| 1| XCTAssertEqual(CGProvider().createRect(from: r2), 62| 1| CGRect(x: 200.5, y: 300.5, width: 400.5, height: 500.5)) 63| 1| } 64| | 65| 1| func testColor() { 66| 1| let c = CGProvider().createColor(from: .rgba(r: 0.1, g: 0.2, b: 0.3, a: 0.4)) 67| 1| //CGFloat(Float(xx)) accounts for floating point margin of error 68| 1| let reference = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [CGFloat(Float(0.1)), CGFloat(Float(0.2)), CGFloat(Float(0.3)), CGFloat(Float(0.4))])! 69| 1| XCTAssertEqual(c.components!, reference.components!) 70| 1| 71| 1| let clear = CGProvider().createColor(from: .none) 72| 1| //CGFloat(Float(xx)) accounts for floating point margin of error 73| 1| let clearReference = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0, 0, 0, 0])! 74| 1| XCTAssertEqual(clear.components!, clearReference.components!) 75| 1| } 76| | 77| 1| func testTransform() { 78| 1| let m = LayerTree.Transform.Matrix(a: 10, b: 20, c: 30, d: 40, tx: 50, ty: 60) 79| 1| let expected = CGAffineTransform(a: 10, b: 20, c: 30, d: 40, tx: 50, ty: 60) 80| 1| XCTAssertEqual(CGProvider().createTransform(from: m), expected) 81| 1| } 82| | 83| 1| func testBlendMode() { 84| 1| XCTAssertEqual(CGProvider().createBlendMode(from: .normal), .normal) 85| 1| XCTAssertEqual(CGProvider().createBlendMode(from: .copy), .copy) 86| 1| XCTAssertEqual(CGProvider().createBlendMode(from: .sourceIn), .sourceIn) 87| 1| } 88| | 89| 1| func testFillRule() { 90| 1| XCTAssertEqual(CGProvider().createFillRule(from: .nonzero), .winding) 91| 1| XCTAssertEqual(CGProvider().createFillRule(from: .evenodd), .evenOdd) 92| 1| } 93| | 94| 1| func testLineCap() { 95| 1| XCTAssertEqual(CGProvider().createLineCap(from: .butt), .butt) 96| 1| XCTAssertEqual(CGProvider().createLineCap(from: .round), .round) 97| 1| XCTAssertEqual(CGProvider().createLineCap(from: .square), .square) 98| 1| } 99| | 100| 1| func testLineJoin() { 101| 1| XCTAssertEqual(CGProvider().createLineJoin(from: .miter), .miter) 102| 1| XCTAssertEqual(CGProvider().createLineJoin(from: .round), .round) 103| 1| XCTAssertEqual(CGProvider().createLineJoin(from: .bevel), .bevel) 104| 1| } 105| | 106| 1| func testShapeLine() { 107| 1| let path = CGProvider().createPath(from: .line(between: [.zero, Point(10, 20), Point(30, 40)])) 108| 1| let segments: [CGPath.Segment] = [.move(CGPoint(0, 0)), .line(CGPoint(10, 20)), .line(CGPoint(30, 40))] 109| 1| XCTAssertEqual(path.segments(), segments) 110| 1| } 111| | 112| 1| func testShapeRect() { 113| 1| let path = CGProvider().createPath(from: .rect(within: Rect(x: 10, y: 20, width: 30, height: 40), 114| 1| radii: Size(2, 4))) 115| 1| 116| 1| let expected = CGPath(roundedRect: CGRect(x:10, y: 20, width: 30, height: 40), 117| 1| cornerWidth: 2.0, 118| 1| cornerHeight: 4.0, 119| 1| transform: nil) 120| 1| 121| 1| XCTAssertEqual(path, expected) 122| 1| } 123| | 124| 1| func testShapeEllipse() { 125| 1| let path = CGProvider().createPath(from: .ellipse(within: Rect(x: 10, y: 20, width: 30, height: 40))) 126| 1| let expected = CGPath(ellipseIn: CGRect(x:10, y: 20, width: 30, height: 40), transform: nil) 127| 1| XCTAssertEqual(path, expected) 128| 1| } 129| | 130| 1| func testShapePolygon() { 131| 1| let path = CGProvider().createPath(from: .polygon(between: 132| 1| [.zero, 133| 1| Point(0, 20), 134| 1| Point(20, 20), 135| 1| Point(20, 0)])) 136| 1| let expected = CGMutablePath() 137| 1| expected.move(to: CGPoint(x: 0, y: 0)) 138| 1| expected.addLine(to: CGPoint(x: 0, y: 20)) 139| 1| expected.addLine(to: CGPoint(x: 20, y: 20)) 140| 1| expected.addLine(to: CGPoint(x: 20, y: 0)) 141| 1| expected.closeSubpath() 142| 1| 143| 1| XCTAssertEqual(path.segments(), expected.segments()) 144| 1| } 145| | 146| 1| func testShapePath() { 147| 1| let model = LayerTree.Path() 148| 1| model.segments.append(.move(to: Point(0, 0))) 149| 1| model.segments.append(.line(to: Point(100, 100))) 150| 1| model.segments.append(.cubic(to: Point(100, 0), control1: Point(50, 75), control2: Point(150, 25))) 151| 1| model.segments.append(.close) 152| 1| 153| 1| let expected = CGMutablePath() 154| 1| expected.move(to: CGPoint(x: 0, y: 0)) 155| 1| expected.addLine(to: CGPoint(x: 100, y: 100)) 156| 1| expected.addCurve(to: CGPoint(x: 100, y: 0), control1: CGPoint(x: 50, y: 75), control2: CGPoint(x: 150, y: 25)) 157| 1| expected.closeSubpath() 158| 1| 159| 1| let path = CGProvider().createPath(from: .path(model)) 160| 1| XCTAssertEqual(path, expected) 161| 1| } 162| | 163| 1| func testTextPath() { 164| 1| XCTAssertNotNil(CGProvider().createPath(from: "Hi", at: .zero, with: .normal)) 165| 1| } 166| | 167| 1| func testSubpath() { 168| 1| let p1 = CGMutablePath() 169| 1| p1.move(to: CGPoint(x: 0, y: 0)) 170| 1| p1.addLine(to: CGPoint(x: 100, y: 100)) 171| 1| p1.closeSubpath() 172| 1| 173| 1| let p2 = CGMutablePath() 174| 1| p2.move(to: CGPoint(x: 100, y: 0)) 175| 1| p2.addLine(to: CGPoint(x: 200, y: 200)) 176| 1| p2.closeSubpath() 177| 1| 178| 1| let segments = CGProvider().createPath(from: [p1, p2]).segments() 179| 1| XCTAssertEqual(segments, [.move(CGPoint(x: 0, y: 0)), 180| 1| .line(CGPoint(x: 100, y: 100)), 181| 1| .close, 182| 1| .move(CGPoint(x: 100, y: 0)), 183| 1| .line(CGPoint(x: 200, y: 200)), 184| 1| .close]) 185| 1| } 186| | 187| | //TODO: verify these within type provider 188| | 189| | // func createImage(from image: LayerTree.Image) -> CGImage? 190| | 191| |} 192| | 193| |private extension CGPoint { 194| 0| init(_ x: CGFloat, _ y: CGFloat) { 195| 0| self.init(x: x, y: y) 196| 0| } 197| 3| init(_ x: Int, _ y: Int) { 198| 3| self.init(x: x, y: y) 199| 3| } 200| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/Renderer.LayerTreeProviderTests.swift: 1| |// 2| |// Renderer.LayerTreeProviderTests.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 27/11/18. 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| |import CoreGraphics 31| |@testable import SwiftDraw 32| | 33| |final class RendererLayerTreeProviderTests: XCTestCase { 34| | 35| | private typealias Point = LayerTree.Point 36| | private typealias Rect = LayerTree.Rect 37| | private typealias Size = LayerTree.Size 38| | private typealias Transform = LayerTree.Transform 39| | private typealias Matrix = LayerTree.Transform.Matrix 40| | 41| 1| func testFloat() { 42| 1| let float = LayerTreeProvider().createFloat(from: 10) 43| 1| XCTAssertEqual(float, 10) 44| 1| } 45| | 46| 1| func testPoint() { 47| 1| let point = LayerTreeProvider().createPoint(from: Point(10, 20)) 48| 1| XCTAssertEqual(point, Point(10, 20)) 49| 1| } 50| | 51| 1| func testSize() { 52| 1| let size = LayerTreeProvider().createSize(from: Size(10, 20)) 53| 1| XCTAssertEqual(size, Size(10, 20)) 54| 1| } 55| | 56| 1| func testRect() { 57| 1| let rect = Rect(x: 0, y: 10, width: 20, height: 30) 58| 1| let converted = LayerTreeProvider().createRect(from: rect) 59| 1| XCTAssertEqual(converted, rect) 60| 1| } 61| | 62| 1| func testShape() { 63| 1| let shape = LayerTree.Shape.rect(within: Rect(x: 0, y: 10, width: 20, height: 30), 64| 1| radii: .zero) 65| 1| 66| 1| let converted = LayerTreeProvider().createPath(from: shape) 67| 1| XCTAssertEqual(converted, [shape]) 68| 1| } 69| | 70| 1| func testSubPaths() { 71| 1| let rect = LayerTree.Shape.rect(within: Rect(x: 0, y: 10, width: 20, height: 30), 72| 1| radii: .zero) 73| 1| let line = LayerTree.Shape.line(between: [.zero, Point(0, 100)]) 74| 1| 75| 1| let p1 = LayerTreeProvider().createPath(from: rect) 76| 1| let p2 = LayerTreeProvider().createPath(from: line) 77| 1| let converted = LayerTreeProvider().createPath(from: [p1, p2]) 78| 1| XCTAssertEqual(converted, [rect, line]) 79| 1| } 80| | 81| 1| func testTextPathIsUnsupported() { 82| 1| XCTAssertNil(LayerTreeProvider().createPath(from: "", at: .zero, with: .normal)) 83| 1| } 84| | 85| 1| func testColor() { 86| 1| let color = LayerTreeProvider().createColor(from: .black) 87| 1| XCTAssertEqual(color, .black) 88| 1| } 89| | 90| 1| func testBlendMode() { 91| 1| let blend = LayerTreeProvider().createBlendMode(from: .sourceIn) 92| 1| XCTAssertEqual(blend, .sourceIn) 93| 1| } 94| | 95| 1| func testTransform() { 96| 1| let matrix = Transform.identity.toMatrix() 97| 1| let transfrom = LayerTreeProvider().createTransform(from: matrix) 98| 1| XCTAssertEqual(transfrom, .identity) 99| 1| } 100| | 101| 1| func testFillRule() { 102| 1| let fill = LayerTreeProvider().createFillRule(from: .nonzero) 103| 1| XCTAssertEqual(fill, .nonzero) 104| 1| } 105| | 106| 1| func testLineCap() { 107| 1| let cap = LayerTreeProvider().createLineCap(from: .butt) 108| 1| XCTAssertEqual(cap, .butt) 109| 1| } 110| | 111| 1| func testLineJoin() { 112| 1| let join = LayerTreeProvider().createLineJoin(from: .bevel) 113| 1| XCTAssertEqual(join, .bevel) 114| 1| } 115| | 116| 1| func testImage() { 117| 1| let image = LayerTreeProvider().createImage(from: .mock) 118| 1| XCTAssertEqual(image, .mock) 119| 1| } 120| |} 121| | 122| |private extension LayerTree.Image { 123| | 124| 2| static var mock: LayerTree.Image { 125| 2| return .png(data: Data()) 126| 2| } 127| |} 128| | /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/RendererTests.swift: 1| |// 2| |// CGRenderer.PathTests.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| |import XCTest 33| |@testable import SwiftDraw 34| | 35| |final class RendererTests: XCTestCase { 36| | 37| 1| func testPerformCommands() { 38| 1| let renderer = MockRenderer() 39| 1| renderer.perform([ 40| 1| .pushState, 41| 1| .popState, 42| 1| .pushTransparencyLayer, 43| 1| .popTransparencyLayer, 44| 1| .concatenate(transform: .identity), 45| 1| .translate(tx: 10, ty: 20), 46| 1| .scale(sx: 1, sy: 2), 47| 1| .rotate(angle: 10), 48| 1| .setFill(color: .none), 49| 1| .setFillPattern(.mock), 50| 1| .setStroke(color: .none), 51| 1| .setLine(width: 10), 52| 1| .setLineCap(.butt), 53| 1| .setLineJoin(.bevel), 54| 1| .setLineMiter(limit: 10), 55| 1| .setClip(path: .mock), 56| 1| .setClipMask([], frame: .zero), 57| 1| .fill(.mock, rule: .nonzero), 58| 1| .stroke(.mock), 59| 1| .setAlpha(0.5), 60| 1| .setBlend(mode: .sourceIn), 61| 1| .draw(image: .mock), 62| 1| .drawGradient(.mock, from: .zero, to: .zero), 63| 1| ]) 64| 1| 65| 1| XCTAssertEqual(renderer.operations, [ 66| 1| "pushState", 67| 1| "popState", 68| 1| "pushTransparencyLayer", 69| 1| "popTransparencyLayer", 70| 1| "concatenateTransform", 71| 1| "translate", 72| 1| "scale", 73| 1| "rotate", 74| 1| "setFillColor", 75| 1| "setFillPattern", 76| 1| "setStrokeColor", 77| 1| "setLineWidth", 78| 1| "setLineCap", 79| 1| "setLineJoin", 80| 1| "setLineMiterLimit", 81| 1| "setClip", 82| 1| "setClipMask", 83| 1| "fillPath", 84| 1| "strokePath", 85| 1| "setAlpha", 86| 1| "setBlendMode", 87| 1| "drawImage", 88| 1| "drawGradient" 89| 1| ]) 90| 1| } 91| |} 92| | 93| |private extension LayerTree.Shape { 94| | 95| 3| static var mock: LayerTree.Shape { 96| 3| return .line(between: []) 97| 3| } 98| |} 99| | 100| |private extension Array where Element == LayerTree.Shape { 101| | 102| 3| static var mock: [LayerTree.Shape] { 103| 3| return [.mock] 104| 3| } 105| |} 106| | 107| |private extension LayerTree.Image { 108| | 109| 1| static var mock: LayerTree.Image { 110| 1| return .png(data: Data()) 111| 1| } 112| |} 113| | 114| |private extension LayerTree.Pattern { 115| | 116| 1| static var mock: LayerTree.Pattern { 117| 1| return LayerTree.Pattern(frame: .zero) 118| 1| } 119| |} 120| | 121| |private extension LayerTree.Gradient { 122| | 123| 1| static var mock: LayerTree.Gradient { 124| 1| return LayerTree.Gradient(start: .zero, end: .zero) 125| 1| } 126| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/StackTests.swift: 1| |// 2| |// StackTests.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| |import XCTest 33| |@testable import SwiftDraw 34| | 35| |final class StackTests: XCTestCase { 36| | 37| 1| func testInit() { 38| 1| let stack = Stack(root: 10) 39| 1| 40| 1| XCTAssertEqual(stack.root, 10) 41| 1| XCTAssertEqual(stack.top, 10) 42| 1| XCTAssertTrue(stack.storage.isEmpty) 43| 1| } 44| | 45| 1| func testPush() { 46| 1| var stack = Stack(root: 10) 47| 1| 48| 1| XCTAssertTrue(stack.storage.isEmpty) 49| 1| 50| 1| stack.push(20) 51| 1| XCTAssertEqual(stack.top, 20) 52| 1| XCTAssertFalse(stack.storage.isEmpty) 53| 1| 54| 1| stack.push(30) 55| 1| XCTAssertEqual(stack.top, 30) 56| 1| stack.push(40) 57| 1| XCTAssertEqual(stack.top, 40) 58| 1| stack.push(50) 59| 1| XCTAssertEqual(stack.top, 50) 60| 1| XCTAssertEqual(stack.storage, [20, 30, 40, 50]) 61| 1| } 62| | 63| 1| func testPop() { 64| 1| var stack = Stack(root: 10) 65| 1| 66| 1| //cannot pop off root 67| 1| XCTAssertFalse(stack.pop()) 68| 1| 69| 1| stack.push(20) 70| 1| stack.push(30) 71| 1| stack.push(40) 72| 1| stack.push(50) 73| 1| XCTAssertEqual(stack.top, 50) 74| 1| XCTAssertTrue(stack.pop()) 75| 1| XCTAssertEqual(stack.top, 40) 76| 1| XCTAssertTrue(stack.pop()) 77| 1| XCTAssertEqual(stack.top, 30) 78| 1| XCTAssertTrue(stack.pop()) 79| 1| XCTAssertEqual(stack.top, 20) 80| 1| XCTAssertTrue(stack.pop()) 81| 1| XCTAssertEqual(stack.top, 10) 82| 1| 83| 1| //cannot pop off root 84| 1| XCTAssertFalse(stack.pop()) 85| 1| } 86| | 87| 1| func testMutation() { 88| 1| 89| 1| var stack = Stack(root: 10) 90| 1| 91| 1| XCTAssertEqual(stack.top, 10) 92| 1| stack.top = 50 93| 1| XCTAssertEqual(stack.top, 50) 94| 1| XCTAssertEqual(stack.root, 50) 95| 1| XCTAssertTrue(stack.storage.isEmpty) 96| 1| 97| 1| stack.push(100) 98| 1| stack.top = 200 99| 1| 100| 1| XCTAssertEqual(stack.top, 200) 101| 1| XCTAssertEqual(stack.root, 50) 102| 1| XCTAssertEqual(stack.storage, [200]) 103| 1| 104| 1| stack.push(500) 105| 1| stack.top = 600 106| 1| XCTAssertEqual(stack.top, 600) 107| 1| XCTAssertEqual(stack.root, 50) 108| 1| XCTAssertEqual(stack.storage, [200, 600]) 109| 1| 110| 1| stack.pop() 111| 1| stack.top = 33 112| 1| XCTAssertEqual(stack.top, 33) 113| 1| XCTAssertEqual(stack.root, 50) 114| 1| XCTAssertEqual(stack.storage, [33]) 115| 1| 116| 1| stack.pop() 117| 1| stack.top = 1 118| 1| XCTAssertEqual(stack.top, 1) 119| 1| XCTAssertEqual(stack.root, 1) 120| 1| XCTAssertTrue(stack.storage.isEmpty) 121| 1| 122| 1| } 123| | 124| | 125| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class StyleTests: XCTestCase { 36| | 37| 1| func testStyle() { 38| 1| XCTAssertEqual(try XMLParser().parseStyleAttributes("selector: hi;"), 39| 1| ["selector": "hi"]) 40| 1| XCTAssertEqual(try XMLParser().parseStyleAttributes("selector: hi"), 41| 1| ["selector": "hi"]) 42| 1| XCTAssertEqual(try XMLParser().parseStyleAttributes("selector: hi "), 43| 1| ["selector": "hi"]) 44| 1| XCTAssertEqual(try XMLParser().parseStyleAttributes(" trans-form : rotate(4)"), 45| 1| ["trans-form": "rotate(4)"]) 46| 1| 47| 1| XCTAssertThrowsError(try XMLParser().parseStyleAttributes("selector")) 48| 1| XCTAssertThrowsError(try XMLParser().parseStyleAttributes(": hmm")) 49| 1| } 50| | 51| 1| func testStyles() throws { 52| 1| let e = XML.Element(name: "line") 53| 1| e.attributes["x"] = "5" 54| 1| e.attributes["y"] = "5" 55| 1| e.attributes["stroke-color"] = "black" 56| 1| e.attributes["style"] = "fill: red; x: 20" 57| 1| 58| 1| //Style attributes should override any XML.Element attribute 59| 1| let att = try XMLParser().parseAttributes(e) 60| 1| 61| 1| XCTAssertEqual(try att.parseCoordinate("x"), 20.0) 62| 1| XCTAssertEqual(try att.parseCoordinate("y"), 5.0) 63| 1| XCTAssertEqual(try att.parseColor("stroke-color"), .keyword(.black)) 64| 1| XCTAssertEqual(try att.parseColor("fill"), .keyword(.red)) 65| 1| } 66| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class URLTests: XCTestCase { 36| | 37| 1| func testDecodedData() { 38| 1| let url = URL(maybeData: "data:image/png;base64,f00d") 39| 1| XCTAssertEqual(url?.decodedData?.mimeType, "image/png") 40| 1| XCTAssertEqual(url?.decodedData?.data.base64EncodedString(), "f00d") 41| 1| 42| 1| XCTAssertNil(URL(maybeData: "data:;base64,f00d")?.decodedData) 43| 1| XCTAssertNil(URL(maybeData: "data:image/png;bas,f00d")?.decodedData) 44| 1| XCTAssertNil(URL(maybeData: "data:image/png;base64")?.decodedData) 45| 1| XCTAssertNil(URL(maybeData: "data:image/png;base64,")?.decodedData) 46| 1| } 47| | 48| 1| func testDataURL() { 49| 1| XCTAssertTrue(URL(maybeData: "data:image/png;base64,f00d")!.isDataURL) 50| 1| XCTAssertTrue(URL(maybeData: "data:f00d")!.isDataURL) 51| 1| XCTAssertFalse(URL(maybeData: "#identifier")!.isDataURL) 52| 1| XCTAssertFalse(URL(maybeData: "data")!.isDataURL) 53| 1| XCTAssertFalse(URL(maybeData: "www.google.com")!.isDataURL) 54| 1| } 55| | 56| 1| func testDecodedDataLineBreak() { 57| 1| let url = URL(maybeData: "data:image/png;base64,8badf00d\n\t 8badf00d") 58| 1| XCTAssertEqual(url?.decodedData?.mimeType, "image/png") 59| 1| XCTAssertEqual(url?.decodedData?.data.base64EncodedString(), "8badf00d8badf00d") 60| 1| } 61| | 62| |} 63| | /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class UseTests: XCTestCase { 36| | 37| 1| func testUse() throws { 38| 1| var node = ["xlink:href": "#line2", "href": "#line1"] 39| 1| 40| 1| var parsed = try XMLParser().parseUse(node) 41| 1| XCTAssertEqual(parsed.href.fragment, "line2") 42| 1| XCTAssertNil(parsed.x) 43| 1| XCTAssertNil(parsed.y) 44| 1| 45| 1| node["x"] = "20" 46| 1| node["y"] = "30" 47| 1| 48| 1| parsed = try XMLParser().parseUse(node) 49| 1| XCTAssertEqual(parsed.href.fragment, "line2") 50| 1| XCTAssertEqual(parsed.x, 20) 51| 1| XCTAssertEqual(parsed.y, 30) 52| 1| } 53| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/ValueParserTests.swift: 1| |// 2| |// ValueParserTests.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 6/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| | 32| | 33| |import XCTest 34| |@testable import SwiftDraw 35| | 36| |final class ValueParserTests: XCTestCase { 37| | 38| 11| var parser = XMLParser.ValueParser() 39| | 40| 1| func testFloat() { 41| 1| XCTAssertEqual(try parser.parseFloat("10"), 10) 42| 1| XCTAssertEqual(try parser.parseFloat("10.0"), 10.0) 43| 1| 44| 1| XCTAssertThrowsError(try parser.parseFloat("")) 45| 1| //XCTAssertThrowsError(try parser.parseFloat("10a")) 46| 1| } 47| | 48| 1| func testFloats() { 49| 1| XCTAssertEqual(try parser.parseFloats("10 20 30.5"), [10, 20, 30.5]) 50| 1| XCTAssertEqual(try parser.parseFloats("10.0"), [10.0]) 51| 1| XCTAssertEqual(try parser.parseFloats("5 10 1 5"), [5, 10, 1, 5]) 52| 1| XCTAssertEqual(try parser.parseFloats(" 1, 2.5, 3.5 "), [1, 2.5, 3.5]) 53| 1| XCTAssertEqual(try parser.parseFloats(" "), []) 54| 1| XCTAssertEqual(try parser.parseFloats(""), []) 55| 1| 56| 1| //XCTAssertThrowsError(try parser.parseFloats("")) 57| 1| //XCTAssertThrowsError(try parser.parseFloat("10a")) 58| 1| } 59| | 60| 1| func testPercentage() { 61| 1| XCTAssertEqual(try parser.parsePercentage("0"), 0) 62| 1| XCTAssertEqual(try parser.parsePercentage("1"), 1) 63| 1| XCTAssertEqual(try parser.parsePercentage("0.45"), 0.45) 64| 1| XCTAssertEqual(try parser.parsePercentage("0.0%"), 0) 65| 1| XCTAssertEqual(try parser.parsePercentage("100%"), 1) 66| 1| XCTAssertEqual(try parser.parsePercentage("55%"), 0.55) 67| 1| XCTAssertEqual(try parser.parsePercentage("10.25%"), 0.1025) 68| 1| 69| 1| XCTAssertThrowsError(try parser.parsePercentage("100")) 70| 1| XCTAssertThrowsError(try parser.parsePercentage("asd")) 71| 1| XCTAssertThrowsError(try parser.parsePercentage(" ")) 72| 1| //XCTAssertThrowsError(try parser.parseFloat("10a")) 73| 1| } 74| | 75| 1| func testCoordinate() { 76| 1| XCTAssertEqual(try parser.parseCoordinate("0"), 0) 77| 1| XCTAssertEqual(try parser.parseCoordinate("0.0"), 0) 78| 1| XCTAssertEqual(try parser.parseCoordinate("100"), 100) 79| 1| XCTAssertEqual(try parser.parseCoordinate("25.0"), 25.0) 80| 1| XCTAssertEqual(try parser.parseCoordinate("-25.0"), -25.0) 81| 1| 82| 1| XCTAssertThrowsError(try parser.parseCoordinate("asd")) 83| 1| XCTAssertThrowsError(try parser.parseCoordinate(" ")) 84| 1| } 85| | 86| 1| func testLength() { 87| 1| XCTAssertEqual(try parser.parseLength("0"), 0) 88| 1| XCTAssertEqual(try parser.parseLength("100"), 100) 89| 1| XCTAssertEqual(try parser.parseLength("25"), 25) 90| 1| XCTAssertEqual(try parser.parseLength("1.3"), 1) //should error? 91| 1| 92| 1| XCTAssertThrowsError(try parser.parseLength("asd")) 93| 1| XCTAssertThrowsError(try parser.parseLength(" ")) 94| 1| XCTAssertThrowsError(try parser.parseLength("-25")) 95| 1| } 96| | 97| 1| func testBool() { 98| 1| XCTAssertEqual(try parser.parseBool("false"), false) 99| 1| XCTAssertEqual(try parser.parseBool("FALSE"), false) 100| 1| XCTAssertEqual(try parser.parseBool("true"), true) 101| 1| XCTAssertEqual(try parser.parseBool("TRUE"), true) 102| 1| XCTAssertEqual(try parser.parseBool("1"), true) 103| 1| XCTAssertEqual(try parser.parseBool("0"), false) 104| 1| 105| 1| XCTAssertThrowsError(try parser.parseBool("asd")) 106| 1| XCTAssertThrowsError(try parser.parseBool("yes")) 107| 1| } 108| | 109| 1| func testFill() { 110| 1| XCTAssertEqual(try parser.parseFill("none"), .color(.none)) 111| 1| XCTAssertEqual(try parser.parseFill("black"), .color(.keyword(.black))) 112| 1| XCTAssertEqual(try parser.parseFill("red"), .color(.keyword(.red))) 113| 1| 114| 1| XCTAssertEqual(try parser.parseFill("rgb(10,20,30)"), .color(.rgbi(10, 20, 30))) 115| 1| XCTAssertEqual(try parser.parseFill("rgb(10%,20%,100%)"), .color(.rgbf(0.1, 0.2, 1.0))) 116| 1| XCTAssertEqual(try parser.parseFill("#AAFF00"), .color(.hex(170, 255, 0))) 117| 1| 118| 1| XCTAssertEqual(try parser.parseFill("url(#test)"), .url(URL(string: "#test")!)) 119| 1| 120| 1| XCTAssertThrowsError(try parser.parseFill("Ns ")) 121| 1| XCTAssertThrowsError(try parser.parseFill("d")) 122| 1| XCTAssertThrowsError(try parser.parseFill("url()")) 123| 1| //XCTAssertThrowsError(try parser.parseFill("url(asdf")) 124| 1| } 125| | 126| 1| func testUrl() { 127| 1| XCTAssertEqual(try parser.parseUrl("#testingId").fragment, "testingId") 128| 1| XCTAssertEqual(try parser.parseUrl("http://www.google.com").host, "www.google.com") 129| 1| 130| 1| //XCTAssertThrowsError(try parser.parseUrl("www.google.com")) 131| 1| //XCTAssertThrowsError(try parser.parseUrl("sd")) 132| 1| } 133| | 134| 1| func testUrlSelector() { 135| 1| XCTAssertEqual(try parser.parseUrlSelector("url(#testingId)").fragment, "testingId") 136| 1| XCTAssertEqual(try parser.parseUrlSelector("url(http://www.google.com)").host, "www.google.com") 137| 1| 138| 1| XCTAssertThrowsError(try parser.parseUrlSelector("url(#testingId) other")) 139| 1| } 140| | 141| 1| func testPoints() { 142| 1| XCTAssertEqual(try parser.parsePoints("0 1 2 3"), [DOM.Point(0, 1), DOM.Point(2, 3)]) 143| 1| XCTAssertEqual(try parser.parsePoints("0,1 2,3"), [DOM.Point(0, 1), DOM.Point(2, 3)]) 144| 1| XCTAssertEqual(try parser.parsePoints("0 1.5 1e4 2.4"), [DOM.Point(0, 1.5), DOM.Point(1e4, 2.4)]) 145| 1| // XCTAssertEqual(try parser.parsePoints("0 1 2 3 5.0 6.5"), [0, 1 ,2]) 146| 1| } 147| | 148| 1| func testRaw() { 149| 1| XCTAssertEqual(try parser.parseRaw("evenodd"), DOM.FillRule.evenodd) 150| 1| XCTAssertEqual(try parser.parseRaw("round"), DOM.LineCap.round) 151| 1| XCTAssertEqual(try parser.parseRaw("miter"), DOM.LineJoin.miter) 152| 1| 153| 1| XCTAssertThrowsError((try parser.parseRaw("sd")) as DOM.LineJoin) 154| 1| } 155| |} 156| | 157| | 158| | /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/XML.Parser.ScannerTests.swift: 1| |// 2| |// ScannerTests.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 XCTest 33| |@testable import SwiftDraw 34| | 35| |final class ScannerTests: XCTestCase { 36| | 37| 1| func testIsEOF() throws { 38| 1| var scanner = XMLParser.Scanner(text: "Hi") 39| 1| XCTAssertFalse(scanner.isEOF) 40| 1| try scanner.scanString("Hi") 41| 1| XCTAssertTrue(scanner.isEOF) 42| 1| } 43| | 44| 1| func testScanCharsetHex() { 45| 1| var scanner = XMLParser.Scanner(text: " \t 8badf00d \t \t 007") 46| 1| XCTAssertEqual(try scanner.scanString(matchingAny: .hexadecimal), "8badf00d") 47| 1| XCTAssertEqual(try scanner.scanString(matchingAny: .hexadecimal), "007") 48| 1| XCTAssertThrowsError(try scanner.scanString(matchingAny: .hexadecimal)) 49| 1| } 50| | 51| 1| func testScanCharsetEmoji() { 52| 1| var scanner = XMLParser.Scanner(text: " \t 8badf00d \tšŸ¶ \tšŸŒžšŸ‡¦šŸ‡ŗ 007") 53| 1| let emoji: Foundation.CharacterSet = "šŸ¤ šŸŒžšŸ’ŽšŸ¶\u{1f1e6}\u{1f1fa}" 54| 1| 55| 1| XCTAssertThrowsError(try scanner.scanString(matchingAny: emoji)) 56| 1| XCTAssertEqual(try scanner.scanString(matchingAny: .hexadecimal), "8badf00d") 57| 1| XCTAssertThrowsError(try scanner.scanString(matchingAny: .hexadecimal)) 58| 1| XCTAssertEqual(try scanner.scanString(matchingAny: emoji), "šŸ¶") 59| 1| XCTAssertThrowsError(try scanner.scanString(matchingAny: .hexadecimal)) 60| 1| XCTAssertEqual(try scanner.scanString(matchingAny: emoji), "šŸŒžšŸ‡¦šŸ‡ŗ") 61| 1| XCTAssertThrowsError(try scanner.scanString(matchingAny: emoji)) 62| 1| XCTAssertEqual(try scanner.scanString(matchingAny: .hexadecimal), "007") 63| 1| } 64| | 65| 1| func testScanString() { 66| 1| var scanner = XMLParser.Scanner(text: " \t The quick brown fox") 67| 1| 68| 1| XCTAssertThrowsError(try scanner.scanString("fox")) 69| 1| XCTAssertNoThrow(try scanner.scanString("The")) 70| 1| XCTAssertThrowsError(try scanner.scanString("quick fox")) 71| 1| XCTAssertNoThrow(try scanner.scanString("quick brown")) 72| 1| XCTAssertNoThrow(try scanner.scanString("fox")) 73| 1| XCTAssertThrowsError(try scanner.scanString("fox")) 74| 1| } 75| | 76| 1| func testScanCase() { 77| 1| var scanner = XMLParser.Scanner(text: "NOT OK") 78| 1| XCTAssertEqual(try scanner.scanCase(from: Token.self), .nok) 79| 1| XCTAssertEqual(try scanner.scanCase(from: Token.self), .ok) 80| 1| XCTAssertThrowsError(try scanner.scanCase(from: Token.self)) 81| 1| } 82| | 83| 1| func testScanCharacter() { 84| 1| var scanner = XMLParser.Scanner(text: " \t The fox 8badf00d ") 85| 1| 86| 1| XCTAssertThrowsError(_ = try scanner.scanCharacter(matchingAny: "qfxh")) 87| 1| XCTAssertEqual(try scanner.scanCharacter(matchingAny: "fxT"), "T") 88| 1| XCTAssertThrowsError(_ = try scanner.scanCharacter(matchingAny: "fxT")) 89| 1| XCTAssertEqual(try scanner.scanCharacter(matchingAny: "qfxh"), "h") 90| 1| XCTAssertNoThrow(try scanner.scanString("e fox")) 91| 1| XCTAssertEqual(try scanner.scanCharacter(matchingAny: .hexadecimal), "8") 92| 1| XCTAssertEqual(try scanner.scanCharacter(matchingAny: .hexadecimal), "b") 93| 1| XCTAssertEqual(try scanner.scanCharacter(matchingAny: .hexadecimal), "a") 94| 1| XCTAssertEqual(try scanner.scanCharacter(matchingAny: .hexadecimal), "d") 95| 1| XCTAssertEqual(try scanner.scanCharacter(matchingAny: .hexadecimal), "f") 96| 1| XCTAssertEqual(try scanner.scanCharacter(matchingAny: .hexadecimal), "0") 97| 1| XCTAssertEqual(try scanner.scanCharacter(matchingAny: .hexadecimal), "0") 98| 1| XCTAssertEqual(try scanner.scanCharacter(matchingAny: .hexadecimal), "d") 99| 1| } 100| | 101| 1| func testScanUInt8() { 102| 1| AssertScanUInt8("0", 0) 103| 1| AssertScanUInt8("124", 124) 104| 1| AssertScanUInt8(" 045", 45) 105| 1| AssertScanUInt8("-29", nil) 106| 1| AssertScanUInt8("ab24", nil) 107| 1| } 108| | 109| 1| func testScanFloat() { 110| 1| AssertScanFloat("0", 0) 111| 1| AssertScanFloat("124", 124) 112| 1| AssertScanFloat(" 045", 45) 113| 1| AssertScanFloat("-29", -29) 114| 1| AssertScanFloat("ab24", nil) 115| 1| } 116| | 117| 1| func testScanDouble() { 118| 1| AssertScanDouble("0", 0) 119| 1| AssertScanDouble("124", 124) 120| 1| AssertScanDouble(" 045", 45) 121| 1| AssertScanDouble("-29", -29) 122| 1| AssertScanDouble("ab24", nil) 123| 1| } 124| | 125| 1| func testScanLength() { 126| 1| AssertScanLength("0", 0) 127| 1| AssertScanLength("124", 124) 128| 1| AssertScanLength(" 045", 45) 129| 1| AssertScanLength("-29", nil) 130| 1| AssertScanLength("ab24", nil) 131| 1| } 132| | 133| 1| func testScanBool() { 134| 1| AssertScanBool("0", false) 135| 1| AssertScanBool("1", true) 136| 1| AssertScanBool("true", true) 137| 1| AssertScanBool("false", false) 138| 1| AssertScanBool("false", false) 139| 1| 140| 1| var scanner = XMLParser.Scanner(text: "-29") 141| 1| XCTAssertThrowsError(try scanner.scanBool()) 142| 1| XCTAssertEqual(scanner.scanLocation, 0) 143| 1| } 144| | 145| 1| func testScanPercentageFloat() { 146| 1| AssertScanPercentageFloat("0", 0) 147| 1| AssertScanPercentageFloat("0.5", 0.5) 148| 1| AssertScanPercentageFloat("0.75", 0.75) 149| 1| AssertScanPercentageFloat("1.0", 1.0) 150| 1| AssertScanPercentageFloat("-0.5", nil) 151| 1| AssertScanPercentageFloat("1.5", nil) 152| 1| AssertScanPercentageFloat("as", nil) 153| 1| AssertScanPercentageFloat("29", nil) 154| 1| AssertScanPercentageFloat("24", nil) 155| 1| } 156| | 157| 1| func testScanPercentage() { 158| 1| AssertScanPercentage("0", 0) 159| 1| AssertScanPercentage("0%", 0) 160| 1| AssertScanPercentage("100%", 1.0) 161| 1| AssertScanPercentage("100 %", 1.0) 162| 1| AssertScanPercentage("45.5 %", 0.455) 163| 1| AssertScanPercentage("0.5 %", 0.005) 164| 1| AssertScanPercentage("as", nil) 165| 1| AssertScanPercentage("29", nil) 166| 1| AssertScanPercentage("24", nil) 167| 1| } 168| | 169| 1| func testScanCoordinate() throws { 170| 1| var scanner = XMLParser.Scanner(text: "10.05,12.04-49.05,30.02-10") 171| 1| 172| 1| XCTAssertEqual(try scanner.scanCoordinate(), 10.05) 173| 1| _ = try? scanner.scanString(",") 174| 1| XCTAssertEqual(try scanner.scanCoordinate(), 12.04) 175| 1| _ = try? scanner.scanString(",") 176| 1| XCTAssertEqual(try scanner.scanCoordinate(), -49.05) 177| 1| _ = try? scanner.scanString(",") 178| 1| XCTAssertEqual(try scanner.scanCoordinate(), 30.02) 179| 1| _ = try? scanner.scanString(",") 180| 1| XCTAssertEqual(try scanner.scanCoordinate(), -10) 181| 1| } 182| |} 183| | 184| 5|private func AssertScanUInt8(_ text: String, _ expected: UInt8?, file: StaticString = #file, line: UInt = #line) { 185| 5| var scanner = XMLParser.Scanner(text: text) 186| 5| XCTAssertEqual(try? scanner.scanUInt8(), expected, file: file, line: line) 187| 5|} 188| | 189| 5|private func AssertScanFloat(_ text: String, _ expected: Float?, file: StaticString = #file, line: UInt = #line) { 190| 5| var scanner = XMLParser.Scanner(text: text) 191| 5| XCTAssertEqual(try? scanner.scanFloat(), expected, file: file, line: line) 192| 5|} 193| | 194| 5|private func AssertScanDouble(_ text: String, _ expected: Double?, file: StaticString = #file, line: UInt = #line) { 195| 5| var scanner = XMLParser.Scanner(text: text) 196| 5| XCTAssertEqual(try? scanner.scanDouble(), expected, file: file, line: line) 197| 5|} 198| | 199| 5|private func AssertScanLength(_ text: String, _ expected: DOM.Length?, file: StaticString = #file, line: UInt = #line) { 200| 5| var scanner = XMLParser.Scanner(text: text) 201| 5| XCTAssertEqual(try? scanner.scanLength(), expected, file: file, line: line) 202| 5|} 203| | 204| 5|private func AssertScanBool(_ text: String, _ expected: Bool?, file: StaticString = #file, line: UInt = #line) { 205| 5| var scanner = XMLParser.Scanner(text: text) 206| 5| XCTAssertEqual(try? scanner.scanBool(), expected, file: file, line: line) 207| 5|} 208| | 209| 9|private func AssertScanPercentage(_ text: String, _ expected: Float?, file: StaticString = #file, line: UInt = #line) { 210| 9| var scanner = XMLParser.Scanner(text: text) 211| 9| XCTAssertEqual(try? scanner.scanPercentage(), expected, file: file, line: line) 212| 9|} 213| | 214| 9|private func AssertScanPercentageFloat(_ text: String, _ expected: Float?, file: StaticString = #file, line: UInt = #line) { 215| 9| var scanner = XMLParser.Scanner(text: text) 216| 9| XCTAssertEqual(try? scanner.scanPercentageFloat(), expected, file: file, line: line) 217| 9|} 218| | 219| | 220| |extension Foundation.CharacterSet: ExpressibleByStringLiteral { 221| | 222| | static let hexadecimal: Foundation.CharacterSet = "0123456789ABCDEFabcdef" 223| | 224| 6| public init(stringLiteral value: String) { 225| 6| self.init(charactersIn: value) 226| 6| } 227| | 228| |} 229| | 230| |enum Token: String, CaseIterable { 231| | case ok = "OK" 232| | case nok = "NOT" 233| |} /Users/travis/build/swhitty/SwiftDraw/SwiftDrawTests/XML.SAXParserTests.swift: 1| |// 2| |// XML.SAXParserTests.swift 3| |// SwiftDraw 4| |// 5| |// Created by Simon Whitty on 16/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| |import XCTest 33| |@testable import SwiftDraw 34| | 35| |final class SAXParserTests: XCTestCase { 36| | 37| 1| func testMissingFileThrows() { 38| 1| let missingFile = URL(fileURLWithPath: "/user/tmp/SWIFTDraw/SwiftDraw/missing") 39| 1| //let missingFile = URL(string: "http://www.test.com")! 40| 1| XCTAssertThrowsError(try XML.SAXParser.parse(contentsOf: missingFile)) 41| 1| } 42| | 43| 1| func testInvalidXMLThrows() { 44| 1| let xml = "hi" 45| 1| XCTAssertThrowsError(try XML.SAXParser.parse(data: xml.data(using: .utf8)!)) 46| 1| } 47| | 48| 1| func testValidSVGParses() throws { 49| 1| let xml = """ 50| 1| 51| 1| 52| 1|""" 53| 1| 54| 1| let root = try XML.SAXParser.parse(data: xml.data(using: .utf8)!) 55| 1| XCTAssertEqual(root.name, "svg") 56| 1| XCTAssertTrue(root.children.isEmpty) 57| 1| } 58| | 59| 1| func testUnexpectedElementsThrows() throws { 60| 1| let xml = """ 61| 1| 62| 1| 63| 1| 64| 1|""" 65| 1| XCTAssertThrowsError(try XML.SAXParser.parse(data: xml.data(using: .utf8)!)) 66| 1| } 67| | 68| 1| func testUnexpectedNamespaceElementsSkipped() throws { 69| 1| let xml = """ 70| 1| 71| 1| 72| 1| 73| 1| 74| 1|""" 75| 1| let root = try XML.SAXParser.parse(data: xml.data(using: .utf8)!) 76| 1| XCTAssertEqual(root.name, "svg") 77| 1| XCTAssertEqual(root.children.count, 1) 78| 1| XCTAssertEqual(root.children[0].name, "b") 79| 1| } 80| |} <<<<<< EOF # path=fixes ./SwiftDraw/Parser.XML.swift:31,38,39,41,46,47,50,51,52,64,66,67,71,76,77,79,82,83,86,87,90,91,94,95,98,99,102,103,106,107,110,111,114,115,118,119,122,123,126,127,130,131,132,134,136,144,146,147,150,151,154,155,158,159,162,163,166,167,170,171,174,175,178,179,182,183,186,187,190,191,194,195,198,199 ./SwiftDraw/LayerTree.Image.swift:31,33,38,41,51,52,53,54 ./SwiftDraw/Parser.XML.Gradient.swift:31,33,36,42,43,45,46,50,51,58,62,63,65,66,72,73 ./SwiftDraw/UIImage+Image.swift:31,33,39,40,42,43,44,48,49,55,58,59,60,64,65,69,70 ./SwiftDraw/Stack.swift:31,35,39,40,45,51,53,54,55,58,59,65,66 ./SwiftDraw/XML.Parser.Scanner.swift:31,33,35,37,40,45,46,48,52,53,57,58,63,66,67,70,73,76,77,86,87,90,91,99,100,103,104,109,110,118,121,122,128,131,132,138,141,142,151,154,155,158,159,162,163,169,172,173,177,180,187,190,191,192,193,201,208,209,210,211,213,216,217,221,222,224,225,232,235,236,239,240,242,243,248,249,250,251,253,255,258,259,262,263,264 ./SwiftDraw/Renderer.CoreGraphics.swift:31,40,57,58,61,63,66,67,70,71,74,75,78,79,85,86,96,97,98,102,106,107,111,112,116,117,119,123,124,125,132,133,141,142,163,164,165,180,181,183,184,187,190,191,193,194,200,203,204,214,215,216,223,224,225,231,232,233,239,240,241,248,249,250,257,258,259,270,271,272,275,277,280,281,284,285,288,289,292,293,296,297,300,301,304,305,308,309,312,313,316,317,323,324,327,328,331,332,335,336,339,340,343,344,348,349,354,355,358,359,362,363,367,368,372,373,377,378,384,385 ./SwiftDraw/CommandLine.swift:31,33 ./SwiftDraw/Parser.XML.SVG.swift:31,33,37,38,43,46,49,53,55,58,60,61,65,70,73,74,76,77,78,87,91,92,94,95,102,103,104,105,109,110,113,117,119,120,122,123,124,127,133,134,136,137,140,143,146,147,150,156,157,159,160,163,166,169,170,173,179,180,182,183,186,191,192 ./SwiftDraw/DOM.Pattern.swift:31,33,35,37,43,46,48,53,54,55,56,58,62,63 ./SwiftDraw/XML.SAXParser.swift:31,33,37,39,41,47,50,53,56,57,62,65,66,69,75,76,78,79,83,84,90,91,94,97,100,101,102,108,109,111,112,117,118,119 ./SwiftDraw/Formatter.XML.swift:31,33,35,39,43,44,48,49,52,53,60,61,62,65,67,70,71,75,76,80,87,88,91,93,94,95 ./SwiftDraw/DOM.Path.swift:31,33,35,37,40,45,46,64,68,69,70,92,107,108,109,110,111 ./SwiftDraw/Parser.XML.Element.swift:31,33,40,41,47,48,55,56,61,66,68,69,72,73,76,77,80,82,99,100,102,105,107,108,118,119,121,126,130,131,132,133,135,136,140,141,153,154,155,159,160,164,165,169,170,174,175,182,183,191,192,196,200,202,203,209,212,213,216,219,225,233,234,239,240,241,244,247,248,251,253,254,255,256,257,259,275,276,277 ./SwiftDraw/LayerTree.Gradient.swift:31,33,38,43,44,50,51,56,61,62,63,64 ./SwiftDraw/DOM.Text.swift:31,33,38,41,44,49,50,51 ./SwiftDraw/Parser.XML.Attributes.swift:31,33,41,44,47,56,57,65,67,68,73,75,76,77,78,79,81,83,87,88,92,97,98,100,101,102,108,110,111,115,116,120,121,125,126,129,130,134,135,138,142,144,147,148,150,151,156,162,165,167,168,170,171,175,177,178,179,180 ./SwiftDraw/CommandLine.Configuration.swift:31,33,35,42,43,48,49,53,54,59,60,64,65,72,73,82,83,87,88,90,91,95,98,100,101,105,106,116,117,119,120,121,123,129,130,137,138,147,148,149,150,152,161,162,163,164,166,177,178,179 ./SwiftDraw/LayerTree.CommandGenerator.swift:32,34,36,38,42,47,48,54,55,56,59,64,66,72,73,77,80,81,85,86,91,92,95,96,102,103,105,106,109,114,116,122,123,127,131,135,136,141,142,143,146,147,153,154,156,157,168,169,170,177,186,191,192,200,204,209,212,215,216,225,232,233,235,236,240,241,244,248,251,252,255,258,259,262,263,280,281,282,285,288,290,291,294,297,299,302,303,306,309,317,321,322,323,324,325,327,331,332,333,338,339,344 ./SwiftDraw/Parser.XML.Image.swift:31,33,38,42,44,45 ./SwiftDraw/LayerTree.Path.swift:31,35,38,39,45,46,49,50,53,54,55,56,63,64,65,69,70,72,73,80,81,84,85,86,88,92,94,95,102,103,104 ./SwiftDraw/NSImage+Image.swift:31,34,38,43,44,45,46,50,51,54,59,60,62,63,65,69,72,83,84,88,93,95,96,100,102,108,110,111 ./SwiftDraw/Image.swift:31,33,35,39,43,46,56,57,59,62,65,66,67,69,72,73,76,77,80,81,84,85,87,89,94,95,96,98,102,103,105,106,110,111,113,114 ./SwiftDraw/Image+CoreGraphics.swift:31,34,36,40,44,45,49,50,58,59,60,62,67,69,71,78,80,81,92,94,98,104,106,107,110,111,112,114,118,119 ./SwiftDraw/Parser.XML.Use.swift:31,33,38,40,41 ./SwiftDraw/CGImage+Mask.swift:31,34,37,38,40,42,45,56,57,66,67 ./SwiftDraw/LayerTree.Builder.swift:31,33,35,37,39,42,43,50,51,55,56,61,63,66,67,70,71,73,74,78,80,86,94,96,97,102,107,108,110,111,125,126,128,129,133,135,136,140,142,146,147,149,150,151,152,153,155,158,163,164,170,171,174,185,186,187,190,191,197,198,206,207,213,215,216,221,229,233,238,246,250,251,252,255,258,265,269,271,272,273,285,288,291,295,302,309,310,311,314,315,316,317,318,320,327,328,329 ./SwiftDraw/DOM.Color.swift:31,33,40,190,191,192,193,195,346,347,348 ./SwiftDraw/CGPath+Segment.swift:31,40,47,48,49,50,58,59,77,78,80,81,82,84,90,96,97,99,103,115,116,117,118,120,121 ./SwiftDraw/Parser.XML.Transform.swift:31,33,35,38,41,42,46,47,49,50,52,65,67,68,72,73,86,88,89,93,94,98,99,103,105,106,110,111,115,116,120,122,123,127,128,132,133,139,141,142,146,147,151,152,156,157,161,162 ./SwiftDraw/DOM.Element.swift:31,33,36,37,41,48,52,56,57,59,61,64,67,74,78,82,83,89,96,97,98,103,109,110,111,117,124,125,126,132,135,142,143,144,147,151,152,153,156,160,161,162,165,166 ./SwiftDraw/DOM.Use.swift:31,36,39,42,43,44 ./SwiftDraw/LayerTree.Builder.Path.Arc.swift:31,33,35,39,40,41,44,45,50,51,53,56,57,58,64,67,72,75,78,81,84,89,92,95,98,99,101,102,105,110,119,120,121,123,126,129,131,132,137,140,143,146,147,150,151,154,160,161,163,165,168,170,175,176,181,183,184 ./SwiftDraw/LayerTree.Builder.Layer.swift:31,33,35,40,41,47,48,52,55,56,58,59,60,67,68,74,76,77,78,79 ./SwiftDraw/Parser.XML.Pattern.swift:31,32,34,36,38,42,46,49,51,52,53 ./SwiftDraw/SwiftDraw-macOS.h:31,33,36,39,41 ./SwiftDraw/LayerTree.Layer.swift:28,36,42,43,48,54,57,58,59,67,69,70,77,78,79,86,87,92,96,97,102,103,108,109,114,115,116,121,124,125,126 ./SwiftDraw/LayerTree.Color.swift:31,32,35,39,42,43,44,46,49,50,67,68,69,75,76,85,86,87,99,100,101,110,111,112,126,127,128,129,132,133,137,138,139,149,150,151,152,164,165,166 ./SwiftDraw/Renderer.LayerTree.swift:32,49,50,52,54,56,59,60,63,64,67,68,71,72,75,76,79,80,83,84,87,88,91,92,95,96,99,100,103,104,107,108,111,112,115,116,119,120,123,124,127,128 ./SwiftDraw/Parser.XML.Path.swift:31,33,35,37,41,46,47,49,51,53,55,57,61,65,67,68,91,92,93,98,100,101,107,109,110,116,118,119,123,125,126,130,132,133,147,149,150,160,162,163,173,175,176,182,184,185,201,205,206,207,211 ./SwiftDraw/URL+Data.swift:31,33,35,40,41,45,47,48,49,52,53,60,61,64,68,69,71,72 ./SwiftDraw/Formatter.swift:32 ./SwiftDraw/CGPattern+Closure.swift:31,33,35,42,46,47,50,51,55,64,65,70,71,72 ./SwiftDraw/DOM.Gradient.swift:31,33,35,41,43,47,48,53,58,59,60,61,62,72,73 ./SwiftDraw/XML.swift:31,34,39,41,46,47,48 ./SwiftDraw/DOM.SVG.swift:31,37,39,41,45,46,52,53,59,61,62,63,67,68,72,73 ./SwiftDraw/LayerTree.Pattern.swift:31,33,35,38,42,43,46,47,48 ./SwiftDraw/DOM.Switch.swift:31,35,36 ./SwiftDraw/DOM.swift:31,33,35,43,44,49,53,54,55,59,66,67,68,69,73,74,78,79,84,85,90,91,100,101,104,105 ./SwiftDraw/CommandLine.Arguments.swift:31,33,35,41,45,46,48,49,50,59,60,61,64,65,67,68,71,72,73,75,79,80,84,85 ./SwiftDraw/LayerTree.Builder.Shape.swift:31,33,35,55,56,58,59,65,66,72,73,79,80,81 ./SwiftDraw/Parser.XML.Color.swift:32,34,48,49,51,52,56,58,59,64,66,67,71,74,75,77,78,83,84,87,89,92,93,95,96,100,108,109,113,120,122,123,128,130,131,141,142,146,148,149 ./SwiftDraw/LayerTree.swift:31,33,35,40,44,45,49,53,54,58,59,62,63,64,68,72,73,77,78,81,82,83,87,91,92,96,97,100,101,104,105,108,109,112,113,116,117,118,124,125 ./SwiftDraw/SwiftDraw.h:31,33,36,39,41,42 ./SwiftDraw/LayerTree.Builder.Path.swift:31,33,35,37,40,43,49,50,52,53,73,74,76,77,83,84,86,87,90,92,96,97,98,101,103,107,108,109,112,116,117,118,121,125,126,127,130,134,140,141,142,145,148,153,159,160,161,164,167,171,172,174,175,179,181,184,186,189,191,192,195,198,201,206,208,209,212,214,218,219,225,227,228,232,233,234,235,239,240 ./SwiftDraw/LayerTree.Transform.swift:31,37,39,45,49,50,54,55,59,60,68,69,70,71,85,86,87,88,98,99 ./SwiftDraw/Parser.XML.Text.swift:31,33,35,41,42,44,45,52,54,55 ./SwiftDraw/LayerTree.Shape.swift:31,33,40,41 ./SwiftDraw/Renderer.swift:32,49,50,53,55,73,75,76,79,84,89,101,106,107,157,158,159,163,164,165,166,170,175,187,190,193,196 ./SwiftDraw/DOM.Image.swift:36,39,45,46,47 ./LinuxMain.swift:2,4,7 ./SwiftDrawTests/URL+DataTests.swift:31,34,36,41,46,47,54,55,60,61,62,63 ./SwiftDrawTests/RendererTests.swift:31,34,36,64,90,91,92,94,97,98,99,101,104,105,106,108,111,112,113,115,118,119,120,122,125,126 ./SwiftDrawTests/Parser.XML.ImageTests.swift:31,32,34,37,47,48,49,51,56,58,60,62,64,66,70,71,75,77,79,81,83,85,87,91,92 ./SwiftDrawTests/Parser.XML.GradientTests.swift:31,32,34,37,39,43,46,49,50,53,55,62,66,68,69,71,74,75 ./SwiftDrawTests/Parser.SVGTests.swift:31,32,35,37,41,45,48,52,55,56,60,61,65,66,70,71,78,83,84,86,88,91,94,98,99,106,111,114,117,118,119 ./SwiftDrawTests/LayerTree.LayerTests.swift:31,34,36,41,46,49,50,56,59,60,65,69,72,75,78,81,88,89,90,92,96,97,98 ./SwiftDrawTests/Parser.AttributesTests.swift:31,34,36,42,45,49,53,58,61,67,68,72,79,80,83,88,91,94,95,100,101,107,108,113,114,121,122,129,130,137,138,144,145,151,162,163,164,165,170,174,176,177,178,179,180 ./SwiftDrawTests/LayerTree.ShapeTests.swift:28,31,33,39,46,51,56,61,66,70,75,79,82,84,85,89,91,92,96,98,99,103,105,106,112,119,120,124,126,127,131,133,134,139,143,144 ./SwiftDrawTests/NSImage+ImageTests.swift:31,34,36,40,41,45,46,49,53,54,58,62,65,66,67,69,77,78 ./SwiftDrawTests/ValueParserTests.swift:31,32,35,37,39,43,46,47,55,58,59,68,73,74,81,84,85,91,95,96,104,107,108,113,117,119,124,125,129,132,133,137,139,140,146,147,152,154,155,156,157,158 ./SwiftDrawTests/CGRenderer.PathTests.swift:31,34,36,38,40,43,46,50,54,55,58,61,65,69,70,73,76,80,84,88,89,92,95,99,103,107,108,111,118,124,125,128,133,137,138,141,146,150,151,153,160,162,167,168,176,178,184,185,188,191,194,195,199,200,201,203,208,209,212,213,218 ./SwiftDrawTests/Parser.XML.ElementTests.swift:31,34,36,42,45,46,51,54,55,61,64,65,71,74,80,81,84,87,88,93,94,98,101,104,107,110,111,116,118,119,125,129,136,137,138,142,146,153,154,155 ./SwiftDrawTests/LayerTree.TransformTests.swift:28,31,33,36,40,41,45,46,50,51,55,56,62,63,67,68,72,74,75,79,81,82,86,89,90,94,99,100,104,107,108,112,115,116,120,123,124,129,132,133 ./SwiftDrawTests/CoordinateTests.swift:31,34,36,40,50,51,53,56,66,67,71,75,76,80,84,85 ./SwiftDrawTests/NSBitmapImageRep+Extensions.swift:31,33,35,47,48,52,53,56,57 ./SwiftDrawTests/CommandLine.ArgumentsTests.swift:31,34,36,40,41,45,46,50,51,55,56,60,61,62 ./SwiftDrawTests/UseTests.swift:31,34,36,39,44,47,52,53 ./SwiftDrawTests/GradientTests.swift:31,34,36,39,44,50,54,55,57,59,64,68,75,76 ./SwiftDrawTests/UIImage+ImageTests.swift:31,34,36,40,41,45,46 ./SwiftDrawTests/Parser.XML.PatternTests.swift:31,34,36,38,41,45,49,50,53,56,60,64,67,68,71,74,78,82,85,86,89,91,96,101,103 ./SwiftDrawTests/ImageTests.swift:31,34,36,41,43,46,47,50,51,59,60,68,69,70,71,73,79,80 ./SwiftDrawTests/StackTests.swift:31,34,36,39,43,44,47,49,53,61,62,65,68,82,85,86,88,90,96,99,103,109,115,121,122,123,124,125 ./SwiftDrawTests/Parser.XML.PathTests.swift:31,34,38,40,43,49,50,53,57,58,62,65,68,69,77,80,81,89,92,93,100,103,104,111,112,119,120,127,128,135,136,143,144,151,152,157,158,162,164,166,172,173,177,179,181,182,184,187,189,191,197,198,199,204,205,210,212,213,214,219,220,223,224,227,228,231,232,237,238,242,243,247,248,251,252,259,260,261,262,267,268 ./SwiftDrawTests/Renderer.CoreGraphicsTypesTests.swift:28,32,34,39,43,44,48,49,53,54,59,63,64,70,75,76,81,82,87,88,92,93,98,99,104,105,110,111,115,120,122,123,128,129,142,144,145,152,158,161,162,165,166,172,177,185,186,188,190,191,192,196,199,200 ./SwiftDrawTests/LayerTree.ColorTests.swift:31,34,36,38,42,46,47,51,57,62,67,68,72,77,81,85,86,89,93,97,98,101,107,114,115,118,125,132,133,134,135 ./SwiftDrawTests/LayerTree.BuilderTests.swift:31,34,36,39,43,47,51,52,58,60,63,65,67,68,74,77,80,81,84,88,91,92,95,98,100,105,106,115,122,126,127,128,130,133,134,135,137,141,142 ./SwiftDrawTests/XML.SAXParserTests.swift:31,34,36,41,42,46,47,53,57,58,66,67,79,80 ./SwiftDrawTests/XCTestManifests.swift:3,20,21,30,31,40,41,53,54,65,66,77,78,93,94,103,104,115,116,127,128,137,138,151,152,165,166,175,176,185,186,196,197,218,219,234,235,256,257,269,270,282,283,298,299,307,308,317,318,341,342,352,353,363,364,388,389,410,411,419,420,432,433,447,448,469,470,481,482,491,492,502,503,511,512,530,531,548,549,592 ./SwiftDrawTests/LayerTree.ImageTests.swift:28,31,33,36,41,45,48,49,54,58,61,63,64 ./SwiftDrawTests/DOM+Extensions.swift:31,34,36,39,40,43,44,47,48,51,52,55,56,59,60,63,64,70,71,81,82,83,85,90,91,92,96,98,101,102,104,105,106,110,112,115,116,118,119,120,124,125,128,129,130,131,133,137,138,142,143,146,147 ./SwiftDrawTests/XML.Parser.ScannerTests.swift:31,34,36,42,43,49,50,54,63,64,67,74,75,81,82,85,99,100,107,108,115,116,123,124,131,132,139,143,144,155,156,167,168,171,181,182,183,187,188,192,193,197,198,202,203,207,208,212,213,217,218,219,221,223,226,227,228,229,233 ./SwiftDrawTests/Parser.XML.TransformTests.swift:31,34,36,44,49,50,60,65,66,76,81,82,86,91,92,98,103,104,112,117,118,126,131,132,138,139 ./SwiftDrawTests/CGPath+SegmentTests.swift:31,34,36,47,54,55,59,67,68,69 ./SwiftDrawTests/LayerTree.Builder.LayerTests.swift:31,34,36,40,43,44,49,52,55,56,61,62,68,74,80,87,88 ./SwiftDrawTests/Renderer.LayerTreeProviderTests.swift:28,32,34,40,44,45,49,50,54,55,60,61,65,68,69,74,79,80,83,84,88,89,93,94,99,100,104,105,109,110,114,115,119,120,121,123,126,127,128 ./SwiftDrawTests/LayerTree.Builder.ShapeTests.swift:31,34,36,41,42,47,48 ./SwiftDrawTests/LayerTree.CommandGeneratorTests.swift:28,31,33,38,41,46,47,48,54,58,59 ./SwiftDrawTests/CommandLine.ConfigurationTests.swift:31,34,36,40,43,46,47,53,56,57,61,67,68,72,77,78,82,87,88,96,97,98,99,101,104,105 ./SwiftDrawTests/ParserSVGImageTests.swift:31,35,37,39,41,44,45,54,55,56,60,61,66,67,76,78,101,102,111,118,119,122,124,126,130,131,134,135,140,141,144,145,150,151,154,155,156,159,162,164,166,171,172,173,175,176,183,184,185,186,190,191,192,194,199,200,201 ./SwiftDrawTests/StyleTests.swift:31,34,36,46,49,50,57,60,65,66 ./SwiftDrawTests/Bundle+Extensions.swift:31,32,34,36,39,40,44,46,47,50,51,53 ./SwiftDrawTests/DOM.ElementTests.swift:31,34,36,40,42,45,49,52,53,57,59,62,66,69,70,74,76,79,83,86,87,91,93,96,100,103,104,108,110,113,117,120,121,125,127,130,134,137,138,142,144,147,151,154,155,159,161,164,168,171,172 ./SwiftDrawTests/Parser.XML.TextTests.swift:31,34,36,39,42,45,49,52,53,57,60,61,67,68,69,70 ./SwiftDrawTests/MockRenderer.swift:31,34,36,38,40,43,44,47,48,51,52,55,56,59,60,63,64,67,68,71,72,75,76,79,80,83,84,87,88,91,92,95,96,99,100,103,104,107,108,111,112,115,116,119,120,123,124,127,128,131,132 ./SwiftDrawTests/LayerTree.PathTests.swift:28,31,33,38,40,41,45,48,51,54,57,58,60,63,67,71,72,76,80,84,85,89,93,97,101,102,106,110,114,118,119,127,133,134,140,144,145,151,155,156,163,165,170,171,178,180,186,187,191,194,195,198,199,203,205,206,210,212,213,218,219,222,223,228,229,230,231,232 ./SwiftDrawTests/Parser.GraphicAttributeTests.swift:31,32,35,37,54,69,71,86,87,90,97,98,101,105,106,109,113,114,117,121,122,123 ./SwiftDrawTests/Parser.XML.ColorTests.swift:31,34,36,41,42,50,51,57,58,64,65,71,72,73,75,78,79 ./SwiftDrawTests/CGRendererTests.swift:31,36,38,41,44,47,48,51,54,57,58,59,61,64,70,71,74,75,76,78,83,86,87,88,90,94,95 ./CommandLine/CommandLine.swift:31,34,36,39,44,45,51,52,58,59,61,62,68,69,71,72,81,82,83,89,91,93,98,99,100,102,107,108,109,111,114,115,116,118,127,128,129,130,132,139,140,141 ./CommandLine/main.swift:31,37,39 ./Examples/Basic/Sources/ViewController.swift:31,34,36,41,42,45,46,59,60,61,64,65,72,73,74,76,78,86,87,91,92 ./Examples/Basic/Sources/AppDelegate.swift:31,33,36,38,39,41,46,48,49,50,51,52 ./Package.swift:2,4,36 <<<<<< EOF