diff --git a/README.md b/README.md index e8491d1..a33646f 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,7 @@ CGImageRef cgr = [doc cgImageWithDimension:400 error:&error]; | Bitmap | Vector | |----------|----------| -||| +||| ### Set the error correction @@ -279,12 +279,6 @@ let loadedDoc = try QRCode.Document(jsonData: jsonData) -There are also some extensions on `CGImage` to help making qr codes even easier - -```swift -let qrCodeImage = CGImage.qrCode("Hi there!", dimension: 800) -``` - ### QRCode builder `QRCode.Builder` is a lightweight Swift-only convenience shim for the QRCode Document. diff --git a/Sources/QRCode/QRCode+Document+Export.swift b/Sources/QRCode/QRCode+Document+Export.swift index abe46ae..4755adc 100644 --- a/Sources/QRCode/QRCode+Document+Export.swift +++ b/Sources/QRCode/QRCode+Document+Export.swift @@ -75,6 +75,7 @@ public extension QRCode.Document { ), components: components, shape: self.design.shape, + logoTemplate: self.logoTemplate, additionalQuietSpace: additionalQuietSpace ) } @@ -85,11 +86,7 @@ public extension QRCode.Document { /// - components: The components of the QR code to include in the path /// - Returns: A path containing the components @objc func path(dimension: Int, components: QRCode.Components = .all) -> CGPath { - return self.qrcode.path( - CGSize(dimension: dimension), - components: components, - shape: self.design.shape - ) + self.path(CGSize(dimension: dimension), components: components) } } diff --git a/Tests/QRCodeTests/QRCodeDocGenerator.swift b/Tests/QRCodeTests/QRCodeDocGenerator.swift index 59875ab..5bfd672 100644 --- a/Tests/QRCodeTests/QRCodeDocGenerator.swift +++ b/Tests/QRCodeTests/QRCodeDocGenerator.swift @@ -699,8 +699,8 @@ final class QRCodeDocGeneratorTests: XCTestCase { func testComponentGeneration() throws { markdownText += "## Component paths\n\n" - markdownText += "| all | on pixels | off pixels | eye background | eye outer | eye pupil |\n" - markdownText += "|:------:|:------:|:------:|:------:|:------:|:------:|\n" + markdownText += "| | all | on pixels | off pixels | eye background | eye outer | eye pupil |\n" + markdownText += "|:------:|:------:|:------:|:------:|:------:|:------:|:------:|\n" let doc = try QRCode.Document( utf8String: "QR Code generation test with a lot of content to display!", @@ -717,54 +717,75 @@ final class QRCodeDocGeneratorTests: XCTestCase { let rect = CGRect(origin: .zero, size: .init(width: dimension, height: dimension)) - // Do all first + let logoTemplate = QRCode.LogoTemplate( + image: try resourceImage(for: "apple", extension: "png"), + path: CGPath(rect: CGRect(x: 0.40, y: 0.365, width: 0.55, height: 0.25), transform: nil), + inset: 32 + ) + let logoImageC = try resourceImage(for: "instagram-icon", extension: "png") + let logoTemplate2 = QRCode.LogoTemplate.CircleCenter(image: logoImageC) + + try [ + ("nologo", nil), + ("logo-1", logoTemplate), + ("logo-2", logoTemplate2) + ].forEach { template in + + doc.logoTemplate = template.1 + let filenameaddition = template.0 + + markdownText += "| \(filenameaddition) " + + // Do all first + + do { + let image = try CGImage.Create(size: rect.size, flipped: true) { ctx in + ctx.saveGState() + ctx.setFillColor(CGColor.gray(0, 0.05)) + ctx.fill([rect]) + ctx.setStrokeColor(CGColor.gray(0, 0.8)) + ctx.setLineWidth(0.5) + ctx.stroke(rect) + ctx.restoreGState() + + for item in items { + let path = doc.path(dimension: self.dimension, components: item.0) + ctx.addPath(path) + ctx.setFillColor(item.2) + ctx.fillPath() + } + }//.flipping(.horizontally) + + let content = try image.representation.png() + let filename = "components-all-\(filenameaddition).png" + let link = try imageStore.store(content, filename: filename) + markdownText += "|
" + } + + // Now each individually + + for item in items.enumerated() { + let path = doc.path(dimension: dimension, components: item.element.0) + let image = try CGImage.Create(size: rect.size, flipped: true) { ctx in + ctx.saveGState() + ctx.setFillColor(CGColor.gray(0, 0.05)) + ctx.fill([rect]) + ctx.setStrokeColor(CGColor.gray(0, 0.8)) + ctx.setLineWidth(0.5) + ctx.stroke(rect) + ctx.restoreGState() - do { - let image = try CGImage.Create(size: rect.size, flipped: true) { ctx in - ctx.saveGState() - ctx.setFillColor(CGColor.gray(0, 0.05)) - ctx.fill([rect]) - ctx.setStrokeColor(CGColor.gray(0, 0.8)) - ctx.setLineWidth(0.5) - ctx.stroke(rect) - ctx.restoreGState() - - for item in items { - let path = doc.path(dimension: self.dimension, components: item.0) ctx.addPath(path) - ctx.setFillColor(item.2) + ctx.setFillColor(item.element.2) ctx.fillPath() } - }//.flipping(.horizontally) - let content = try image.representation.png() - let filename = "components-all.png" - let link = try imageStore.store(content, filename: filename) - markdownText += "
|" - } - - // Now each individually - - for item in items.enumerated() { - let path = doc.path(dimension: dimension, components: item.element.0) - let image = try CGImage.Create(size: rect.size, flipped: true) { ctx in - ctx.saveGState() - ctx.setFillColor(CGColor.gray(0, 0.05)) - ctx.fill([rect]) - ctx.setStrokeColor(CGColor.gray(0, 0.8)) - ctx.setLineWidth(0.5) - ctx.stroke(rect) - ctx.restoreGState() - - ctx.addPath(path) - ctx.setFillColor(item.element.2) - ctx.fillPath() + let content = try image.representation.png() + let filename = "components-\(item.offset)-\(filenameaddition).png" + let link = try imageStore.store(content, filename: filename) + markdownText += "|" } - - let content = try image.representation.png() - let filename = "components-\(item.offset).png" - let link = try imageStore.store(content, filename: filename) - markdownText += "|" + markdownText += "|\n" } markdownText += "\n" diff --git a/Tests/QRCodeTests/QRCodeMaskingTests.swift b/Tests/QRCodeTests/QRCodeMaskingTests.swift index 0349f06..4e26435 100644 --- a/Tests/QRCodeTests/QRCodeMaskingTests.swift +++ b/Tests/QRCodeTests/QRCodeMaskingTests.swift @@ -328,4 +328,52 @@ final class QRCodeMaskingTests: XCTestCase { try outputFolder.write(pdfhBigger, to: "logotemplate-issue34-height-bigger-\(quietSpace).pdf") } } + + func testPathGenerationWithLogoTemplate() throws { + + // Check that generating a CGPath contains a hole for the logotemplate if it exists + + let outputFolder = try outputFolder.subfolder(with: "logo-path-check") + + let logoImage = try resourceImage(for: "instagram-icon", extension: "png") + let logoTemplate = QRCode.LogoTemplate( + image: logoImage, + path: CGPath(ellipseIn: CGRect(x: 0.65, y: 0.35, width: 0.30, height: 0.30), transform: nil), + inset: 8 + ) + + let doc = try QRCode.build + .text("Logo template checking with path export Logo template checking with path export Logo template checking with path export") + .backgroundColor(CGColor(srgbRed: 1, green: 1, blue: 0, alpha: 1)) + .eye.shape(QRCode.EyeShape.Squircle()) + .document + + try [0, 12].forEach { quietSpace in + doc.design.additionalQuietZonePixels = UInt(quietSpace) + + do { + // No logotemplate + doc.logoTemplate = nil + let path = doc.path(dimension: 400) + let b1 = try CreateBitmap(dimension: 400, backgroundColor: CGColor.commonWhite) { ctx in + ctx.addPath(path) + ctx.setFillColor(.commonBlack) + ctx.fillPath() + } + try outputFolder.write(try b1.representation.png(), to: "path-without-logotemplate-\(quietSpace).png") + } + + do { + // No logotemplate + doc.logoTemplate = logoTemplate + let path = doc.path(dimension: 400) + let b1 = try CreateBitmap(dimension: 400, backgroundColor: CGColor.commonWhite) { ctx in + ctx.addPath(path) + ctx.setFillColor(.commonBlack) + ctx.fillPath() + } + try outputFolder.write(try b1.representation.png(), to: "path-with-logotemplate-\(quietSpace).png") + } + } + } } diff --git a/Tests/QRCodeTests/Utils.swift b/Tests/QRCodeTests/Utils.swift index f3eaacf..a14d07e 100644 --- a/Tests/QRCodeTests/Utils.swift +++ b/Tests/QRCodeTests/Utils.swift @@ -121,7 +121,7 @@ enum CreateBitmapError: Error { } /// Create a bitmap and draw into it -func CreateBitmap(dimension: Int, flipped: Bool = true, _ block: (CGContext) -> Void) throws -> CGImage { +func CreateBitmap(dimension: Int, backgroundColor: CGColor? = nil, flipped: Bool = true, _ block: (CGContext) -> Void) throws -> CGImage { let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue) let colorSpace = CGColorSpace(name: CGColorSpace.sRGB)! guard let ctx = CGContext( @@ -141,6 +141,14 @@ func CreateBitmap(dimension: Int, flipped: Bool = true, _ block: (CGContext) -> ctx.scaleBy(x: 1, y: -1) ctx.translateBy(x: 0, y: Double(-dimension)) } + + if let backgroundColor = backgroundColor { + ctx.usingGState { c in + c.setFillColor(backgroundColor) + c.fill(CGRect(origin: .zero, size: CGSize(width: ctx.width, height: ctx.height))) + } + } + block(ctx) guard let img = ctx.makeImage() else {