swift - How can I generate a barcode using a specific barcode symbology (codabar)? - Stack Overflow

admin2025-04-25  3

I have a string for which I want to generate a barcode using a specific barcode symbology: codabar.

I can generate a barcode, but it uses the wrong barcode symbology causing it to be read incorrectly by scanners.

func generateBarcode(from string: String) -> Image {
    let context = CIContext()
    let generator = CIFilter.code128BarcodeGenerator()
    generator.message = Data(string.utf8)

    if let outputImage = generator.outputImage,
        let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
        let uiImage = UIImage(cgImage: cgImage)
        return Image(uiImage: uiImage)
    }

    return Image(systemName: "barcode")
}

I have a barcode font and tried to convert the string using that, but that doesn't work. (there is a small chance the font I'm using is incomplete or otherwise "wrong", I did get an error loading it when trying some other stuff)

Text("A25490001104784B").font(.custom("codabar", size: 18))

How can I generate a barcode using a specific barcode symbology (codabar)?

I have a string for which I want to generate a barcode using a specific barcode symbology: codabar.

I can generate a barcode, but it uses the wrong barcode symbology causing it to be read incorrectly by scanners.

func generateBarcode(from string: String) -> Image {
    let context = CIContext()
    let generator = CIFilter.code128BarcodeGenerator()
    generator.message = Data(string.utf8)

    if let outputImage = generator.outputImage,
        let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
        let uiImage = UIImage(cgImage: cgImage)
        return Image(uiImage: uiImage)
    }

    return Image(systemName: "barcode")
}

I have a barcode font and tried to convert the string using that, but that doesn't work. (there is a small chance the font I'm using is incomplete or otherwise "wrong", I did get an error loading it when trying some other stuff)

Text("A25490001104784B").font(.custom("codabar", size: 18))

How can I generate a barcode using a specific barcode symbology (codabar)?

Share Improve this question edited Jan 16 at 17:59 Joakim Danielson 52.4k5 gold badges33 silver badges71 bronze badges asked Jan 16 at 13:31 PieterPieter 2171 gold badge4 silver badges16 bronze badges 6
  • I merely know that Codabar is an old format like for library books. And Code128 is a newer more compact format. So I have my doubts. Maybe you can just use a codabar font to edit the barcode without the generator stuff. I hope someone else knows more. – Joop Eggen Commented Jan 16 at 13:49
  • I found this question and answer right here on SO: stackoverflow.com/questions/78223493/… – Duncan C Commented Jan 16 at 14:17
  • I found that too, but I don't understand the answer to the question--or how to use the mentioned library for that matter. When I use the code from the question I get the same No code generator selected. message. The how to use generator example in the README of the library doesn't work and I haven't gotten it to work to generate a codabar barcode. – Pieter Commented Jan 16 at 15:17
  • ZXing is a barcode library found on Github that supports generating Codabar barcodes along with many others. There seem to be several Swift ports available. Give one of those a try. – HangarRash Commented Jan 16 at 18:39
  • I used ZXing back in the Objective-C days. It worked well. Dunno how good, or how well-maintained, the Swift ports are. – Duncan C Commented Jan 16 at 19:06
 |  Show 1 more comment

1 Answer 1

Reset to default 0

Based on the answer in this question I tried to generate Codabar barcodes using the RSBarcodes_Swift library. I didn't get it working using the usage example from the README, it gave an No code generator selected. message.

RSUnifiedCodeGenerator.shared.generateCode("2166529V", machineReadableCodeObjectType: AVMetadataObject.ObjectType.codabar.rawValue)

This is because the Codabar generator is not listed in the RSUnifiedCodeGenerator.generateCode method.

Instead of trying to use the RSCodaBarGenerator directly I decided to not use the library and instead copy and alter the code necessary to generate Codabar barcodes.

import Foundation
import UIKit
import AVFoundation

open class CodabarGenerator {
    private let codabarAlphabetString = "0123456789-$:/.+ABCD"

    // swiftlint:disable:next cyclomatic_complexity
    private func encodeCharacterString(_ characterString: String) -> String {
        switch characterString {
        case "0":
            return "1010100110"
        case "1":
            return "1010110010"
        case "2":
            return "1010010110"
        case "3":
            return "1100101010"
        case "4":
            return "1011010010"
        case "5":
            return "1101010010"
        case "6":
            return "1001010110"
        case "7":
            return "1001011010"
        case "8":
            return "1001101010"
        case "9":
            return "1101001010"
        case "ー":
            return "1010011010"
        case "$":
            return "1011001010"
        case ":":
            return "11010110110"
        case "/":
            return "11011010110"
        case ".":
            return "11011011010"
        case "+":
            return "10110110110"
        case "A":
            return "10110010010"
        case "B":
            return "10010010110"
        case "C":
            return "10100100110"
        case "D":
            return "10100110010"
        default:
            fatalError("Invalid character \(characterString) for Codabar encoding")
        }
    }

    func isValid(_ contents: String) -> Bool {
        if contents.count > 0
            && contents.range(of: "A") == nil
            && contents.range(of: "B") == nil
            && contents.range(of: "C") == nil
            && contents.range(of: "D") == nil {
            for character in contents {
                let location = codabarAlphabetString.firstIndex(of: character)
                if location == nil {
                    return false
                }
            }
            return true
        } else {
            return false
        }
    }

    func initiator() -> String {
        self.encodeCharacterString("A")
    }

    func terminator() -> String {
        self.encodeCharacterString("B")
    }

    func barcode(_ contents: String) -> String {
        var barcode = ""
        for character in contents {
            barcode += self.encodeCharacterString(String(character))
        }
        return initiator() + barcode + terminator()
    }

    let barcodeDefaultHeight = 28
    var fillColor: UIColor = UIColor.white
    var strokeColor: UIColor = UIColor.black

    func drawCompleteBarcode(_ completeBarcode: String, targetSize: CGSize? = nil) -> UIImage? {
        let length: Int = completeBarcode.count
        if length <= 0 {
            return nil
        }

        // Values taken from CIImage generated AVMetadataObjectTypePDF417Code type image
        // Top spacing          = 1.5
        // Bottom spacing       = 2
        // Left & right spacing = 2
        let width = length + 4
        // Calculate the correct aspect ratio, so that the resulting image can be resized to the target size
        var height = barcodeDefaultHeight
        if let targetSize = targetSize {
            height = Int(targetSize.height / targetSize.width * CGFloat(width))
        }
        let size = CGSize(width: CGFloat(width), height: CGFloat(height))
        UIGraphicsBeginImageContextWithOptions(size, false, 1)
        if let context = UIGraphicsGetCurrentContext() {
            context.setShouldAntialias(false)

            self.fillColor.setFill()
            self.strokeColor.setStroke()

            context.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
            context.setLineWidth(1)

            // swiftlint:disable:next identifier_name
            var i = 0
            for char in completeBarcode {
                i += 1
                if char == "1" {
                    // swiftlint:disable:next identifier_name
                    let x = i + (2 + 1)
                    context.move(to: CGPoint(x: CGFloat(x), y: 1.5))
                    context.addLine(to: CGPoint(x: CGFloat(x), y: size.height - 2))
                }
            }
            context.drawPath(using: CGPathDrawingMode.fillStroke)
            let barcode = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()

            return barcode
        } else {
            return nil
        }
    }
}

Using the above code barcodes can be generated like this.

func generateBarcode(contents: String) -> Image {
    let codabarGenerator = CodabarGenerator()
    let encodedBarcode = codabarGenerator.barcode(contents)

    if let barcodeImage = codabarGenerator.drawCompleteBarcode(encodedBarcode) {
        return Image(uiImage: barcodeImage
    } else {
        return Image(systemName: "xmark.circle")
    }
}
转载请注明原文地址:http://anycun.com/QandA/1745529893a90820.html