diff --git a/EFQRCode.xcodeproj/project.pbxproj b/EFQRCode.xcodeproj/project.pbxproj index d7fc000..7115bf9 100755 --- a/EFQRCode.xcodeproj/project.pbxproj +++ b/EFQRCode.xcodeproj/project.pbxproj @@ -110,6 +110,10 @@ D2FAEBB92749720F00DFA959 /* EFPointStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FAEBB72749720F00DFA959 /* EFPointStyle.swift */; }; D2FAEBBA2749720F00DFA959 /* EFPointStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FAEBB72749720F00DFA959 /* EFPointStyle.swift */; }; D2FAEBBB2749720F00DFA959 /* EFPointStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FAEBB72749720F00DFA959 /* EFPointStyle.swift */; }; + D53C7D102CB1402100C2A40A /* EFPointPatternType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53C7D0F2CB1402100C2A40A /* EFPointPatternType.swift */; }; + D53C7D112CB1402100C2A40A /* EFPointPatternType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53C7D0F2CB1402100C2A40A /* EFPointPatternType.swift */; }; + D53C7D122CB1402100C2A40A /* EFPointPatternType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53C7D0F2CB1402100C2A40A /* EFPointPatternType.swift */; }; + D53C7D132CB1402100C2A40A /* EFPointPatternType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53C7D0F2CB1402100C2A40A /* EFPointPatternType.swift */; }; F829C6B81A7A94F100A2CD59 /* EFQRCode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DD67C0B1A5C55C900ED2280 /* EFQRCode.framework */; }; /* End PBXBuildFile section */ @@ -178,6 +182,7 @@ D2DF1DB7256EC4CF00EB6012 /* European SEPA Money Transfer + Watermark.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = "European SEPA Money Transfer + Watermark.playground"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D2DF1DB8256EC4CF00EB6012 /* Basic B&W QR Code.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = "Basic B&W QR Code.playground"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; D2FAEBB72749720F00DFA959 /* EFPointStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EFPointStyle.swift; sourceTree = ""; }; + D53C7D0F2CB1402100C2A40A /* EFPointPatternType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EFPointPatternType.swift; sourceTree = ""; }; F8111E3319A95C8B0040E7D1 /* EFQRCode.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = EFQRCode.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F8111E3719A95C8B0040E7D1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F8111E3E19A95C8B0040E7D1 /* EFQRCode iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "EFQRCode iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -261,6 +266,7 @@ 12C0B701219C102C000545C9 /* EFIntSize.swift */, 12C0B706219C1046000545C9 /* EFInputCorrectionLevel.swift */, D2FAEBB72749720F00DFA959 /* EFPointStyle.swift */, + D53C7D0F2CB1402100C2A40A /* EFPointPatternType.swift */, 12C0B6F2219C0F7D000545C9 /* EFQRCodeMode.swift */, 12C0B6FC219C0FD3000545C9 /* EFUIntPixel.swift */, 12664E9A1FA9CEAB00D56500 /* EFWatermarkMode.swift */, @@ -722,6 +728,7 @@ 12EE9E3C23863D6E00241C60 /* UIImage+.swift in Sources */, 12664ED11FA9CEDD00D56500 /* EFQRCode.swift in Sources */, 12EE9E502386968F00241C60 /* NSColor+.swift in Sources */, + D53C7D122CB1402100C2A40A /* EFPointPatternType.swift in Sources */, 12664EB81FA9CED100D56500 /* CIImage+.swift in Sources */, 12C0B704219C102C000545C9 /* EFIntSize.swift in Sources */, 12C0B709219C1046000545C9 /* EFInputCorrectionLevel.swift in Sources */, @@ -760,6 +767,7 @@ 12EE9E3B23863D6E00241C60 /* UIImage+.swift in Sources */, 12664ED01FA9CEDD00D56500 /* EFQRCode.swift in Sources */, 12EE9E4F2386968F00241C60 /* NSColor+.swift in Sources */, + D53C7D112CB1402100C2A40A /* EFPointPatternType.swift in Sources */, 12664EB71FA9CED100D56500 /* CIImage+.swift in Sources */, 12C0B703219C102C000545C9 /* EFIntSize.swift in Sources */, 12C0B708219C1046000545C9 /* EFInputCorrectionLevel.swift in Sources */, @@ -789,6 +797,7 @@ D2265578256EA35600920D11 /* EFQRCode+ObjC.swift in Sources */, 12EE9E3D23863D6E00241C60 /* UIImage+.swift in Sources */, 5282BCBD1FF3903D00DFB36B /* EFQRCodeGenerator.swift in Sources */, + D53C7D132CB1402100C2A40A /* EFPointPatternType.swift in Sources */, 12EE9E512386968F00241C60 /* NSColor+.swift in Sources */, 5282BCB51FF3903D00DFB36B /* EFWatermarkMode.swift in Sources */, 5282BCB71FF3903D00DFB36B /* CGImage+.swift in Sources */, @@ -818,6 +827,7 @@ 12EE9E3A23863D6E00241C60 /* UIImage+.swift in Sources */, 12664ECF1FA9CEDD00D56500 /* EFQRCode.swift in Sources */, 12EE9E4E2386968F00241C60 /* NSColor+.swift in Sources */, + D53C7D102CB1402100C2A40A /* EFPointPatternType.swift in Sources */, 12664EB61FA9CED100D56500 /* CIImage+.swift in Sources */, 12C0B702219C102C000545C9 /* EFIntSize.swift in Sources */, 12C0B707219C1046000545C9 /* EFInputCorrectionLevel.swift in Sources */, diff --git a/Examples/Shared/PointStyle.swift b/Examples/Shared/PointStyle.swift index 431169f..e040233 100644 --- a/Examples/Shared/PointStyle.swift +++ b/Examples/Shared/PointStyle.swift @@ -28,7 +28,7 @@ import EFQRCode import CoreGraphics class StarPointStyle: EFPointStyle { - func fillRect(context: CGContext, rect: CGRect, isStatic: Bool) { + func fillRect(context: CGContext, rect: CGRect, patternType: EFPointPatternType) { let path = CGMutablePath() var points: [CGPoint] = [] let radius = Float(rect.width / 2) diff --git a/Source/EFPointPatternType.swift b/Source/EFPointPatternType.swift new file mode 100644 index 0000000..72f6426 --- /dev/null +++ b/Source/EFPointPatternType.swift @@ -0,0 +1,32 @@ +// +// EFPointPatternType.swift +// EFQRCode +// +// Created by Henry on 05/10/2024. +// Copyright © 2024 EyreFree. All rights reserved. +// + +import Foundation + +@objc public enum EFPointPatternType: Int { + case none = 0 + + // Empty border + case border + + // Timing pattern + case timing + + // Alignment pattern + case alignment + + // Finder pattern markers + case finderTopLeftInner + case finderTopLeftOuter + + case finderTopRightInner + case finderTopRightOuter + + case finderBottomLeftInner + case finderBottomLeftOuter +} diff --git a/Source/EFPointStyle.swift b/Source/EFPointStyle.swift index f6d4014..9d7510c 100644 --- a/Source/EFPointStyle.swift +++ b/Source/EFPointStyle.swift @@ -33,21 +33,21 @@ import CoreGraphics /// - Parameters: /// - context: the context to draw in. /// - rect: the boundaries of the point to draw. - /// - isStatic: true if it is recommended to use a square instead of current style. - func fillRect(context: CGContext, rect: CGRect, isStatic: Bool) + /// - patternType: the type of static point being drawn, it is recommended to use a square if the pattern is not `.none` + func fillRect(context: CGContext, rect: CGRect, patternType: EFPointPatternType) } /// Drawing classical look and feel QR code foreground points 🔳. @objc public class EFSquarePointStyle: NSObject, EFPointStyle { - public func fillRect(context: CGContext, rect: CGRect, isStatic: Bool) { + public func fillRect(context: CGContext, rect: CGRect, patternType: EFPointPatternType) { context.fill(rect) } } /// Drawing rounded foreground points 🔘. @objc public class EFCirclePointStyle: NSObject, EFPointStyle { - public func fillRect(context: CGContext, rect: CGRect, isStatic: Bool) { - if isStatic { + public func fillRect(context: CGContext, rect: CGRect, patternType: EFPointPatternType) { + if patternType != .none { context.fill(rect) } else { context.fillEllipse(in: rect) @@ -57,8 +57,8 @@ import CoreGraphics /// Drawing Sparkling foreground points ✨. @objc public class EFDiamondPointStyle: NSObject, EFPointStyle { - public func fillRect(context: CGContext, rect: CGRect, isStatic: Bool) { - if isStatic { + public func fillRect(context: CGContext, rect: CGRect, patternType: EFPointPatternType) { + if patternType != .none { context.fill(rect) } else { fillDiamond(context: context, rect: rect) diff --git a/Source/EFQRCodeGenerator.swift b/Source/EFQRCodeGenerator.swift index fb42f20..0ea08f3 100644 --- a/Source/EFQRCodeGenerator.swift +++ b/Source/EFQRCodeGenerator.swift @@ -576,13 +576,13 @@ public class EFQRCodeGenerator: NSObject { size: EFIntSize ) -> CGImage? { let codeSize = codes.count - + let scaleX = size.width.cgFloat / CGFloat(codeSize) let scaleY = size.height.cgFloat / CGFloat(codeSize) if scaleX < 1.0 || scaleY < 1.0 { print("Warning: Size too small.") } - + var points = [CGPoint]() if let locations = getAlignmentPatternLocations(version: getVersion(size: codeSize - 2)) { for indexX in locations { @@ -608,8 +608,8 @@ public class EFQRCodeGenerator: NSObject { // CTM-90 let indexXCTM = indexY let indexYCTM = codeSize - indexX - 1 - - let isStaticPoint = isStatic(x: indexX, y: indexY, size: codeSize, APLPoints: points) + + let patternType = patternTypeFor(x: indexX, y: indexY, size: codeSize, APLPoints: points) drawPoint( context: context, @@ -619,7 +619,7 @@ public class EFQRCodeGenerator: NSObject { width: scaleX - 2 * pointOffset, height: scaleY - 2 * pointOffset ), - isStatic: isStaticPoint + patternType: patternType ) } } @@ -690,7 +690,9 @@ public class EFQRCodeGenerator: NSObject { // CTM-90 let indexXCTM = indexY let indexYCTM = codeSize - indexX - 1 - if isStatic(x: indexX, y: indexY, size: codeSize, APLPoints: points) { + let patternType = patternTypeFor(x: indexX, y: indexY, size: codeSize, APLPoints: points) + + if patternType != .none { drawPoint( context: context, rect: CGRect( @@ -699,7 +701,7 @@ public class EFQRCodeGenerator: NSObject { width: pointWidthOriX, height: pointWidthOriY ), - isStatic: true + patternType: patternType ) } else { drawPoint( @@ -721,7 +723,9 @@ public class EFQRCodeGenerator: NSObject { // CTM-90 let indexXCTM = indexY let indexYCTM = codeSize - indexX - 1 - if isStatic(x: indexX, y: indexY, size: codeSize, APLPoints: points) { + let patternType = patternTypeFor(x: indexX, y: indexY, size: codeSize, APLPoints: points) + + if patternType != .none { drawPoint( context: context, rect: CGRect( @@ -730,7 +734,7 @@ public class EFQRCodeGenerator: NSObject { width: pointWidthOriX - 2 * pointOffset, height: pointWidthOriY - 2 * pointOffset ), - isStatic: true + patternType: patternType ) } else { drawPoint( @@ -812,9 +816,9 @@ public class EFQRCodeGenerator: NSObject { ) ) } - - private func drawPoint(context: CGContext, rect: CGRect, isStatic: Bool = false) { - pointStyle.fillRect(context: context, rect: rect, isStatic: isStatic) + + private func drawPoint(context: CGContext, rect: CGRect, patternType: EFPointPatternType = .none) { + pointStyle.fillRect(context: context, rect: rect, patternType: patternType) } private func createContext(size: EFIntSize) -> CGContext? { @@ -886,22 +890,38 @@ public class EFQRCodeGenerator: NSObject { return imageCodes } - /// Special Points of QRCode - private func isStatic(x: Int, y: Int, size: Int, APLPoints: [CGPoint]) -> Bool { + private func patternTypeFor(x: Int, y: Int, size: Int, APLPoints: [CGPoint]) -> EFPointPatternType { // Empty border if x == 0 || y == 0 || x == (size - 1) || y == (size - 1) { - return true + return .border } // Finder Patterns - if (x <= 8 && y <= 8) || (x <= 8 && y >= (size - 9)) || (x >= (size - 9) && y <= 8) { - return true + if (x <= 8 && y <= 8) { + if 2...6 ~= x && 2...6 ~= y { + return .finderTopLeftInner + } + return .finderTopLeftOuter + } + + if (x <= 8 && y >= (size - 9)) { + if 2...6 ~= x && (size - 7)...(size - 4) ~= y { + return .finderTopRightInner + } + return .finderTopRightOuter + } + + if (x >= (size - 9) && y <= 8) { + if (size - 7)...(size - 4) ~= x && 2...6 ~= y { + return .finderBottomLeftInner + } + return .finderBottomLeftOuter } if isTimingPointStatic { // Timing Patterns if x == 7 || y == 7 { - return true + return .timing } } @@ -911,7 +931,7 @@ public class EFQRCodeGenerator: NSObject { && x <= Int(point.x + 2) && y >= Int(point.y - 2) && y <= Int(point.y + 2) - } + } ? .alignment : .none } /// [Alignment Pattern Locations](