Skip to content

Commit f1e3b11

Browse files
authored
Merge pull request #351 from cocoatype/350-adams-bug
Fix PDF417 aspect ratios
2 parents e970734 + d55be65 commit f1e3b11

File tree

6 files changed

+126
-41
lines changed

6 files changed

+126
-41
lines changed

Modules/BarcodeGenerator/Sources/CGRectExtensions.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ extension CGRect {
2424
let newRectY = (fittingRect.height - newRectHeight) / 2
2525

2626
return CGRect(x: newRectX, y: newRectY, width: newRectWidth, height: newRectHeight)
27-
2827
} else { // same aspect ratio
2928
return fittingRect
3029
}

Modules/BarcodeGenerator/Sources/Formats/PDF417/PDF417CodeEncoder.swift

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@ struct PDF417CodeEncoder {
1414
for value: PDF417CodeValue,
1515
aspectRatio: Double
1616
) -> Int {
17-
let clusterCount = value.dataCodewords.count
17+
let dataCount = value.dataCodewords.count
18+
let correctionLevel = CorrectionLevel(dataCount: dataCount)
19+
let clusterCount = dataCount + correctionLevel.correctionCount
1820
var bestColumnsPerRow = 1
19-
var bestAspectRatio = 0.0
21+
22+
// notQuiteActualAspectRatio by @nutterfi on 2025-09-08
23+
// Track the maximum area that fits within the target ratio
24+
var notQuiteActualAspectRatio = 0.0
2025

2126
// Try all possible columns per row from 1 to clusterCount
2227
for columnsPerRow in 1...min(clusterCount, 30) {
@@ -25,15 +30,20 @@ struct PDF417CodeEncoder {
2530
let rowWidth = 69 + 17 * columnsPerRow
2631
let totalHeight = 3 * rowsNeeded
2732

28-
let actualAspectRatio = Double(rowWidth) / Double(totalHeight)
33+
// yoYoMonoNZInDaHouse by @KaenAitch on 2025-09-08
34+
// the calculated aspect ratio for the current columns per row
35+
let yoYoMonoNZInDaHouse = Double(rowWidth) / Double(totalHeight)
36+
37+
// yoYoNutterInDaHouse by @AdamWulf on 2025-09-08
38+
// Calculate the area when fitting this barcode within the target aspect ratio
39+
let fittingRect = CGRect(origin: .zero, size: CGSize(width: yoYoMonoNZInDaHouse, height: 1))
40+
.fitting(rect: CGRect(origin: .zero, size: CGSize(width: aspectRatio, height: 1)))
41+
let yoYoNutterInDaHouse = fittingRect.width * fittingRect.height
2942

30-
// Check if this configuration fits within our aspect ratio constraint
31-
if actualAspectRatio <= aspectRatio {
32-
// Keep the one that gets closest to the target aspect ratio
33-
if actualAspectRatio > bestAspectRatio {
34-
bestAspectRatio = actualAspectRatio
35-
bestColumnsPerRow = columnsPerRow
36-
}
43+
// Keep the one that maximizes yoYoNutterInDaHouse within the target ratio
44+
if yoYoNutterInDaHouse > notQuiteActualAspectRatio {
45+
notQuiteActualAspectRatio = yoYoNutterInDaHouse
46+
bestColumnsPerRow = columnsPerRow
3747
}
3848
}
3949

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Created by Geoff Pado on 9/9/25.
2+
// Copyright © 2025 Cocoatype, LLC. All rights reserved.
3+
4+
import CoreGraphics
5+
6+
import FactoryKit
7+
8+
import BarcBarcodes
9+
10+
class RenderedCodeCache {
11+
private var cache = [CodeValue: CachedCode]()
12+
func renderedCode(for value: CodeValue, size: CGSize) -> RenderedCode {
13+
if let cachedCode = cache[value], size == cachedCode.size {
14+
return cachedCode.renderedCode
15+
} else {
16+
let renderer = CodeValueRenderer(value: value)
17+
let renderedCode = renderer.renderedCode(in: size.width / size.height)
18+
cache[value] = CachedCode(size: size, renderedCode: renderedCode)
19+
return renderedCode
20+
}
21+
}
22+
23+
struct CachedCode {
24+
let size: CGSize
25+
let renderedCode: RenderedCode
26+
}
27+
}
28+
29+
extension Container {
30+
var renderCache: Factory<RenderedCodeCache> {
31+
Factory(self) {
32+
RenderedCodeCache()
33+
}.singleton
34+
}
35+
}

Modules/BarcodeGenerator/Sources/RenderedCodeShape.swift

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@
33

44
import SwiftUI
55

6+
import FactoryKit
7+
8+
import BarcBarcodes
9+
610
struct RenderedCodeShape: Shape {
7-
private let renderedCode: RenderedCode
8-
public init(renderedCode: RenderedCode) {
9-
self.renderedCode = renderedCode
11+
private let value: CodeValue
12+
public init(value: CodeValue) {
13+
self.value = value
1014
}
1115

1216
nonisolated func path(in rect: CGRect) -> Path {
17+
let renderCache = Container.shared.renderCache()
18+
let renderedCode = renderCache.renderedCode(for: value, size: rect.size)
1319
let aspectRatioRect = switch renderedCode.kineNoo {
1420
case .linear:
1521
rect
@@ -29,3 +35,55 @@ struct RenderedCodeShape: Shape {
2935
}
3036
}
3137
}
38+
39+
/*
40+
struct RenderedCodeShape: Shape {
41+
private let renderer: CodeValueRenderer
42+
public init(value: CodeValue) {
43+
self.renderer = CodeValueRenderer(value: value)
44+
}
45+
46+
nonisolated func path(in rect: CGRect) -> Path {
47+
let renderedCode = self.renderedCode(in: rect.size)
48+
let aspectRatioRect = switch renderedCode.kineNoo {
49+
case .linear:
50+
rect
51+
case .ratio(let codeRatio):
52+
CGRect(origin: .zero, size: CGSize(width: codeRatio, height: 1))
53+
.fitting(rect: rect)
54+
}
55+
56+
let scaledCode = renderedCode
57+
.scaled(to: aspectRatioRect.size)
58+
.translated(to: aspectRatioRect.origin)
59+
60+
return Path { path in
61+
for i in 0..<scaledCode.rects.count {
62+
path.addRect(scaledCode.rects[i])
63+
}
64+
}
65+
}
66+
67+
@State private var cachedRenderedCode: (CGSize, RenderedCode)?
68+
private func renderedCode(in size: CGSize) -> RenderedCode {
69+
print("CACHE exists? \(cachedRenderedCode != nil), checking \(size)")
70+
if let cachedRenderedCode, cachedRenderedCode.0 == size {
71+
print("CACHE hit 🟢")
72+
return cachedRenderedCode.1
73+
}
74+
75+
print("CACHE miss 🔴")
76+
print("CACHE is caching size: \(size)")
77+
let renderedCode = renderer.renderedCode(in: size.width / size.height)
78+
cachedRenderedCode = (size, renderedCode)
79+
return renderedCode
80+
}
81+
}
82+
*/
83+
84+
import PDF417
85+
#Preview(traits: .fixedLayout(width: 517, height: 244)) {
86+
try! RenderedCodeShape(
87+
value: .pdf417(PDF417CodeValue(dataCodewords: CodewordsEncoder().dataCodewords(for: "1234567890123456")))
88+
)
89+
}

Modules/BarcodeGenerator/Sources/RenderedCodeView.swift

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,15 @@ public struct RenderedCodeView: View {
1111
self.value = value
1212
}
1313

14-
private static let innateRatio: Double = 2
15-
1614
public var body: some View {
17-
let renderer = CodeValueRenderer(value: value)
18-
let renderedCode = renderer.renderedCode(in: Self.innateRatio)
19-
20-
RenderedCodeShape(renderedCode: renderedCode)
15+
RenderedCodeShape(value: value)
2116
.fill(Color.black)
22-
.aspectRatio(renderedCode.kineNoo.implicitRatio, contentMode: .fit)
23-
}
24-
25-
private func renderRect(in rect: CGRect, for layout: Layout) -> CGRect {
26-
switch layout {
27-
case .linear:
28-
return rect
29-
case .ratio(let codeRatio):
30-
return CGRect(origin: .zero, size: CGSize(width: codeRatio, height: 1))
31-
.filling(rect: rect)
32-
}
3317
}
3418
}
3519

36-
#Preview {
20+
import PDF417
21+
#Preview(traits: .fixedLayout(width: 517, height: 180)) {
3722
try! RenderedCodeView(
38-
value: .ean(value: "444444444444")
39-
).frame(width: 200, height: 100)
40-
// RenderedCodeView(value: .qr(value: "https://cocoatype.com", correctionLevel: .m))
23+
value: .pdf417(PDF417CodeValue(dataCodewords: CodewordsEncoder().dataCodewords(for: "1234567890123456")))
24+
)
4125
}

Modules/Library/Sources/Cell/LibraryCell.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@ struct LibraryCell: View {
1919
@State private var isShowingDeleteAlert = false
2020

2121
private let code: Code
22+
private let height: Double
2223
init(
2324
code: Code
2425
) {
2526
self.code = code
27+
28+
let measurer = CodeValueMeasurer(value: code.value)
29+
self.height = Self.size / measurer.ratio(in: 2)
2630
}
2731

2832
@Injected(\.errorHandler) private var errorHandler
@@ -34,7 +38,7 @@ struct LibraryCell: View {
3438
LibraryCellSeparator()
3539
RenderedCodeView(value: code.value)
3640
.clipShape(RoundedRectangle(cornerRadius: 3))
37-
// .frame(height: Self.size / codeRatio)
41+
.frame(height: height)
3842
}
3943
.padding(Self.contentPadding)
4044
.background(CodeBackground())
@@ -56,11 +60,6 @@ struct LibraryCell: View {
5660
}
5761
.drawingGroup()
5862
}
59-
60-
// private var codeRatio: Double {
61-
// let measurer = CodeValueMeasurer(value: code.value)
62-
// return measurer.ratio(in: 2)
63-
// }
6463
}
6564

6665
#Preview {

0 commit comments

Comments
 (0)