├── .github
└── workflows
│ ├── darwin.yml
│ ├── deploy.yml
│ └── linux.yml
├── .gitignore
├── LICENSE
├── Package.swift
├── README.md
├── Sources
└── QRCodeGenerator
│ ├── BitBuffer.swift
│ ├── QRCode.swift
│ ├── QRCodeConstants.swift
│ ├── QRCodeECC.swift
│ ├── QRCodeError.swift
│ ├── QRCodeMask.swift
│ ├── QRCodeVersion.swift
│ └── QRSegment.swift
├── SwiftQRCodeGenerator.podspec
└── Tests
├── LinuxMain.swift
└── QRCodeGeneratorTests
├── QRCodeGeneratorTests.swift
└── XCTestManifests.swift
/.github/workflows/darwin.yml:
--------------------------------------------------------------------------------
1 | name: Darwin
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: macOS-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v2
11 | - name: Build SPM
12 | run: swift build
13 | - name: Test SPM
14 | run: swift test
15 | - name: Lint Podspec
16 | run: pod lib lint --allow-warnings --use-libraries
17 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy
2 |
3 | on:
4 | push:
5 | branches:
6 | - "!*"
7 | tags:
8 | - "v*"
9 |
10 | jobs:
11 | deploy:
12 | runs-on: macOS-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 | - uses: ncipollo/release-action@v1
17 | name: Create GitHub Release
18 | with:
19 | token: ${{ secrets.GITHUB_TOKEN }}
20 | - name: Deploy to CocoaPods
21 | env:
22 | COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
23 | run: |
24 | set -eo pipefail
25 | pod spec lint --allow-warnings
26 | pod trunk push --allow-warnings
27 |
--------------------------------------------------------------------------------
/.github/workflows/linux.yml:
--------------------------------------------------------------------------------
1 | name: Linux
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-20.04
8 | strategy:
9 | matrix:
10 | swift: ['5.4']
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: fwal/setup-swift@v1
15 | with:
16 | swift-version: ${{ matrix.swift }}
17 | - name: Build
18 | run: swift build
19 | - name: Test
20 | run: swift test
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .build
3 | .vscode
4 | .idea
5 | Packages
6 | *.xcodeproj
7 | *.swp
8 | xcuserdata/
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Project Nayuki. (MIT License)
4 | Copyright (c) 2020 fwcd
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "swift-qrcode-generator",
8 | products: [
9 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
10 | .library(
11 | name: "QRCodeGenerator",
12 | targets: ["QRCodeGenerator"]
13 | )
14 | ],
15 | dependencies: [
16 | // Dependencies declare other packages that this package depends on.
17 | // .package(url: /* package url */, from: "1.0.0"),
18 | ],
19 | targets: [
20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
21 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
22 | .target(
23 | name: "QRCodeGenerator",
24 | dependencies: []
25 | ),
26 | .testTarget(
27 | name: "QRCodeGeneratorTests",
28 | dependencies: ["QRCodeGenerator"]
29 | )
30 | ]
31 | )
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Swift QR Code Generator
2 |
3 | [](https://github.com/fwcd/swift-qrcode-generator/actions/workflows/linux.yml)
4 | [](https://github.com/fwcd/swift-qrcode-generator/actions/workflows/darwin.yml)
5 |
6 | A QR code generator written in pure Swift with no dependencies.
7 |
8 | The project is mostly a direct translation of [Nayuki's](https://github.com/nayuki/) [QR code generator for Rust](https://github.com/nayuki/QR-Code-generator/tree/master/rust), with small changes applied to make the code more idiomatic in Swift.
9 |
10 | ## Usage
11 |
12 | ### Swift Package Manager
13 | To use in your project, add the following dependency to your `Package.swift`:
14 |
15 | ```swift
16 | .package(url: "https://github.com/fwcd/swift-qrcode-generator.git", from: "1.0.0")
17 | ```
18 |
19 | ### CocoaPods
20 | Swift QR Code Generator is available through [CocoaPods](http://cocoapods.org). To install
21 | it, simply add the following line to your Podfile:
22 |
23 | ```bash
24 | pod 'SwiftQRCodeGenerator'
25 | ```
26 |
27 | ## Example
28 | ```swift
29 | import QRCodeGenerator
30 |
31 | let qr = try! QRCode.encode(text: text, ecl: .medium)
32 | let svg = qr.toSVGString(border: 4)
33 | ```
34 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/BitBuffer.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * QR Code generator library (Swift)
3 | *
4 | * Copyright (c) Project Nayuki. (MIT License)
5 | * https://www.nayuki.io/page/qr-code-generator-library
6 | * Copyright (c) 2020 fwcd
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | * this software and associated documentation files (the "Software"), to deal in
10 | * the Software without restriction, including without limitation the rights to
11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12 | * the Software, and to permit persons to whom the Software is furnished to do so,
13 | * subject to the following conditions:
14 | * - The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | * - The Software is provided "as is", without warranty of any kind, express or
17 | * implied, including but not limited to the warranties of merchantability,
18 | * fitness for a particular purpose and noninfringement. In no event shall the
19 | * authors or copyright holders be liable for any claim, damages or other
20 | * liability, whether in an action of contract, tort or otherwise, arising from,
21 | * out of or in connection with the Software or the use or other dealings in the
22 | * Software.
23 | */
24 |
25 | /// An appendable sequence of bits (0s and 1s).
26 | public struct BitBuffer {
27 | public var bits: [Bool]
28 | public var count: UInt { UInt(bits.count) }
29 |
30 | public init(_ bits: [Bool] = []) {
31 | self.bits = bits
32 | }
33 |
34 | /// Appends the given number of low-order bits of the given value to this buffer.
35 | ///
36 | /// Requires len ≤ 31 and val < 2len.
37 | public mutating func appendBits(_ value: UInt32, _ length: Int) {
38 | assert(length <= 31 && (value >> length) == 0, "Value out of range")
39 | bits += (0.. Bool {
45 | (x >> i) & 1 != 0
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/QRCode.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * QR Code generator library (Swift)
3 | *
4 | * Copyright (c) Project Nayuki. (MIT License)
5 | * https://www.nayuki.io/page/qr-code-generator-library
6 | * Copyright (c) 2020 fwcd
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | * this software and associated documentation files (the "Software"), to deal in
10 | * the Software without restriction, including without limitation the rights to
11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12 | * the Software, and to permit persons to whom the Software is furnished to do so,
13 | * subject to the following conditions:
14 | * - The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | * - The Software is provided "as is", without warranty of any kind, express or
17 | * implied, including but not limited to the warranties of merchantability,
18 | * fitness for a particular purpose and noninfringement. In no event shall the
19 | * authors or copyright holders be liable for any claim, damages or other
20 | * liability, whether in an action of contract, tort or otherwise, arising from,
21 | * out of or in connection with the Software or the use or other dealings in the
22 | * Software.
23 | */
24 |
25 | import Foundation
26 |
27 | /// A QR code represented as a matrix of black/white modules.
28 | /// Contains factory methods for encoding QR codes from text
29 | /// or binary.
30 | public struct QRCode {
31 | // Scalar parameters:
32 |
33 | /// The version number of this QR Code, which is between 1 and 40 (inclusive).
34 | /// This determines the size of this barcode.
35 | public let version: QRCodeVersion
36 | /// The width and height of this QR Code, measured in modules, between
37 | /// 21 and 177 (inclusive). This is equal to version * 4 + 17.
38 | public let size: Int
39 | /// The error correction level used in this QR Code.
40 | public let errorCorrectionLevel: QRCodeECC
41 | /// The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).
42 | /// Even if a QR Code is created with automatic masking requested (mask = None),
43 | /// the resulting object still has a mask value between 0 and 7.
44 | public private(set) var mask: QRCodeMask
45 |
46 | // Grids of modules/pixels, with dimensions of size*size:
47 |
48 | /// The modules of this QR Code (false = white, true = black).
49 | /// Immutable after constructor finishes. Accessed through subscripts.
50 | private var modules: [Bool]
51 |
52 | /// Indicates function modules that are not subjected to masking. Discarded when constructor finishes.
53 | private var isFunction: [Bool]
54 |
55 | /*---- Static factory functions (high level) ----*/
56 |
57 | /// Returns a QR Code representing the given Unicode text string at the given error correction level.
58 | ///
59 | /// As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode
60 | /// code points (not UTF-8 code units) if the low error correction level is used. The smallest possible
61 | /// QR Code version is automatically chosen for the output. The ECC level of the result may be higher than
62 | /// the ecl argument if it can be done without increasing the version.
63 | ///
64 | /// Returns a `QrCode` if successful, or throws if the
65 | /// data is too long to fit in any version at the given ECC level.
66 | public static func encode(text: String, ecl: QRCodeECC) throws -> Self {
67 | let chrs = Array(text)
68 | let segs = QRSegment.makeSegments(chrs)
69 | return try QRCode.encode(segments: segs, ecl: ecl)
70 | }
71 |
72 | /// Returns a QR Code representing the given binary data at the given error correction level.
73 | ///
74 | /// This function always encodes using the binary segment mode, not any text mode. The maximum number of
75 | /// bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.
76 | /// The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.
77 | ///
78 | /// Returns a `QrCode` if successful, or throws if the
79 | /// data is too long to fit in any version at the given ECC level.
80 | public static func encode(binary data: [UInt8], ecl: QRCodeECC) throws -> Self {
81 | let segs = [QRSegment.makeBytes(data)]
82 | return try QRCode.encode(segments: segs, ecl: ecl)
83 | }
84 |
85 | /*---- Static factory functions (mid level) ----*/
86 |
87 | /// Returns a QR Code representing the given segments at the given error correction level.
88 | ///
89 | /// The smallest possible QR Code version within the given range is automatically
90 | /// chosen for the output. Iff boostecl is `true`, then the ECC level of the result
91 | /// may be higher than the ecl argument if it can be done without increasing the
92 | /// version. The mask number is either between 0 to 7 (inclusive) to force that
93 | /// mask, or `None` to automatically choose an appropriate mask (which may be slow).
94 | ///
95 | /// This function allows the user to create a custom sequence of segments that switches
96 | /// between modes (such as alphanumeric and byte) to encode text in less space.
97 | /// This is a mid-level API; the high-level API is `encode(text:)` and `encode(binary:)`.
98 | ///
99 | /// Returns a `QrCode` if successful, or throws if the data is too
100 | /// long to fit in any version in the given range at the given ECC level.
101 | public static func encode(segments: [QRSegment], ecl: QRCodeECC, minVersion: QRCodeVersion = .min, maxVersion: QRCodeVersion = .max, mask: QRCodeMask? = nil, boostECL: Bool = true) throws -> Self {
102 | assert(minVersion <= maxVersion, "Invalid value")
103 | var mutECL = ecl
104 |
105 | // Find the minimal version number to use
106 | var version = minVersion
107 | var dataUsedBits: UInt!
108 | while true {
109 | // Number of data bits available
110 | let dataCapacityBits: UInt = QRCode.getNumDataCodewords(version: version, ecl: mutECL) * 8
111 | let dataUsed: UInt? = QRSegment.getTotalBits(segments: segments, version: version)
112 | if let used = dataUsed, used <= dataCapacityBits {
113 | // The version number is found to be suitable
114 | dataUsedBits = used
115 | break
116 | } else if version >= maxVersion {
117 | let msg: String
118 | if let used = dataUsed {
119 | msg = "Data length = \(used) bits, Max capacity = \(dataCapacityBits) bits"
120 | } else {
121 | msg = "Segment too long"
122 | }
123 | throw QRCodeError.dataTooLong(msg)
124 | } else {
125 | version = QRCodeVersion(version.value + 1)
126 | }
127 | }
128 |
129 | // Increase error correction level while the data still fits in the current version number
130 | for newECL in [QRCodeECC.medium, QRCodeECC.quartile, QRCodeECC.high] {
131 | if boostECL && dataUsedBits <= QRCode.getNumDataCodewords(version: version, ecl: newECL) * 8 {
132 | mutECL = newECL
133 | }
134 | }
135 |
136 | // Concatenate all segments to create the data bit string
137 | var bb = BitBuffer()
138 | for seg in segments {
139 | bb.appendBits(seg.mode.modeBits, 4)
140 | bb.appendBits(UInt32(seg.numChars), Int(seg.mode.numCharCountBits(version: version)))
141 | bb.bits += seg.data
142 | }
143 |
144 | assert(bb.count == dataUsedBits)
145 |
146 | // Add terminator and pad up to a byte if applicable
147 | let dataCapacityBits: UInt = QRCode.getNumDataCodewords(version: version, ecl: mutECL) * 8
148 | assert(bb.count <= dataCapacityBits)
149 | var numZeroBits = min(4, dataCapacityBits - bb.count)
150 | bb.appendBits(0, Int(numZeroBits))
151 | numZeroBits = (0 &- bb.count) & 7
152 | bb.appendBits(0, Int(numZeroBits))
153 | assert(bb.count % 8 == 0)
154 |
155 | // Pad with alternating bytes until data capacity is reached
156 | let padBytes = [0xEC, 0x11]
157 | var i = 0
158 | while bb.count < dataCapacityBits {
159 | bb.appendBits(UInt32(padBytes[i]), 8)
160 | i += 1
161 | if i >= padBytes.count {
162 | i = 0
163 | }
164 | }
165 |
166 | // Pack bits into bytes in big endian
167 | var dataCodeWords = [UInt8](repeating: 0, count: Int(bb.count / 8))
168 | for (i, bit) in bb.bits.enumerated() {
169 | dataCodeWords[i >> 3] |= (bit ? 1 : 0) << (7 - (i & 7))
170 | }
171 |
172 | // Create the QRCode object
173 | return QRCode.encodeCodewords(version: version, ecl: mutECL, dataCodeWords: dataCodeWords, mask: mask)
174 | }
175 |
176 | /*---- Constructor (low level) ----*/
177 |
178 | /// Creates a new QR Code with the given version number,
179 | /// error correction level, data codeword bytes, and mask number.
180 | ///
181 | /// This is a low-level API that most users should not use directly.
182 | /// A mid-level API is the `encodeSegments()` function.
183 | public static func encodeCodewords(version: QRCodeVersion, ecl: QRCodeECC, dataCodeWords: [UInt8], mask: QRCodeMask? = nil) -> Self {
184 | var mutMask = mask
185 |
186 | // Initialize fields
187 | let size = UInt(version.value) * 4 + 17
188 | var result = Self(
189 | version: version,
190 | size: Int(size),
191 | errorCorrectionLevel: ecl,
192 | mask: QRCodeMask(0), // Dummy value
193 | modules: Array(repeating: false, count: Int(size * size)), // Initially all white
194 | isFunction: Array(repeating: false, count: Int(size * size))
195 | )
196 |
197 | // Compute ECC, draw modules
198 | result.drawFunctionPatterns()
199 | let allCodeWords = result.addECCAndInterleave(data: dataCodeWords)
200 | result.drawCodewords(data: allCodeWords)
201 |
202 | // Do masking
203 | if mutMask == nil { // Automatically choose best mask
204 | var minPenalty = Int32.max
205 | for i in UInt8(0)..<8 {
206 | let newMask = QRCodeMask(i)
207 | result.apply(mask: newMask)
208 | result.drawFormatBits(mask: newMask)
209 | let penalty = Int32(result.getPenaltyScore())
210 | if penalty < minPenalty {
211 | mutMask = newMask
212 | minPenalty = penalty
213 | }
214 | result.apply(mask: newMask) // Undoes mask due to XOR
215 | }
216 | }
217 | let resMask: QRCodeMask = mutMask!
218 | result.mask = resMask
219 | result.apply(mask: resMask) // Apply the final choice of mask
220 | result.drawFormatBits(mask: resMask)
221 |
222 | result.isFunction = []
223 | return result
224 | }
225 |
226 | /*---- Public methods ----*/
227 |
228 | /// Returns the color of the module (pixel) at the given coordinates,
229 | /// which is `false` for white or `true` for black.
230 | ///
231 | /// The top left corner has the coordinates (x=0, y=0). If the given
232 | /// coordinates are out of bounds, then `false` (white) is returned.
233 | public func getModule(x: Int, y: Int) -> Bool {
234 | 0 <= x && x < size && 0 <= y && y < size && self[x, y]
235 | }
236 |
237 | private subscript(_ x: Int, _ y: Int) -> Bool {
238 | /// Returns the color of the module at the given coordinates, which
239 | /// are assumed to be in bounds.
240 | get { modules[y * size + x] }
241 | /// Sets the color of the module at the given coordintes.
242 | set { modules[y * size + x] = newValue }
243 | }
244 |
245 | /// Returns a string of SVG code for an image depicting
246 | /// this QR Code, with the given number of border modules.
247 | ///
248 | /// The string always uses Unix newlines (\n), regardless of the platform.
249 | public func toSVGString(border: Int, width: Int? = nil, foreground: String = "#000000", background: String? = "#FFFFFF") -> String {
250 | assert(border >= 0, "Border must be non-negative")
251 | let dimension = size + (border * 2)
252 | let path = (0.." } ?? ""
261 | return """
262 |
263 |
264 |
268 | """
269 | }
270 |
271 | /*---- Private helper methods for constructor: Drawing function modules ----*/
272 |
273 | /// Reads this object's version field, and draws and marks all function modules.
274 | private mutating func drawFunctionPatterns() {
275 | // Draw horizontal and vertical timing patterns
276 | for i in 0..> 9) * 0x537)
313 | }
314 | let bits: UInt32 = (data << 10 | rem) ^ 0x5412 // uint15
315 |
316 | // Draw first copy
317 | for i in 0..<6 {
318 | setFunctionModule(x: 8, y: i, isBlack: getBit(bits, Int32(i)))
319 | }
320 | setFunctionModule(x: 8, y: 7, isBlack: getBit(bits, 6))
321 | setFunctionModule(x: 8, y: 8, isBlack: getBit(bits, 7))
322 | setFunctionModule(x: 7, y: 8, isBlack: getBit(bits, 8))
323 | for i in 9..<15 {
324 | setFunctionModule(x: 14 - i, y: 8, isBlack: getBit(bits, Int32(i)))
325 | }
326 |
327 | // Draw second copy
328 | for i in 0..<8 {
329 | setFunctionModule(x: size - 1 - i, y: 8, isBlack: getBit(bits, Int32(i)))
330 | }
331 | for i in 8..<15 {
332 | setFunctionModule(x: 8, y: size - 15 + i, isBlack: getBit(bits, Int32(i)))
333 | }
334 | setFunctionModule(x: 8, y: size - 8, isBlack: true) // Always black
335 | }
336 |
337 | /// Draws two copies of the version bits (with its own error correction code),
338 | /// based on this object's version field, iff 7 <= version <= 40.
339 | private mutating func drawVersion() {
340 | guard version.value >= 7 else { return }
341 |
342 | // Calculate error correction code and pack bits
343 | let data = UInt32(version.value) // uint6, in the range [7, 40]
344 | var rem = data
345 | for _ in 0..<12 {
346 | rem = (rem << 1) ^ ((rem >> 11) * 0x1F25)
347 | }
348 | let bits: UInt32 = data << 12 | rem // uint18
349 | assert(bits >> 18 == 0)
350 |
351 | // Draw two copies
352 | for i in 0..<18 {
353 | let bit = getBit(bits, Int32(i))
354 | let a: Int = size - 11 + i % 3
355 | let b: Int = i / 3
356 | setFunctionModule(x: a, y: b, isBlack: bit)
357 | setFunctionModule(x: b, y: a, isBlack: bit)
358 | }
359 | }
360 |
361 | /// Draws a 9*9 finder pattern including the border separator,
362 | /// with the center module at (x, y). Modules can be out of bounds.
363 | private mutating func drawFinderPattern(x: Int, y: Int) {
364 | for dy in -4...4 {
365 | for dx in -4...4 {
366 | let xx: Int = x + dx
367 | let yy: Int = y + dy
368 | if 0 <= xx && xx < size && 0 <= yy && yy < size {
369 | let dist: Int = max(abs(dx), abs(dy)) // Chebyshev/infinity norm
370 | setFunctionModule(x: xx, y: yy, isBlack: dist != 2 && dist != 4)
371 | }
372 | }
373 | }
374 | }
375 |
376 | /// Draws a 5*5 alignment pattern, with the center module
377 | /// at (x, y). All modules must be in bounds.
378 | private mutating func drawAlignmentPattern(x: Int, y: Int) {
379 | for dy in -2...2 {
380 | for dx in -2...2 {
381 | setFunctionModule(x: x + dx, y: y + dy, isBlack: max(abs(dx), abs(dy)) != 1)
382 | }
383 | }
384 | }
385 |
386 | /// Sets the color of a module and marks it as a function mdoule.
387 | /// Only used by the constructor. Coordinates must be in bounds.
388 | private mutating func setFunctionModule(x: Int, y: Int, isBlack: Bool) {
389 | self[x, y] = isBlack
390 | isFunction[y * size + x] = true
391 | }
392 |
393 | /*---- Private helper methods for constructor: Codewords and masking ----*/
394 |
395 | /// Returns a new byte string representing the given data with the appropriate error correction
396 | /// codewords appended to it, based on this object's version and error correction level.
397 | private func addECCAndInterleave(data: [UInt8]) -> [UInt8] {
398 | let ver = version
399 | let ecl = errorCorrectionLevel
400 | assert(data.count == QRCode.getNumDataCodewords(version: ver, ecl: ecl), "Illegal argument")
401 |
402 | // Calculate parameter numbers
403 | let numBlocks: UInt = QRCode.tableGet(numErrorCorrectionBlocks, version: ver, ecl: ecl)
404 | let blockECCLen: UInt = QRCode.tableGet(eccCodewordsPerBlock, version: ver, ecl: ecl)
405 | let rawCodeWords: UInt = QRCode.getNumRawDataModules(version: ver) / 8
406 | let numShortBlocks: UInt = numBlocks - rawCodeWords % numBlocks
407 | let shortBlockLen: UInt = rawCodeWords / numBlocks
408 |
409 | // Split data into blocks and append ECC to each block
410 | var blocks = [[UInt8]]()
411 | let rsDiv: [UInt8] = QRCode.reedSolomonComputeDivisor(degree: blockECCLen)
412 | var k: UInt = 0
413 | for i in 0..= numShortBlocks ? 1 : 0)
415 | var dat = Array(data[Int(k)..= numShortBlocks {
431 | result.append(block[Int(i)])
432 | }
433 | }
434 | }
435 |
436 | return result
437 | }
438 |
439 | /// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
440 | /// data area of this QR Code. Function modules need to be marked off before this is called.
441 | private mutating func drawCodewords(data: [UInt8]) {
442 | assert(data.count == QRCode.getNumRawDataModules(version: version) / 8, "Illegal argument")
443 |
444 | var i: UInt = 0 // Bit index into the data
445 | // Do the funny zigzag scan
446 | var right: Int = size - 1
447 | while right >= 1 { // Index of right column in each column pair
448 | if right == 6 {
449 | right = 5
450 | }
451 | for vert in 0..> 3)]), 7 - Int32(i & 7))
458 | i += 1
459 | }
460 | // If this QR code has any remainder bits (0 to 7), they were assigned as
461 | // 0/false/white by the constructor and are left unchanged by this method
462 | }
463 | }
464 | right -= 2
465 | }
466 | assert(i == data.count * 8)
467 | }
468 |
469 | // XORs the codeword modules in this QR Code with the given mask pattern.
470 | // The function modules must be marked and the codeword bits must be drawn
471 | // before masking. Due to the arithmetic of XOR, calling applyMask() with
472 | // the same mask value a second time will undo the mask. A final well-formed
473 | // QR Code needs exactly one (not zero, two, etc.) mask applied.
474 | private mutating func apply(mask: QRCodeMask) {
475 | for y in 0.. Int {
497 | var result: Int = 0
498 |
499 | // Adjacent modules in row having same color and finder-like patterns
500 | for y in 0.. 5 {
510 | result += 1
511 | }
512 | } else {
513 | runHistory.addHistory(runLength: runX)
514 | if !runColor {
515 | result += runHistory.countPatterns() * penaltyN3
516 | }
517 | runColor = self[x, y]
518 | runX = 1
519 | }
520 | }
521 | result += runHistory.terminateAndCount(runColor: runColor, runLength: runX) * penaltyN3
522 | }
523 |
524 | // Adjacent modules in column having same color and finder-like patterns
525 | for x in 0.. 5 {
535 | result += 1
536 | }
537 | } else {
538 | runHistory.addHistory(runLength: runY)
539 | if !runColor {
540 | result += runHistory.countPatterns() * penaltyN3
541 | }
542 | runColor = self[x, y]
543 | runY = 1
544 | }
545 | }
546 | result += runHistory.terminateAndCount(runColor: runColor, runLength: runY) * penaltyN3
547 | }
548 |
549 | // 2*2 blocks of modules having same color
550 | for y in 0..<(size - 1) {
551 | for x in 0..<(size - 1) {
552 | let color: Bool = self[x, y]
553 | if color == self[x + 1, y] && color == self[x, y + 1] && color == self[x + 1, y + 1] {
554 | result += penaltyN2
555 | }
556 | }
557 | }
558 |
559 | // Balance of black and white modules
560 | let black: Int = modules.map { $0 ? 1 : 0 }.reduce(0, +)
561 | let total: Int = size * size // Note that size is odd, so black/total != 1/2
562 | // Compute the smallest integer k >= 0 such that (45 - 5k)% <= black/total <= (55+5k)%
563 | let k: Int = (abs(black * 20 - total * 10) + total - 1) / total - 1
564 | result += k * penaltyN4
565 | return result
566 | }
567 |
568 | /*---- Private helper functions ----*/
569 |
570 | /// Returns an ascending list of positions of alignment patterns for this version number.
571 | /// Each position is in the range [0,177), and are used on both the x and y axes.
572 | /// This could be implemented as lookup table of 40 variable-length lists of unsigned bytes.
573 | private func getAlignmentPatternPositions() -> [Int] {
574 | let ver = version.value
575 | if ver == 1 {
576 | return []
577 | } else {
578 | let numAlign = Int(ver) / 7 + 2
579 | let step: Int = (ver == 32) ? 26 : ((Int(ver) * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2)
580 | var result: [Int] = (0..<(numAlign - 1)).map { size - 7 - $0 * step }
581 | result.append(6)
582 | result.reverse()
583 | return result
584 | }
585 | }
586 |
587 | /// Returns the number of data bits that can be stored in a QR Code of the given version number, after
588 | /// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.
589 | /// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.
590 | private static func getNumRawDataModules(version: QRCodeVersion) -> UInt {
591 | let ver = UInt(version.value)
592 | var result: UInt = (16 * ver + 128) * ver + 64
593 | if ver >= 2 {
594 | let numAlign: UInt = ver / 7 + 2
595 | result -= (25 * numAlign - 10) * numAlign - 55
596 | if ver >= 7 {
597 | result -= 36
598 | }
599 | }
600 | assert(208 <= result && result <= 29648)
601 | return result
602 | }
603 |
604 | /// Returns the number of 8-bit data (i.e. not error correction) codewords contained in any
605 | /// QR Code of the given version number and error correction level, with remainder bits discarded.
606 | /// This stateless pure function could be implemented as a (40*4)-cell lookup table.
607 | private static func getNumDataCodewords(version: QRCodeVersion, ecl: QRCodeECC) -> UInt {
608 | QRCode.getNumRawDataModules(version: version) / 8
609 | - QRCode.tableGet(eccCodewordsPerBlock, version: version, ecl: ecl)
610 | * QRCode.tableGet(numErrorCorrectionBlocks, version: version, ecl: ecl)
611 | }
612 |
613 | /// Returns an entry from the given table based on the given values.
614 | private static func tableGet(_ table: [[Int]], version: QRCodeVersion, ecl: QRCodeECC) -> UInt {
615 | UInt(table[Int(ecl.ordinal)][Int(version.value)])
616 | }
617 |
618 | /// Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.
619 | private static func reedSolomonComputeDivisor(degree: UInt) -> [UInt8] {
620 | assert(1 <= degree && degree <= 255, "Degree out of range")
621 | // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1.
622 | // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93].
623 | var result = [UInt8](repeating: 0, count: Int(degree - 1))
624 | result.append(1) // Start off with monomial x^0
625 |
626 | // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
627 | // and drop the highest monomial term which is always 1x^degree.
628 | // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
629 | var root: UInt8 = 1
630 | for _ in 0.. [UInt8] {
646 | var result = [UInt8](repeating: 0, count: divisor.count)
647 | for b in data { // Polynomial divison
648 | let factor: UInt8 = b ^ result[...].popFirst()!
649 | result.append(0)
650 | for (i, y) in divisor.enumerated() {
651 | result[i] ^= QRCode.reedSolomonMultiply(x: y, y: factor)
652 | }
653 | }
654 | return result
655 | }
656 |
657 | /// Returns the product of the two given field elements modulo GF(2^8/0x11D).
658 | /// All inputs are valid. This could be implemented as a 256*256 lookup table.
659 | private static func reedSolomonMultiply(x: UInt8, y: UInt8) -> UInt8 {
660 | // Russian peasant multiplication
661 | var z: UInt8 = 0
662 | for i in (0..<8).reversed() {
663 | z = (z << 1) ^ ((z >> 7) * 0x1D)
664 | z ^= ((y >> i) & 1) * x
665 | }
666 | return z
667 | }
668 |
669 | /*---- Helper struct for get_penalty_score() ----*/
670 |
671 | private struct FinderPenalty {
672 | let qrSize: Int
673 | var runHistory: [Int]
674 |
675 | init(_ qrSize: Int) {
676 | self.qrSize = qrSize
677 | runHistory = Array(repeating: 0, count: 7)
678 | }
679 |
680 | /// Pushes the given value to the front and drops the last value.
681 | mutating func addHistory(runLength: Int) {
682 | var currentRunLength = runLength
683 | if runHistory[0] == 0 {
684 | currentRunLength += qrSize
685 | }
686 | for i in (0..<(runHistory.count - 1)).reversed() {
687 | runHistory[i + 1] = runHistory[i]
688 | }
689 | runHistory[0] = currentRunLength
690 | }
691 |
692 | /// Can only be called immediately after a white run is added and
693 | /// returns either 0, 1 or 2.
694 | func countPatterns() -> Int {
695 | let n = runHistory[1]
696 | assert(n <= qrSize * 3)
697 | let core = n > 0 && runHistory[2] == n && runHistory[3] == n && runHistory[4] == n && runHistory[5] == n
698 | return ((core && runHistory[0] >= n * 4 && runHistory[6] >= n) ? 1 : 0)
699 | + ((core && runHistory[6] >= n * 4 && runHistory[0] >= n) ? 1 : 0)
700 | }
701 |
702 | /// Must be called at the end of a line (row or column) of modules.
703 | mutating func terminateAndCount(runColor: Bool, runLength: Int) -> Int {
704 | var currentRunLength = runLength
705 | if runColor { // Terminate black run
706 | addHistory(runLength: runLength)
707 | currentRunLength = 0
708 | }
709 | currentRunLength += qrSize // Add white border to final run
710 | addHistory(runLength: currentRunLength)
711 | return countPatterns()
712 | }
713 | }
714 | }
715 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/QRCodeConstants.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * QR Code generator library (Swift)
3 | *
4 | * Copyright (c) Project Nayuki. (MIT License)
5 | * https://www.nayuki.io/page/qr-code-generator-library
6 | * Copyright (c) 2020 fwcd
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | * this software and associated documentation files (the "Software"), to deal in
10 | * the Software without restriction, including without limitation the rights to
11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12 | * the Software, and to permit persons to whom the Software is furnished to do so,
13 | * subject to the following conditions:
14 | * - The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | * - The Software is provided "as is", without warranty of any kind, express or
17 | * implied, including but not limited to the warranties of merchantability,
18 | * fitness for a particular purpose and noninfringement. In no event shall the
19 | * authors or copyright holders be liable for any claim, damages or other
20 | * liability, whether in an action of contract, tort or otherwise, arising from,
21 | * out of or in connection with the Software or the use or other dealings in the
22 | * Software.
23 | */
24 |
25 | // For use in getPenaltyScore(), when evaluating which mask is best.
26 | public let penaltyN1: Int = 3
27 | public let penaltyN2: Int = 3
28 | public let penaltyN3: Int = 40
29 | public let penaltyN4: Int = 10
30 |
31 | public let eccCodewordsPerBlock: [[Int]] = [
32 | // Version: (note that index 0 is for padding, and is set to an illegal value)
33 | //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
34 | [-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Low
35 | [-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28], // Medium
36 | [-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // Quartile
37 | [-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], // High
38 | ]
39 |
40 | public let numErrorCorrectionBlocks: [[Int]] = [
41 | // Version: (note that index 0 is for padding, and is set to an illegal value)
42 | //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
43 | [-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25], // Low
44 | [-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49], // Medium
45 | [-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68], // Quartile
46 | [-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81], // High
47 | ]
48 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/QRCodeECC.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * QR Code generator library (Swift)
3 | *
4 | * Copyright (c) Project Nayuki. (MIT License)
5 | * https://www.nayuki.io/page/qr-code-generator-library
6 | * Copyright (c) 2020 fwcd
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | * this software and associated documentation files (the "Software"), to deal in
10 | * the Software without restriction, including without limitation the rights to
11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12 | * the Software, and to permit persons to whom the Software is furnished to do so,
13 | * subject to the following conditions:
14 | * - The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | * - The Software is provided "as is", without warranty of any kind, express or
17 | * implied, including but not limited to the warranties of merchantability,
18 | * fitness for a particular purpose and noninfringement. In no event shall the
19 | * authors or copyright holders be liable for any claim, damages or other
20 | * liability, whether in an action of contract, tort or otherwise, arising from,
21 | * out of or in connection with the Software or the use or other dealings in the
22 | * Software.
23 | */
24 |
25 | /// The error correction level in a QR Code symbol.
26 | public enum QRCodeECC: UInt {
27 | /// The QR Code can tolerate about 7% erroneous codewords.
28 | case low = 0
29 | /// The QR Code can tolerate about 15% erroneous codewords.
30 | case medium = 1
31 | /// The QR Code can tolerate about 25% erroneous codewords.
32 | case quartile = 2
33 | /// The QR Code can tolerate about 30% erroneous codewords.
34 | case high = 3
35 |
36 | /// Returns an unsigned 2-bit integer (in the range 0 to 3).
37 | var ordinal: UInt { rawValue }
38 |
39 | /// Returns an unsigned 2-bit integer (in the range 0 to 3).
40 | var formatBits: UInt32 {
41 | switch self {
42 | case .low: return 1
43 | case .medium: return 0
44 | case .quartile: return 3
45 | case .high: return 2
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/QRCodeError.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * QR Code generator library (Swift)
3 | *
4 | * Copyright (c) Project Nayuki. (MIT License)
5 | * https://www.nayuki.io/page/qr-code-generator-library
6 | * Copyright (c) 2020 fwcd
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | * this software and associated documentation files (the "Software"), to deal in
10 | * the Software without restriction, including without limitation the rights to
11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12 | * the Software, and to permit persons to whom the Software is furnished to do so,
13 | * subject to the following conditions:
14 | * - The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | * - The Software is provided "as is", without warranty of any kind, express or
17 | * implied, including but not limited to the warranties of merchantability,
18 | * fitness for a particular purpose and noninfringement. In no event shall the
19 | * authors or copyright holders be liable for any claim, damages or other
20 | * liability, whether in an action of contract, tort or otherwise, arising from,
21 | * out of or in connection with the Software or the use or other dealings in the
22 | * Software.
23 | */
24 |
25 | public enum QRCodeError: Error {
26 | /// The error type when the supplied data does not fit any QR Code version.
27 | ///
28 | /// Ways to handle this exception include:
29 | ///
30 | /// - Decrease the error correction level if it was greater than `QRCodeECC.low`.
31 | /// - If the `encodeSegmentsAdvanced()` function was called, then increase the maxversion
32 | /// argument if it was less than `qrCodeMaxVersion`. (This advice does not apply to the
33 | /// other factory functions because they search all versions up to `qrCodeMaxVersion`.)
34 | /// - Split the text data into better or optimal segments in order to reduce the number of bits required.
35 | /// - Change the text or binary data to be shorter.
36 | /// - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric).
37 | /// - Propagate the error upward to the caller/user.
38 | case dataTooLong(String)
39 | }
40 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/QRCodeMask.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * QR Code generator library (Swift)
3 | *
4 | * Copyright (c) Project Nayuki. (MIT License)
5 | * https://www.nayuki.io/page/qr-code-generator-library
6 | * Copyright (c) 2020 fwcd
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | * this software and associated documentation files (the "Software"), to deal in
10 | * the Software without restriction, including without limitation the rights to
11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12 | * the Software, and to permit persons to whom the Software is furnished to do so,
13 | * subject to the following conditions:
14 | * - The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | * - The Software is provided "as is", without warranty of any kind, express or
17 | * implied, including but not limited to the warranties of merchantability,
18 | * fitness for a particular purpose and noninfringement. In no event shall the
19 | * authors or copyright holders be liable for any claim, damages or other
20 | * liability, whether in an action of contract, tort or otherwise, arising from,
21 | * out of or in connection with the Software or the use or other dealings in the
22 | * Software.
23 | */
24 |
25 | /// A number between 0 and 7 (inclusive).
26 | public struct QRCodeMask: Hashable {
27 | public let value: UInt8
28 |
29 | public init(_ value: UInt8) {
30 | assert(value <= 7, "Mask value out of range")
31 | self.value = value
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/QRCodeVersion.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * QR Code generator library (Swift)
3 | *
4 | * Copyright (c) Project Nayuki. (MIT License)
5 | * https://www.nayuki.io/page/qr-code-generator-library
6 | * Copyright (c) 2020 fwcd
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | * this software and associated documentation files (the "Software"), to deal in
10 | * the Software without restriction, including without limitation the rights to
11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12 | * the Software, and to permit persons to whom the Software is furnished to do so,
13 | * subject to the following conditions:
14 | * - The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | * - The Software is provided "as is", without warranty of any kind, express or
17 | * implied, including but not limited to the warranties of merchantability,
18 | * fitness for a particular purpose and noninfringement. In no event shall the
19 | * authors or copyright holders be liable for any claim, damages or other
20 | * liability, whether in an action of contract, tort or otherwise, arising from,
21 | * out of or in connection with the Software or the use or other dealings in the
22 | * Software.
23 | */
24 |
25 | /// A number between 1 and 40 (inclusive).
26 | public struct QRCodeVersion: Hashable, Comparable {
27 | /// The minimum version number supported in the QR Code Model 2 standard.
28 | public static let min = QRCodeVersion(1)
29 | /// The maximum version number supported in the QR Code Model 2 standard.
30 | public static let max = QRCodeVersion(40)
31 |
32 | public let value: UInt8
33 |
34 | public init(_ value: UInt8) {
35 | assert(1 <= value && value <= 40, "Version number out of range")
36 | self.value = value
37 | }
38 |
39 | public static func <(lhs: QRCodeVersion, rhs: QRCodeVersion) -> Bool {
40 | lhs.value < rhs.value
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Sources/QRCodeGenerator/QRSegment.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * QR Code generator library (Swift)
3 | *
4 | * Copyright (c) Project Nayuki. (MIT License)
5 | * https://www.nayuki.io/page/qr-code-generator-library
6 | * Copyright (c) 2020 fwcd
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | * this software and associated documentation files (the "Software"), to deal in
10 | * the Software without restriction, including without limitation the rights to
11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12 | * the Software, and to permit persons to whom the Software is furnished to do so,
13 | * subject to the following conditions:
14 | * - The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | * - The Software is provided "as is", without warranty of any kind, express or
17 | * implied, including but not limited to the warranties of merchantability,
18 | * fitness for a particular purpose and noninfringement. In no event shall the
19 | * authors or copyright holders be liable for any claim, damages or other
20 | * liability, whether in an action of contract, tort or otherwise, arising from,
21 | * out of or in connection with the Software or the use or other dealings in the
22 | * Software.
23 | */
24 |
25 | import Foundation
26 |
27 | /// The set of all legal characters in alphanumeric mode,
28 | /// where each character value maps to the index in the string.
29 | fileprivate let alphanumericCharset: [Character] = [
30 | "0","1","2","3","4","5","6","7","8","9",
31 | "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
32 | " ","$","%","*","+","-",".","/",":"
33 | ]
34 |
35 | /*---- QrSegment functionality ----*/
36 |
37 | /// A segment of character/binary/control data in a QR Code symbol.
38 | ///
39 | /// Instances of this struct are immutable.
40 | ///
41 | /// The mid-level way to create a segment is to take the payload data
42 | /// and call a static factory function such as `QrSegment::make_numeric()`.
43 | /// The low-level way to create a segment is to custom-make the bit buffer
44 | /// and call the `QrSegment::new()` constructor with appropriate values.
45 | ///
46 | /// This segment struct imposes no length restrictions, but QR Codes have restrictions.
47 | /// Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
48 | /// Any segment longer than this is meaningless for the purpose of generating QR Codes.
49 | public struct QRSegment: Hashable {
50 | /// The mode indicator of this segment.
51 | public let mode: Mode
52 | /// The length of this segment"s unencoded data. Measured in characters for
53 | /// numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
54 | /// Not the same as the data"s bit length.
55 | public let numChars: UInt
56 | /// The data bits of this segment.
57 | public let data: [Bool]
58 |
59 | /*---- Static factory functions (mid level) ----*/
60 |
61 | /// Returns a segment representing the given binary data encoded in byte mode.
62 | ///
63 | /// All input byte slices are acceptable.
64 | ///
65 | /// Any text string can be converted to UTF-8 bytes and encoded as a byte mode segment.
66 | public static func makeBytes(_ data: [UInt8]) -> Self {
67 | var bb = BitBuffer()
68 | for b in data {
69 | bb.appendBits(UInt32(b), 8)
70 | }
71 | return QRSegment(mode: .byte, numChars: UInt(data.count), data: bb.bits)
72 | }
73 |
74 | /// Returns a segment representing the given string of decimal digits encoded in numeric mode.
75 | ///
76 | /// Panics if the string contains non-digit characters.
77 | public static func makeNumeric(_ text: [Character]) -> Self {
78 | var bb = BitBuffer()
79 | var accumData: UInt32 = 0
80 | var accumCount: UInt8 = 0
81 | for c in text {
82 | assert(c.isNumber && c.isASCII, "String contains non-numeric characters")
83 | let zero: Character = "0"
84 | accumData = accumData * 10 + (UInt32(c.asciiValue!) - UInt32(zero.asciiValue!))
85 | accumCount += 1
86 | if accumCount == 3 {
87 | bb.appendBits(accumData, 10)
88 | accumData = 0
89 | accumCount = 0
90 | }
91 | }
92 | if accumCount > 0 { // 1 or 2 digits remaining
93 | bb.appendBits(accumData, Int(accumCount * 3 + 1))
94 | }
95 | return QRSegment(mode: .numeric, numChars: UInt(text.count), data: bb.bits)
96 | }
97 |
98 | /// Returns a segment representing the given text string encoded in alphanumeric mode.
99 | ///
100 | /// The characters allowed are: 0 to 9, A to Z (uppercase only), space,
101 | /// dollar, percent, asterisk, plus, hyphen, period, slash, colon.
102 | ///
103 | /// Panics if the string contains non-encodable characters.
104 | public static func makeAlphanumeric(_ text: [Character]) -> Self {
105 | var bb = BitBuffer()
106 | var accumData: UInt32 = 0
107 | var accumCount: UInt32 = 0
108 | for c in text {
109 | guard let i = alphanumericCharset.firstIndex(of: c) else {
110 | fatalError("String contains unencodable characters in alphanumeric mode")
111 | }
112 | accumData = accumData * 45 + UInt32(i)
113 | accumCount += 1
114 | if accumCount == 2 {
115 | bb.appendBits(accumData, 11)
116 | accumData = 0
117 | accumCount = 0
118 | }
119 | }
120 | if accumCount > 0 { // 1 character remaining
121 | bb.appendBits(accumData, 6)
122 | }
123 | return QRSegment(mode: .alphanumeric, numChars: UInt(text.count), data: bb.bits)
124 | }
125 |
126 | /// Returns a list of zero or more segments to represent the given Unicode text string.
127 | ///
128 | /// The result may use various segment modes and switch
129 | /// modes to optimize the length of the bit stream.
130 | public static func makeSegments(_ text: [Character]) -> [Self] {
131 | if text.isEmpty {
132 | return []
133 | } else if QRSegment.isNumeric(text) {
134 | return [QRSegment.makeNumeric(text)]
135 | } else if QRSegment.isAlphanumeric(text) {
136 | return [QRSegment.makeAlphanumeric(text)]
137 | } else {
138 | let s = String(text)
139 | return [QRSegment.makeBytes([UInt8](s.data(using: .utf8)!))]
140 | }
141 | }
142 |
143 | /// Returns a segment representing an Extended Channel Interpretation
144 | /// (ECI) designator with the given assignment value.
145 | public static func makeECI(assignVal: UInt32) -> Self {
146 | var bb = BitBuffer()
147 | if assignVal < (1 << 7) {
148 | bb.appendBits(assignVal, 8)
149 | } else if assignVal < (1 << 14) {
150 | bb.appendBits(2, 2)
151 | bb.appendBits(assignVal, 14)
152 | } else if assignVal < 1_000_000 {
153 | bb.appendBits(6, 3)
154 | bb.appendBits(assignVal, 21)
155 | } else {
156 | fatalError("ECI assignment value out of range")
157 | }
158 | return QRSegment(mode: .eci, numChars: 0, data: bb.bits)
159 | }
160 |
161 | /*---- Constructor (low level) ----*/
162 |
163 | /// Creates a new QR Code segment with the given attributes and data.
164 | ///
165 | /// The character count (numchars) must agree with the mode and
166 | /// the bit buffer length, but the constraint isn"t checked.
167 | public init(mode: Mode, numChars: UInt, data: [Bool]) {
168 | self.mode = mode
169 | self.numChars = numChars
170 | self.data = data
171 | }
172 |
173 | /*---- Other static functions ----*/
174 |
175 | /// Calculates and returns the number of bits needed to encode the given
176 | /// segments at the given version. The result is None if a segment has too many
177 | /// characters to fit its length field, or the total bits exceeds usize::MAX.
178 | public static func getTotalBits(segments: [Self], version: QRCodeVersion) -> UInt? {
179 | var result: UInt = 0
180 | for seg in segments {
181 | let ccBits = seg.mode.numCharCountBits(version: version)
182 | guard seg.numChars < (1 << ccBits) else {
183 | return nil // The segment"s length doesn't fit the field's bit width
184 | }
185 | result += 4 + UInt(ccBits) + UInt(seg.data.count)
186 | }
187 | return result
188 | }
189 |
190 | /// Tests whether the given string can be encoded as a segment in alphanumeric mode.
191 | /// A string is encodable iff each character is in the following set: 0 to 9, A to Z
192 | /// (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
193 | public static func isAlphanumeric(_ text: [Character]) -> Bool {
194 | text.allSatisfy { alphanumericCharset.contains($0) }
195 | }
196 |
197 | // Tests whether the given string can be encoded as a segment in numeric mode.
198 | // A string is encodable iff each character is in the range 0 to 9.
199 | public static func isNumeric(_ text: [Character]) -> Bool {
200 | text.allSatisfy { $0.isNumber }
201 | }
202 |
203 | /*---- QrSegmentMode functionality ----*/
204 | public enum Mode: Hashable {
205 | case numeric
206 | case alphanumeric
207 | case byte
208 | case kanji
209 | case eci
210 |
211 | /// Returns an unsigned 4-bit integer value (range 0 to 15)
212 | /// representing the mode indicator bits for this mode object.
213 | var modeBits: UInt32 {
214 | switch self {
215 | case .numeric: return 0x1
216 | case .alphanumeric: return 0x2
217 | case .byte: return 0x4
218 | case .kanji: return 0x8
219 | case .eci: return 0x7
220 | }
221 | }
222 |
223 | /// Returns the bit width of the character count field for a segment in this mode
224 | /// in a QR Code at the given version number. The result is in the range [0, 16].
225 | func numCharCountBits(version: QRCodeVersion) -> UInt8 {
226 | let v: [UInt8]
227 | switch self {
228 | case .numeric: v = [10, 12, 14]
229 | case .alphanumeric: v = [9, 11, 13]
230 | case .byte: v = [8, 16, 16]
231 | case .kanji: v = [8, 10, 12]
232 | case .eci: v = [0, 0, 0]
233 | }
234 | return v[(Int(version.value) + 7) / 17]
235 | }
236 | }
237 | }
238 |
--------------------------------------------------------------------------------
/SwiftQRCodeGenerator.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'SwiftQRCodeGenerator'
3 | s.version = '2.0.3'
4 | s.summary = 'QR code generator written in pure Swift'
5 | s.homepage = 'https://github.com/fwcd/swift-qrcode-generator'
6 | s.license = { :type => 'MIT', :file => 'LICENSE' }
7 | s.author = 'fwcd'
8 | s.source = { :git => 'https://github.com/fwcd/swift-qrcode-generator.git', :tag => "v#{s.version.to_s}" }
9 | s.swift_version = '5.1'
10 | s.ios.deployment_target = '11.0'
11 | s.tvos.deployment_target = '11.0'
12 | s.watchos.deployment_target = '4.0'
13 | s.osx.deployment_target = '10.13'
14 | s.module_name = 'QRCodeGenerator'
15 | s.source_files = 'Sources/**/*'
16 | s.frameworks = 'Foundation'
17 | end
18 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import QRCodeGeneratorTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += QRCodeGeneratorTests.allTests()
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------
/Tests/QRCodeGeneratorTests/QRCodeGeneratorTests.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * QR Code generator library (Swift)
3 | *
4 | * Copyright (c) Project Nayuki. (MIT License)
5 | * https://www.nayuki.io/page/qr-code-generator-library
6 | * Copyright (c) 2020 fwcd
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | * this software and associated documentation files (the "Software"), to deal in
10 | * the Software without restriction, including without limitation the rights to
11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12 | * the Software, and to permit persons to whom the Software is furnished to do so,
13 | * subject to the following conditions:
14 | * - The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | * - The Software is provided "as is", without warranty of any kind, express or
17 | * implied, including but not limited to the warranties of merchantability,
18 | * fitness for a particular purpose and noninfringement. In no event shall the
19 | * authors or copyright holders be liable for any claim, damages or other
20 | * liability, whether in an action of contract, tort or otherwise, arising from,
21 | * out of or in connection with the Software or the use or other dealings in the
22 | * Software.
23 | */
24 |
25 | import XCTest
26 | @testable import QRCodeGenerator
27 |
28 | final class QRCodeGeneratorTests: XCTestCase {
29 | static var allTests = [
30 | ("testQRCodeGeneration", testQRCodeGeneration),
31 | ]
32 |
33 | func testQRCodeGeneration() throws {
34 | let text = "Hello World!"
35 | let qr = try QRCode.encode(text: text, ecl: .low)
36 | let svg = qr.toSVGString(border: 4)
37 |
38 | XCTAssertEqual(svg, """
39 |
40 |
41 |
45 | """)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Tests/QRCodeGeneratorTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(QRCodeGeneratorTests.allTests),
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------