48 lines
1.9 KiB
Swift
48 lines
1.9 KiB
Swift
import CoreImage
|
|
import CoreImage.CIFilterBuiltins
|
|
import SwiftUI
|
|
|
|
/// Printed Kasztelan cards contain an arbitrary 13-digit account number.
|
|
/// Code 128 preserves all 13 digits exactly. EAN-13 cannot, because its final
|
|
/// digit is always a mandatory checksum.
|
|
struct CardBarcodeView: View {
|
|
let digits: String
|
|
@Environment(\.displayScale) private var displayScale
|
|
|
|
var body: some View {
|
|
GeometryReader { geometry in
|
|
if let image = barcodeImage(
|
|
targetWidth: geometry.size.width,
|
|
targetHeight: geometry.size.height,
|
|
displayScale: displayScale
|
|
) {
|
|
Image(decorative: image, scale: displayScale)
|
|
.interpolation(.none)
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
|
|
}
|
|
}
|
|
.background(.white)
|
|
.accessibilityElement()
|
|
.accessibilityLabel("Barcode \(digits)")
|
|
}
|
|
|
|
private func barcodeImage(targetWidth: CGFloat, targetHeight: CGFloat, displayScale: CGFloat) -> CGImage? {
|
|
let clean = String(digits.filter(\.isNumber))
|
|
guard clean.count == 13, let data = clean.data(using: .ascii) else { return nil }
|
|
|
|
let generator = CIFilter.code128BarcodeGenerator()
|
|
generator.message = data
|
|
generator.quietSpace = 12
|
|
guard let output = generator.outputImage else { return nil }
|
|
|
|
let pixelWidth = floor(targetWidth * displayScale)
|
|
let pixelHeight = floor(targetHeight * displayScale)
|
|
let horizontalScale = max(1, floor(pixelWidth / output.extent.width))
|
|
let verticalScale = max(1, floor(pixelHeight / output.extent.height))
|
|
let scaled = output.transformed(
|
|
by: CGAffineTransform(scaleX: horizontalScale, y: verticalScale)
|
|
)
|
|
return CIContext(options: [.useSoftwareRenderer: false]).createCGImage(scaled, from: scaled.extent)
|
|
}
|
|
}
|