EAN-13 generation

PS 10Dec2003 - Making EAN13 barcodes is slightly more complex than Code 39 generation, but not a very difficult task.

I got almost all information on how to do this from [1 ]. If you're interested in this app, you should really go read that page and related ones.

I have included a sample (real) product number: 8710400163398 decodes as:

 87 - netherlands
 10400 - Albert Heijn
 16339 - Fruit drink orange-peach
 8 - check digit

http://pascal.scheffers.net/software/barcode-screenshot.gif

Have fun -- Pascal.


    #EAN 13 encoding routines.
    #A complete description of EAN-13 barcodes can be found on 
    # http://www.barcodeisland.com/ean13.phtml
    
    #The EAN13 check sum is calculated by taking the first twelve
    #digits of the barcode (the 13th will be the check digit)
    #Take all odd number digits (the first digit is odd) add them up 
    #and multiply by 3. Add to that the sum of the remaining six even positions
    #The checkdigit is an inverted modulo 10. Eaxample: If the total sum is 87
    #how much must I add to get to the next multiple of ten (90) which is 
    #three. Making the check digit '3'.

    proc ean13_csum { number } {
        set odd 1
        set sum 0
        foreach digit [split $number ""] {
            set odd [expr {!$odd}]
            puts "$sum += ($odd*2+1)*$digit :: [expr {($odd*2+1)*$digit}]"
            incr sum [expr {($odd*2+1)*$digit}]
        }
        set check [expr {$sum % 10}]
        if { $check > 0 } {
            return [expr {10 - $check}]
        }
        return $check
    }

    proc ean13 { number } {

        if { [string length $number] < 12 } {
            error "Not enough digits, need 12 or 13."
        }

        #EAN13 digits, the ones represent black bars, the zeroes white space.
        #Three pairs of digits: lefthand-odd(A), lefthand-even(B) and righthand
        
        set digits {
            {0 0001101 0100111 1110010}
            {1 0011001 0110011 1100110}
            {2 0010011 0011011 1101100}
            {3 0111101 0100001 1000010}
            {4 0100011 0011101 1011100} 
            {5 0110001 0111001 1001110}
            {6 0101111 0000101 1010000}
            {7 0111011 0010001 1000100}
            {8 0110111 0001001 1001000}
            {9 0001011 0010111 1110100}
        }

        #The first digit of the ean13 code is encoded in the parity of 
        #characters 2 through 7, using this table. Note that the ones and twos
        #are just list indices to the digits table above.
        array set parity_enc {
            0 {1 1 1 1 1}
            1 {1 2 1 2 2}
            2 {1 2 2 1 2}
            3 {1 2 2 2 1}
            4 {2 1 1 2 2}
            5 {2 2 1 1 2}
            6 {2 2 2 1 1}
            7 {2 1 2 1 2}
            8 {2 1 2 2 1}
            9 {2 2 1 2 1}
        }
        
        #If we get a twelve digit number, append the checkdigit to that:
        if { [string length $number] == 12 } {
            set number $number[ean13_csum $number]
        }
        
        #All EAN13 codes start with 'left guard bars':
        lappend bars 101
        #second 'system' char, encoded as a lefthand-odd character:
        lappend bars [lindex [lindex $digits [string index $number 1]] 1]    

        #the five digits that encode the first 'system' digit in their parity:
        foreach digit [split [string range $number 2 6] ""] \
            enc $parity_enc([string index $number 0]) {
                lappend bars [lindex [lindex $digits $digit] $enc]
            }

        #center guard bars:
        lappend bars 01010

        #the right hand chars, all encoded the same:
        for {set i 7} {$i<13} {incr i} {
            lappend bars [lindex [lindex $digits [string index $number $i]] 3]
        }

        #and the final guards:
        lappend bars 101
        
        return [list $bars $number]
    }

    # Tk demo code:

    package require Tk

    wm title . "EAN-13 Barcode"

    label .lbar -text "Barcode"
    entry .barcode -textvariable barcode -width 15
    button .show -text show -command do_display
    grid .lbar .barcode .show -sticky nw

    canvas .c -width 200 -height 100

    grid x .c - -sticky nw

    proc do_display { } {
        global barcode 
        if { [string length $barcode] < 12 } {
            return
        }
        
        .c delete all
        set b [ean13 $barcode]

        set x 25 
        foreach barset [lindex $b 0] {

            set bars [split $barset ""]

            if {[llength $bars] == 7} {
                set len 40
            } else {
                set len 46
            }

           foreach bar $bars { 
                incr x 1
                if { $bar } {
                    .c create line $x 15 $x [expr $len+15]   

                }
            }
        }

        #And create the human readable text below it:
        .c create text 21 60 -text [string index [lindex $b 1] 0]
        .c create text 49 60 -text [string range [lindex $b 1] 1 6]
        .c create text 95 60 -text [string range [lindex $b 1] 7 12]
        
    }

    set barcode {8710400163398} 

    do_display
 }

kroc 11 Oct 2004 : If you want to do EAN8 barcodes too, you should replace [ean13_csum] by this one:

 proc ean13_csum { number } {
    set C 0
    if {[string length $number]%2 != 0} {
        set number 0$number
    }
    foreach { I P } [split $number {}] {
        set C [expr $C + (3 * $P) + $I]
    }
    return [string index [expr 10-$C%10] end]
 }