Skip to content

Commit

Permalink
Fixed issue where CGPath export wouldn't contain a hole for the logot…
Browse files Browse the repository at this point in the history
…emplate
  • Loading branch information
dagronf committed Jun 12, 2024
1 parent d4d8892 commit 8163f86
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 57 deletions.
8 changes: 1 addition & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ CGImageRef cgr = [doc cgImageWithDimension:400 error:&error];
| Bitmap | Vector |
|----------|----------|
|<a href='./Art/images/basic-doco-md-coreimage.png'><img src="./Art/images/basic-doco-md-coreimage.pdf" width="80"/></a>|<a href='./Art/images/basic-doco-md-coreimage.pdf'><img src="./Art/images/basic-doco-md-coreimage.pdf" width="80"/></a>|
|<a href='./Art/images/basic-doco-md-coreimage.png'><img src="./Art/images/basic-doco-md-coreimage.png" width="80"/></a>|<a href='./Art/images/basic-doco-md-coreimage.pdf'><img src="./Art/images/basic-doco-md-coreimage.png" width="80"/></a>|
### Set the error correction
Expand Down Expand Up @@ -279,12 +279,6 @@ let loadedDoc = try QRCode.Document(jsonData: jsonData)

</details>

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.
Expand Down
7 changes: 2 additions & 5 deletions Sources/QRCode/QRCode+Document+Export.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public extension QRCode.Document {
),
components: components,
shape: self.design.shape,
logoTemplate: self.logoTemplate,
additionalQuietSpace: additionalQuietSpace
)
}
Expand All @@ -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)
}
}

Expand Down
109 changes: 65 additions & 44 deletions Tests/QRCodeTests/QRCodeDocGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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!",
Expand All @@ -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 += "|<a href=\"\(link)\"><img src=\"\(link)\" width=\"125\" /></a><br/>"
}

// 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 += "<a href=\"\(link)\"><img src=\"\(link)\" width=\"125\" /></a><br/>|"
}

// 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 += "|<a href=\"\(link)\"><img src=\"\(link)\" width=\"125\" /></a>"
}

let content = try image.representation.png()
let filename = "components-\(item.offset).png"
let link = try imageStore.store(content, filename: filename)
markdownText += "<a href=\"\(link)\"><img src=\"\(link)\" width=\"125\" /></a>|"
markdownText += "|\n"
}

markdownText += "\n"
Expand Down
48 changes: 48 additions & 0 deletions Tests/QRCodeTests/QRCodeMaskingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
}
}
10 changes: 9 additions & 1 deletion Tests/QRCodeTests/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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 {
Expand Down

0 comments on commit 8163f86

Please sign in to comment.