├── .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 | [![Linux](https://github.com/fwcd/swift-qrcode-generator/actions/workflows/linux.yml/badge.svg)](https://github.com/fwcd/swift-qrcode-generator/actions/workflows/linux.yml) 4 | [![Darwin](https://github.com/fwcd/swift-qrcode-generator/actions/workflows/darwin.yml/badge.svg)](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 | 265 | \(backgroundRect) 266 | 267 | 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 | 42 | 43 | 44 | 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 | --------------------------------------------------------------------------------