Skip to content

Commit 13e4ba9

Browse files
committed
Added some conveniences for generation and detection
1 parent b5d14bb commit 13e4ba9

3 files changed

Lines changed: 165 additions & 3 deletions

File tree

README.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ separating these out into two different libraries is because video detection **r
117117
putting it on the app store - something that you may not want if your app doesn't need it!
118118

119119
* For QR code generation, link against `QRCode`
120-
* For QR code detection, link against `QRCodeDetector`
120+
* For QR code video detection, link against `QRCodeDetector`
121121

122122
### In your source
123123

@@ -176,6 +176,12 @@ let loadedDoc = try QRCode.Document(jsonData: jsonData)
176176

177177
</details>
178178

179+
There are also some extensions on `CGImage` to help making qr codes even easier
180+
181+
```swift
182+
let qrCodeImage = CGImage.qrCode("Hi there!", dimension: 800)
183+
```
184+
179185
## Settings
180186

181187
### Set the data content
@@ -260,8 +266,6 @@ square, circle, rounded rectangle, and more.
260266
|<img src="./Art/images/eye_edges.png" width="60"/> |"edges"|`QRCode.EyeShape.Edges`| Simple bordered bars with a configurable corner radius |
261267
|<img src="./Art/images/eye_shield.png" width="60"/> |"shield"|`QRCode.EyeShape.Shield`| A shield with configurable corners |
262268

263-
264-
265269
### Custom Pupil shape (optional)
266270

267271
You can provide an override to the default `EyeShape` pupil shape to change just the shape of the pupil. There are built-in generators for square, circle, rounded rectangle, and more.
@@ -640,6 +644,12 @@ Produces a CGPath representation of the QRCode
640644

641645
The components allow the caller to generate individual paths for the QR code components which can then be individually styled and recombined later on.
642646

647+
There are also extensions on `CGPath` to make it even easier to generate a `CGPath` from a qr code.
648+
649+
```swift
650+
let qrcodePath = CGPath.qrCode("This is a test!!!", dimension: 800)
651+
```
652+
643653
### Generating a styled image
644654

645655
```swift
@@ -916,6 +926,13 @@ if let detected = QRCode.DetectQRCodes(in: /*some image*/),
916926
}
917927
```
918928

929+
Even easier, there is an extension on `CGImage` to detect the strings encoded within the image if you only want the string content for each match.
930+
931+
```swift
932+
let image = CGImage(...)
933+
let messages = image.qrCodedMessages()
934+
```
935+
919936
### From a video stream
920937

921938
In order to allow `QRCode` to be used in App Store or Test Flight targets without having to allow Camera usage,
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//
2+
// QRCode+Conveniences.swift
3+
//
4+
// Copyright © 2023 Darren Ford. All rights reserved.
5+
//
6+
// MIT license
7+
//
8+
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
9+
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
10+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
11+
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all copies or substantial
14+
// portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17+
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
18+
// OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
//
21+
22+
// Some conveniences for creating and detecting QR codes
23+
24+
import Foundation
25+
import CoreGraphics
26+
27+
public extension CGImage {
28+
/// Create a CGImage containing a qr code
29+
/// - Parameters:
30+
/// - text: The text to encode in the qr code
31+
/// - dimension: The size in pixels of the output image
32+
/// - errorCorrection: The error correction
33+
/// - shape: The shape to use, or nil for default
34+
/// - style: The style to use, or nil for default
35+
/// - Returns: The image representation of the qr code, or nil if an error occurred
36+
static func qrCode(
37+
_ text: String,
38+
dimension: Int,
39+
errorCorrection: QRCode.ErrorCorrection = .high,
40+
shape: QRCode.Shape? = nil,
41+
style: QRCode.Style? = nil
42+
) -> CGImage? {
43+
let doc = QRCode.Document(utf8String: text, errorCorrection: errorCorrection)
44+
if let shape = shape { doc.design.shape = shape }
45+
if let style = style { doc.design.style = style }
46+
return doc.cgImage(dimension: dimension)
47+
}
48+
49+
/// Create a CGImage containing a qr code
50+
/// - Parameters:
51+
/// - text: The text to encode in the qr code
52+
/// - dimension: The size in pixels of the output image
53+
/// - foregroundColor: The foreground color
54+
/// - foregroundColor: The background color, or nil for default
55+
/// - errorCorrection: The error correction
56+
/// - Returns: The image representation of the qr code, or nil if an error occurred
57+
static func qrCode(
58+
_ text: String,
59+
dimension: Int,
60+
foregroundColor: CGColor,
61+
backgroundColor: CGColor? = nil,
62+
errorCorrection: QRCode.ErrorCorrection = .high,
63+
shape: QRCode.Style? = nil
64+
) -> CGImage? {
65+
let doc = QRCode.Document(utf8String: text, errorCorrection: errorCorrection)
66+
doc.design.foregroundColor(foregroundColor)
67+
doc.design.backgroundColor(backgroundColor)
68+
return doc.cgImage(dimension: dimension)
69+
}
70+
}
71+
72+
public extension CGPath {
73+
/// Simple convenience for creating a CGPath representation of a qr code
74+
/// - Parameters:
75+
/// - text: The text to encode in the qr code
76+
/// - dimension: The size in pixels of the output image
77+
/// - errorCorrection: The error correction
78+
/// - shape: The shape to use, or nil for default
79+
/// - Returns: The path representation of the qr code, or nil if an error occurred
80+
static func qrCode(
81+
_ text: String,
82+
dimension: Int,
83+
errorCorrection: QRCode.ErrorCorrection = .high,
84+
shape: QRCode.Shape? = nil
85+
) -> CGPath? {
86+
let doc = QRCode.Document(utf8String: text, errorCorrection: errorCorrection)
87+
if let shape = shape { doc.design.shape = shape }
88+
return doc.path(dimension: dimension)
89+
}
90+
}
91+
92+
#if !os(watchOS)
93+
public extension CGImage {
94+
/// Returns all qrcode messages that were encoded in this image
95+
/// - Returns: An array of detected qr code strings
96+
func qrCodedMessages() -> [String] {
97+
let features = QRCode.DetectQRCodes(self)
98+
return features.compactMap { $0.messageString }
99+
}
100+
}
101+
#endif

Tests/QRCodeTests/QRCodeTests.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,4 +372,48 @@ final class QRCodeTests: XCTestCase {
372372
XCTAssertEqual(nsi.representations[0].pixelsHigh, 72)
373373
#endif
374374
}
375+
376+
func stringsFromQRCode(_ cgImage: CGImage) -> [String] {
377+
let features = QRCode.DetectQRCodes(cgImage)
378+
return features.compactMap { $0.messageString }
379+
}
380+
381+
func testCGImageQuickGen() throws {
382+
do {
383+
let image = try XCTUnwrap(CGImage.qrCode("This is a test!!!", dimension: 300))
384+
XCTAssertEqual(image.width, 300)
385+
XCTAssertEqual(image.height, 300)
386+
XCTAssertEqual(image.qrCodedMessages(), ["This is a test!!!"])
387+
}
388+
389+
do {
390+
let path = try XCTUnwrap(CGPath.qrCode("This is a test!!!", dimension: 300))
391+
392+
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
393+
let colorSpace = CGColorSpace(name: CGColorSpace.sRGB)!
394+
let ctx = try XCTUnwrap(CGContext(
395+
data: nil,
396+
width: 300,
397+
height: 300,
398+
bitsPerComponent: 8,
399+
bytesPerRow: 300 * 4,
400+
space: colorSpace,
401+
bitmapInfo: bitmapInfo.rawValue
402+
)
403+
)
404+
405+
ctx.scaleBy(x: 1, y: -1)
406+
ctx.translateBy(x: 0, y: -300)
407+
408+
ctx.setFillColor(.white)
409+
ctx.fill([CGRect(x: 0, y: 0, width: 300, height: 300)])
410+
411+
ctx.addPath(path)
412+
ctx.setFillColor(.black)
413+
ctx.fillPath()
414+
415+
let image = try XCTUnwrap(ctx.makeImage())
416+
XCTAssertEqual(image.qrCodedMessages(), ["This is a test!!!"])
417+
}
418+
}
375419
}

0 commit comments

Comments
 (0)