KasztelanKarta/Kasztelan/EAN13BarcodeView 2.swift
2026-10-16 23:37:49 +01:00

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)
}
}