Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Examples/Sources/GalleryView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ struct GalleryView: View {
ScrollView {
LazyVStack(spacing: 20) {
ForEach(images, id: \.self) { image in
SVGView(svg: image)
SVGView(svg: image.scale(x: 3, y: 1))
.aspectRatio(contentMode: .fit)
.padding([.leading, .trailing], 10)
}
Expand Down
30 changes: 24 additions & 6 deletions SwiftDraw/CommandLine+Process.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,20 @@
#if canImport(CoreGraphics)
switch config.format {
case .jpeg:
let insets = try makeImageInsets(for: config.insets)
return try image.jpegData(size: config.size.cgValue, scale: config.scale.cgValue, insets: insets)
return try image
.inset(makeImageInsets(for: config.insets))
.size(config.size.cgValue)
.jpegData(scale: config.scale.cgValue)

Check warning on line 101 in SwiftDraw/CommandLine+Process.swift

View check run for this annotation

Codecov / codecov/patch

SwiftDraw/CommandLine+Process.swift#L98-L101

Added lines #L98 - L101 were not covered by tests
case .pdf:
let insets = try makeImageInsets(for: config.insets)
return try image.pdfData(size: config.size.cgValue, insets: insets)
return try image
.inset(makeImageInsets(for: config.insets))
.size(config.size.cgValue)
.pdfData()

Check warning on line 106 in SwiftDraw/CommandLine+Process.swift

View check run for this annotation

Codecov / codecov/patch

SwiftDraw/CommandLine+Process.swift#L103-L106

Added lines #L103 - L106 were not covered by tests
case .png:
let insets = try makeImageInsets(for: config.insets)
return try image.pngData(size: config.size.cgValue, scale: config.scale.cgValue, insets: insets)
return try image
.inset(makeImageInsets(for: config.insets))
.size(config.size.cgValue)
.pngData(scale: config.scale.cgValue)

Check warning on line 111 in SwiftDraw/CommandLine+Process.swift

View check run for this annotation

Codecov / codecov/patch

SwiftDraw/CommandLine+Process.swift#L108-L111

Added lines #L108 - L111 were not covered by tests
case .swift, .sfsymbol:
throw Error.unsupported
}
Expand All @@ -129,6 +135,18 @@
}
}

private extension SVG {

func size(_ s: CGSize?) -> SVG {
guard let s else { return self }
return size(s)
}

Check warning on line 143 in SwiftDraw/CommandLine+Process.swift

View check run for this annotation

Codecov / codecov/patch

SwiftDraw/CommandLine+Process.swift#L140-L143

Added lines #L140 - L143 were not covered by tests

func inset(_ insets: Insets) -> SVG {
expand(top: -insets.top, left: -insets.left, bottom: -insets.bottom, right: -insets.right)
}

Check warning on line 147 in SwiftDraw/CommandLine+Process.swift

View check run for this annotation

Codecov / codecov/patch

SwiftDraw/CommandLine+Process.swift#L145-L147

Added lines #L145 - L147 were not covered by tests
}

#if canImport(CoreGraphics)
private extension CommandLine.Scale {
var cgValue: CGFloat {
Expand Down
22 changes: 12 additions & 10 deletions SwiftDraw/NSImage+Image.swift → SwiftDraw/NSImage+SVG.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// NSImage+Image.swift
// NSImage+SVG.swift
// SwiftDraw
//
// Created by Simon Whitty on 24/5/17.
Expand Down Expand Up @@ -72,6 +72,7 @@ public extension NSImage {
}

public extension SVG {

func rasterize() -> NSImage {
return rasterize(with: size)
}
Expand All @@ -91,8 +92,9 @@ public extension SVG {
return image
}

func pngData(size: CGSize? = nil, scale: CGFloat = 0, insets: Insets = .zero) throws -> Data {
let (bounds, pixelsWide, pixelsHigh) = makeBounds(size: size, scale: scale, insets: insets)
func pngData(scale: CGFloat = 0) throws -> Data {
let scale = scale == 0 ? SVG.defaultScale : scale
let (bounds, pixelsWide, pixelsHigh) = Self.makeBounds(size: size, scale: scale)
guard let bitmap = makeBitmap(width: pixelsWide, height: pixelsHigh, isOpaque: false),
let ctx = NSGraphicsContext(bitmapImageRep: bitmap)?.cgContext else {
throw Error("Failed to create CGContext")
Expand All @@ -108,8 +110,9 @@ public extension SVG {
return data
}

func jpegData(size: CGSize? = nil, scale: CGFloat = 0, compressionQuality quality: CGFloat = 1, insets: Insets = .zero) throws -> Data {
let (bounds, pixelsWide, pixelsHigh) = makeBounds(size: size, scale: scale, insets: insets)
func jpegData(scale: CGFloat = 0, compressionQuality quality: CGFloat = 1) throws -> Data {
let scale = scale == 0 ? SVG.defaultScale : scale
let (bounds, pixelsWide, pixelsHigh) = Self.makeBounds(size: size, scale: scale)
guard let bitmap = makeBitmap(width: pixelsWide, height: pixelsHigh, isOpaque: true),
let ctx = NSGraphicsContext(bitmapImageRep: bitmap)?.cgContext else {
throw Error("Failed to create CGContext")
Expand All @@ -127,6 +130,10 @@ public extension SVG {
return data
}

internal static var defaultScale: CGFloat {
NSScreen.main?.backingScaleFactor ?? 1.0
}

private struct Error: LocalizedError {
var errorDescription: String?

Expand All @@ -138,11 +145,6 @@ public extension SVG {

extension SVG {

func makeBounds(size: CGSize?, scale: CGFloat, insets: Insets) -> (bounds: CGRect, pixelsWide: Int, pixelsHigh: Int) {
let scale = scale == 0 ? (NSScreen.main?.backingScaleFactor ?? 1.0) : scale
return Self.makeBounds(size: size, defaultSize: self.size, scale: scale, insets: insets)
}

func makeBitmap(width: Int, height: Int, isOpaque: Bool) -> NSBitmapImageRep? {
guard width > 0 && height > 0 else { return nil }
return NSBitmapImageRep(
Expand Down
55 changes: 18 additions & 37 deletions SwiftDraw/SVG+CoreGraphics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,25 @@
func draw(_ image: SVG, in rect: CGRect? = nil) {
let defaultRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
let renderer = CGRenderer(context: self)
saveGState()

guard let rect = rect, rect != defaultRect else {
renderer.perform(image.commands)
return
if let rect = rect, rect != defaultRect {
translateBy(x: rect.origin.x, y: rect.origin.y)
scaleBy(
x: rect.width / image.size.width,
y: rect.height / image.size.height
)

Check warning on line 48 in SwiftDraw/SVG+CoreGraphics.swift

View check run for this annotation

Codecov / codecov/patch

SwiftDraw/SVG+CoreGraphics.swift#L44-L48

Added lines #L44 - L48 were not covered by tests
}
renderer.perform(image.commands)

let scale = CGSize(width: rect.width / image.size.width,
height: rect.height / image.size.height)
draw(image.commands, in: rect, scale: scale)
}

fileprivate func draw(_ commands: [RendererCommand<CGTypes>], in rect: CGRect, scale: CGSize = CGSize(width: 1.0, height: 1.0)) {
let renderer = CGRenderer(context: self)
saveGState()
translateBy(x: rect.origin.x, y: rect.origin.y)
scaleBy(x: scale.width, y: scale.height)
renderer.perform(commands)
restoreGState()
}
}

public extension SVG {

func pdfData(size: CGSize? = nil, insets: Insets = .zero) throws -> Data {
let (bounds, pixelsWide, pixelsHigh) = makeBounds(size: size, scale: 1, insets: insets)
func pdfData() throws -> Data {
let (bounds, pixelsWide, pixelsHigh) = Self.makeBounds(size: size, scale: 1)
var mediaBox = CGRect(x: 0.0, y: 0.0, width: CGFloat(pixelsWide), height: CGFloat(pixelsHigh))

let data = NSMutableData()
Expand Down Expand Up @@ -92,31 +86,18 @@

extension SVG {

static func makeBounds(size: CGSize?,
defaultSize: CGSize,
scale: CGFloat,
insets: Insets) -> (bounds: CGRect, pixelsWide: Int, pixelsHigh: Int) {
let viewport = CGSize(
width: defaultSize.width - (insets.left + insets.right),
height: defaultSize.height - (insets.top + insets.bottom)
static func makeBounds(size: CGSize, scale: CGFloat) -> (bounds: CGRect, pixelsWide: Int, pixelsHigh: Int) {
let bounds = CGRect(
x: 0,
y: 0,
width: size.width * scale,
height: size.height * scale
)

let size = size ?? viewport

let sx = size.width / viewport.width
let sy = size.height / viewport.height

let width = size.width * scale
let height = size.height * scale
let insets = insets.applying(sx: sx * scale, sy: sy * scale)
let bounds = CGRect(x: -insets.left,
y: -insets.top,
width: width + insets.left + insets.right,
height: height + insets.top + insets.bottom)
return (
bounds: bounds,
pixelsWide: Int(width),
pixelsHigh: Int(height)
pixelsWide: Int(exactly: ceil(bounds.width)) ?? 0,
pixelsHigh: Int(exactly: ceil(bounds.height)) ?? 0
)
}
}
Expand Down
88 changes: 88 additions & 0 deletions SwiftDraw/SVG+Deprecated.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// SVG+Deprecated.swift
// SwiftDraw
//
// Created by Simon Whitty on 23/2/25.
// Copyright 2025 Simon Whitty
//
// Distributed under the permissive zlib license
// Get the latest version from here:
//
// https://github.com/swhitty/SwiftDraw
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//

#if canImport(CoreGraphics)
import CoreGraphics
import Foundation

#if canImport(UIKit)
import UIKit
#endif

public extension SVG {

@available(*, deprecated, message: "add insets via SVG.expand() before pngData")
func pngData(scale: CGFloat = 0, insets: Insets) throws -> Data {
try inset(insets).pngData(scale: scale)
}

Check warning on line 45 in SwiftDraw/SVG+Deprecated.swift

View check run for this annotation

Codecov / codecov/patch

SwiftDraw/SVG+Deprecated.swift#L43-L45

Added lines #L43 - L45 were not covered by tests

@available(*, deprecated, message: "set size via SVG.size() before pngData")
func pngData(size: CGSize, scale: CGFloat = 0) throws -> Data {
try self.size(size).pngData(scale: scale)
}

Check warning on line 50 in SwiftDraw/SVG+Deprecated.swift

View check run for this annotation

Codecov / codecov/patch

SwiftDraw/SVG+Deprecated.swift#L48-L50

Added lines #L48 - L50 were not covered by tests

@available(*, deprecated, message: "add insets via SVG.expand() before jpegData")
func jpegData(scale: CGFloat = 0, compressionQuality quality: CGFloat = 1, insets: Insets) throws -> Data {
try inset(insets).jpegData(scale: scale, compressionQuality: quality)
}

Check warning on line 55 in SwiftDraw/SVG+Deprecated.swift

View check run for this annotation

Codecov / codecov/patch

SwiftDraw/SVG+Deprecated.swift#L53-L55

Added lines #L53 - L55 were not covered by tests

@available(*, deprecated, message: "set size via SVG.size() before jpegData")
func jpegData(size: CGSize, scale: CGFloat = 0, compressionQuality quality: CGFloat = 1) throws -> Data {
try self.size(size).jpegData(scale: scale, compressionQuality: quality)
}

Check warning on line 60 in SwiftDraw/SVG+Deprecated.swift

View check run for this annotation

Codecov / codecov/patch

SwiftDraw/SVG+Deprecated.swift#L58-L60

Added lines #L58 - L60 were not covered by tests

private func inset(_ insets: Insets) -> SVG {
expand(top: -insets.top, left: -insets.left, bottom: -insets.bottom, right: -insets.right)
}

Check warning on line 64 in SwiftDraw/SVG+Deprecated.swift

View check run for this annotation

Codecov / codecov/patch

SwiftDraw/SVG+Deprecated.swift#L62-L64

Added lines #L62 - L64 were not covered by tests

#if canImport(UIKit)
@available(*, deprecated, message: "add insets via SVG.expand() before rasterize()")
func rasterize(scale: CGFloat = 0, insets: UIEdgeInsets) -> UIImage {
inset(insets).rasterize(scale: scale)
}

@available(*, deprecated, message: "add insets via SVG.expand() before pngData()")
func pngData(scale: CGFloat = 0, insets: UIEdgeInsets) throws -> Data {
try inset(insets).pngData(scale: scale)
}

@available(*, deprecated, message: "add insets via SVG.expand() before jpegData()")
func jpegData(scale: CGFloat = 0, compressionQuality quality: CGFloat = 1, insets: UIEdgeInsets) throws -> Data {
try inset(insets).jpegData(scale: scale, compressionQuality: quality)
}

private func inset(_ insets: UIEdgeInsets) -> SVG {
expand(top: -insets.top, left: -insets.left, bottom: -insets.bottom, right: -insets.right)
}
#endif

}
#endif
Loading