diff --git a/Art/images/data_razor.png b/Art/images/data_razor.png
new file mode 100644
index 0000000..6c5d4c5
Binary files /dev/null and b/Art/images/data_razor.png differ
diff --git a/Art/paths.pcvd b/Art/paths.pcvd
index abcbe20..c4c9420 100644
Binary files a/Art/paths.pcvd and b/Art/paths.pcvd differ
diff --git a/Package.swift b/Package.swift
index 299ce03..5b54e82 100644
--- a/Package.swift
+++ b/Package.swift
@@ -36,7 +36,7 @@ let package = Package(
// A microframework for cleaning handling image conversion
.package(
url: "https://github.com/dagronf/SwiftImageReadWrite",
- .upToNextMinor(from: "1.6.1")
+ .upToNextMinor(from: "1.7.2")
),
],
targets: [
diff --git a/README.md b/README.md
index 9f358e2..f508c4d 100644
--- a/README.md
+++ b/README.md
@@ -379,6 +379,7 @@ however you can supply a `PixelShape` object to custom-draw the data. There are
|
|"flower"|`QRCode.PixelShape.Flower`|A 'flower' style|
|
|"horizontal"|`QRCode.PixelShape.Horizontal`|The pixels are horizonally joined to make continuous horizontal bars|
|
|"pointy"|`QRCode.PixelShape.Pointy`|A 'pointy' style|
+|
|"razor"|`QRCode.PixelShape.Razor`| A 'razor' style|
|
|"roundedEndIndent"|`QRCode.PixelShape.RoundedEndIndent`|Rounded path with circular indented ends|
|
|"roundedPath"|`QRCode.PixelShape.RoundedPath`|A smooth rounded-edge path|
|
|"roundedRect"|`QRCode.PixelShape.RoundedRect`|A basic rounded rectangle pixel with configurable radius|
diff --git a/Sources/QRCode/styles/data/QRCodePixelShapeFactory.swift b/Sources/QRCode/styles/data/QRCodePixelShapeFactory.swift
index c670501..a5a10d8 100644
--- a/Sources/QRCode/styles/data/QRCodePixelShapeFactory.swift
+++ b/Sources/QRCode/styles/data/QRCodePixelShapeFactory.swift
@@ -37,12 +37,13 @@ import Foundation
QRCode.PixelShape.Vertical.self,
QRCode.PixelShape.Flower.self,
QRCode.PixelShape.Horizontal.self,
- QRCode.PixelShape.RoundedPath.self,
QRCode.PixelShape.Pointy.self,
QRCode.PixelShape.CurvePixel.self,
+ QRCode.PixelShape.Razor.self,
+ QRCode.PixelShape.RoundedEndIndent.self,
+ QRCode.PixelShape.RoundedPath.self,
QRCode.PixelShape.Sharp.self,
QRCode.PixelShape.Star.self,
- QRCode.PixelShape.RoundedEndIndent.self,
QRCode.PixelShape.Shiny.self,
].sorted(by: { a, b in a.Title < b.Title })
super.init()
diff --git a/Sources/QRCode/styles/data/QRCodePixelShapeRazor.swift b/Sources/QRCode/styles/data/QRCodePixelShapeRazor.swift
new file mode 100644
index 0000000..3031284
--- /dev/null
+++ b/Sources/QRCode/styles/data/QRCodePixelShapeRazor.swift
@@ -0,0 +1,191 @@
+//
+// QRCodePixelShapeRazor.swift
+//
+// Copyright © 2024 Darren Ford. All rights reserved.
+//
+// MIT license
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial
+// portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+// OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+import Foundation
+import CoreGraphics
+
+public extension QRCode.PixelShape {
+ @objc(QRCodePixelShapeRazor) class Razor: NSObject, QRCodePixelShapeGenerator {
+ /// The generator name
+ @objc public static let Name: String = "razor"
+ /// The generator title
+ @objc public static var Title: String { "Razor" }
+ /// Create an instance of this path generator with the specified settings
+ @objc public static func Create(_ settings: [String: Any]?) -> any QRCodePixelShapeGenerator { Razor() }
+
+ /// Make a copy of the object
+ @objc public func copyShape() -> any QRCodePixelShapeGenerator { Razor() }
+
+ static let templateSquare: CGPath = {
+ CGPath(rect: CGRect(origin: .zero, size: .init(width: 10, height: 10)), transform: nil)
+ }()
+
+ static let templatePointingDown: CGPath = {
+ let p = CGMutablePath()
+ p.move(to: CGPoint(x: 0, y: 0))
+ p.line(to: CGPoint(x: 10, y: 0))
+ p.line(to: CGPoint(x: 10, y: 10))
+ p.curve(to: CGPoint(x: 2, y: 8), controlPoint1: CGPoint(x: 10, y: 10), controlPoint2: CGPoint(x: 4.5, y: 9.5))
+ p.curve(to: CGPoint(x: 0, y: 4), controlPoint1: CGPoint(x: -0.5, y: 6.5), controlPoint2: CGPoint(x: 0, y: 4))
+ p.line(to: CGPoint(x: 0, y: 0))
+ p.close()
+ return p
+ }()
+
+ static let templatePointingUp: CGPath = {
+ let p = CGMutablePath()
+ p.move(to: CGPoint(x: 0, y: 0))
+ p.curve(to: CGPoint(x: 8, y: 2), controlPoint1: CGPoint(x: 0, y: 0), controlPoint2: CGPoint(x: 5.5, y: 0.5))
+ p.curve(to: CGPoint(x: 10, y: 6), controlPoint1: CGPoint(x: 10.5, y: 3.5), controlPoint2: CGPoint(x: 10, y: 6))
+ p.line(to: CGPoint(x: 10, y: 10))
+ p.line(to: CGPoint(x: 0, y: 10))
+ p.line(to: CGPoint(x: 0, y: 0))
+ p.close()
+ return p
+ }()
+
+ static let templatePointingRight: CGPath = {
+ let p = CGMutablePath()
+ p.move(to: CGPoint(x: 0, y: 0))
+ p.line(to: CGPoint(x: 10, y: 0))
+ p.curve(to: CGPoint(x: 8, y: 8), controlPoint1: CGPoint(x: 10, y: 0), controlPoint2: CGPoint(x: 9.5, y: 5.5))
+ p.curve(to: CGPoint(x: 4, y: 10), controlPoint1: CGPoint(x: 6.5, y: 10.5), controlPoint2: CGPoint(x: 4, y: 10))
+ p.line(to: CGPoint(x: 0, y: 10))
+ p.line(to: CGPoint(x: 0, y: 0))
+ p.close()
+ return p
+ }()
+
+ static let templatePointingLeft: CGPath = {
+ let p = CGMutablePath()
+ p.move(to: CGPoint(x: 2, y: 2))
+ p.curve(to: CGPoint(x: 6, y: 0), controlPoint1: CGPoint(x: 3.5, y: -0.5), controlPoint2: CGPoint(x: 6, y: 0))
+ p.line(to: CGPoint(x: 10, y: 0))
+ p.line(to: CGPoint(x: 10, y: 10))
+ p.line(to: CGPoint(x: 0, y: 10))
+ p.curve(to: CGPoint(x: 2, y: 2), controlPoint1: CGPoint(x: 0, y: 10), controlPoint2: CGPoint(x: 0.5, y: 4.5))
+
+ p.close()
+ return p
+ }()
+ }
+}
+
+public extension QRCode.PixelShape.Razor {
+ /// Generate a CGPath from the matrix contents
+ /// - Parameters:
+ /// - matrix: The matrix to generate
+ /// - size: The size of the resulting CGPath
+ /// - Returns: A path
+ @objc func generatePath(from matrix: BoolMatrix, size: CGSize) -> CGPath {
+ let dx = size.width / CGFloat(matrix.dimension)
+ let dy = size.height / CGFloat(matrix.dimension)
+ let dm = min(dx, dy)
+
+ let xoff = (size.width - (CGFloat(matrix.dimension) * dm)) / 2.0
+ let yoff = (size.height - (CGFloat(matrix.dimension) * dm)) / 2.0
+
+ // The scale required to convert our template paths to output path size
+ let w = QRCode.PixelShape.RoundedPath.DefaultSize.width
+ let scaleTransform = CGAffineTransform(scaleX: dm / w, y: dm / w)
+
+ let path = CGMutablePath()
+
+ for row in 0 ..< matrix.dimension {
+ for col in 0 ..< matrix.dimension {
+ guard matrix[row, col] == true else { continue }
+
+ let hasLeft: Bool = {
+ if col == 0 { return false }
+ return (col - 1) >= 0 ? matrix[row, col - 1] : false
+ }()
+ let hasRight: Bool = {
+ if col == (matrix.dimension - 1) { return false }
+ return (col + 1) < matrix.dimension ? matrix[row, col + 1] : false
+ }()
+ let hasTop: Bool = {
+ if row == 0 { return false }
+ return (row - 1) >= 0 ? matrix[row - 1, col] : false
+ }()
+ let hasBottom: Bool = {
+ if row == (matrix.dimension - 1) { return false }
+ return (row + 1) < matrix.dimension ? matrix[row + 1, col] : false
+ }()
+
+ let translate = CGAffineTransform(translationX: CGFloat(col) * dm + xoff, y: CGFloat(row) * dm + yoff)
+
+ if !hasLeft, !hasRight, !hasTop, !hasBottom {
+ // isolated block
+ path.addPath(
+ Self.templateSquare,
+ transform: scaleTransform.concatenating(translate)
+ )
+ }
+ else if !hasLeft, !hasRight, !hasTop, hasBottom {
+ // pointing up block
+ path.addPath(
+ Self.templatePointingUp,
+ transform: scaleTransform.concatenating(translate)
+ )
+ }
+ else if !hasLeft, !hasRight, !hasBottom, hasTop {
+ // pointing down block
+ path.addPath(
+ Self.templatePointingDown,
+ transform: scaleTransform.concatenating(translate)
+ )
+ }
+ else if !hasTop, !hasRight, !hasBottom, hasLeft {
+ // pointing right block
+ path.addPath(
+ Self.templatePointingRight,
+ transform: scaleTransform.concatenating(translate)
+ )
+ }
+ else if !hasTop, !hasLeft, !hasBottom, hasRight {
+ // pointing left block
+ path.addPath(
+ Self.templatePointingLeft,
+ transform: scaleTransform.concatenating(translate)
+ )
+ }
+ else {
+ path.addPath(
+ Self.templateSquare,
+ transform: scaleTransform.concatenating(translate)
+ )
+ }
+ }
+ }
+ return path
+ }
+}
+
+// MARK: - Settings
+
+public extension QRCode.PixelShape.Razor {
+ /// Does the shape generator support setting values for a particular key?
+ @objc func supportsSettingValue(forKey key: String) -> Bool { false }
+ /// Returns a storable representation of the shape handler
+ @objc func settings() -> [String: Any] { return [:] }
+ /// Set a configuration value for a particular setting string
+ @objc func setSettingValue(_ value: Any?, forKey key: String) -> Bool { false }
+}
diff --git a/Sources/QRCode/views/QRCodeView.swift b/Sources/QRCode/views/QRCodeView.swift
index ad71e9e..ad03950 100644
--- a/Sources/QRCode/views/QRCodeView.swift
+++ b/Sources/QRCode/views/QRCodeView.swift
@@ -102,7 +102,7 @@ import UIKit
self.rebuildQRCode()
}
-#if os(iOS) || os(visionOS) || os(tvOS)
+#if os(iOS)
public override func didMoveToWindow() {
super.didMoveToWindow()
if self.supportsDrag {
@@ -370,7 +370,7 @@ extension QRCodeView: NSPasteboardItemDataProvider {
}
#endif
-#if os(iOS) || os(tvOS) || os(visionOS)
+#if os(iOS)
extension QRCodeView: UIDragInteractionDelegate {
public func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {