Generating an EAN-13 barcode using Ruby on Rails and RMagick

Posted by matt on December 19, 2006

A fair while ago now I had to create an administration system for a client which needed a barcode printed out to stick on various items. These barcodes were then to be read by a barcode reader at a later date to check their progress in the system. There doesn’t appear to be much information on the internet on how to do this, so I have decided to publish my barcode creating code. There are many different types of barcode. I chose to generate one of the most popular formats - EAN-13.

The demonstration

There is a bare bones demonstration here: Barcode demonstration

Create the form

<div style="width: 500px;">
<%= start_form_tag( :action => "barcode" ) %>
<label>Code <small>(12 or 13 digits)</small></label>
<%= text_field( "e_a_n_bar_code", "code", "size" => 15 ) %>
<label>height <small>(20-50)</small></label>
<%= text_field( "e_a_n_bar_code", "size", "size" => 5 ) %>
<%= submit_tag( "go" ) %>
<%= end_form_tag %></div>

Create the controller

class BarcodeDemoController < ApplicationController
        def form
        end
        def barcode
            bar = EanBarCode.new
            @image = bar.getImage( params[ :e_a_n_bar_code ][ "code" ], params[ :e_a_n_bar_code ][ "size" ].to_i, 'png' )
        end
end

Create the model

class EanBarCode
  SIZES = [
      [ "20", "20" ],
      [ "30", "30" ],
      [ "40", "40" ],
      [ "50", "50" ]
    ].freeze
  attr_reader :groupC, :family, :ean13, :courB08_pil, :courB08_pbm
  attr_writer :groupC, :family, :ean13
   def initialize
      a = { 0 => "0001101", 1 => "0011001", 2 => "0010011", 3 => "0111101", 4 => "0100011",
            5 => "0110001", 6 => "0101111", 7 => "0111011", 8 => "0110111", 9 => "0001011"}
      b = { 0 => "0100111", 1 => "0110011", 2 => "0011011", 3 => "0100001", 4 => "0011101",
            5 => "0111001", 6 => "0000101", 7 => "0010001", 8 => "0001001", 9 => "0010111"}
      c = { 0 => "1110010", 1 => "1100110", 2 => "1101100", 3 => "1000010", 4 => "1011100",
            5 => "1001110", 6 => "1010000", 7 => "1000100", 8 => "1001000", 9 => "1110100"}
      self.groupC = c
      self.family = {0 => [a,a,a,a,a,a], 1 => [a,a,b,a,b,b], 2 => [a,a,b,b,a,b], 3 => [a,a,b,b,b,a], 4 => [a,b,a,a,b,b],
                     5 => [a,b,b,a,a,b], 6 => [a,b,b,b,a,a], 7 => [a,b,a,b,a,b], 8 => [a,b,a,b,b,a], 9 => [a,b,b,a,b,a]}
    end
   def makeCode( code )
      self.ean13 = code.split(//)
      # If the code already has a checksum
      if self.ean13.length == 13
        # Verify checksum
        verifyChecksum( self.ean13 )
      end
      # If the code does not have a checksum
      if self.ean13.length == 12
        # Add checksum value
        self.ean13 << computeChecksum( self.ean13 )
      end
      left = self.family[ self.ean13[ 0 ].to_i ]
      # Add start separator
      strCode = 'L0L'
      for i in 0..5
        strCode += left[ i ][ self.ean13[ i + 1 ].to_i ]
      end
      # Add the middle separator
      strCode += '0L0L0'
      # Compute the right codage class
      for i in 7..13
        strCode += self.groupC[ self.ean13[ i ].to_i ]
      end
      # Add stop separator
      strCode += 'L0L'
      return strCode
    end
   def computeChecksum( arg )
      # UPCA/EAN13
      weight=[1,3]*6
      magic=10
      sum = 0
      for i in 0..11         # checksum based on first 12 digits.
         sum = sum + arg[i].to_i * weight[i]
      end
      z = ( magic - (sum % magic) ) % magic
      if z < 0 or z >= magic
         return None
      end
      return z
    end
   def verifyChecksum( bits )
      computedChecksum = self.computeChecksum(bits[12])
      codeBarChecksum = bits[12]
   end
    def getImage( value, height = 50, extension = ‘png’ )
      # courbB08.pil PIL Font file uuencoded
      courB08_pil =”"”eJztl91rFkcUxp+Zt7vGFYzVtiJKICgYlLRWkaBBVGgDraFGCH5gsQp+QMBqabAVRYJYAlakCkohCpYgxaLkIu1NvLBeSAStglpqL6xQAsVe2AuL5u2buH3mzGaYPf9AKWTl8d3nl7MzZ2bnazvea9+97+PurFWut5e0Zu+s7VybYfKavP7LK3X/5TlM4Q3/OWbyf1ARD/6mgb2SjwtPhbpnq0iKZ6ahrmCjwqbxdgamRnHOA69jimN5zvIS8cDcUEeVdYzRAw1FHcJYXgPvG4s6Jlgj7xeEequS3wLeNvGvnrEOtq+Jt82szT+b86+WHlgS2jHGuHF6YHnog1zaupxqCcy3t4X3rVG9iXhgjW+bsFQ80BaxRDywTrF1VId6toPaqOI2UlsV20ptV2w7tUuxXVSXYl3UvoIZ9kFFPPBJ6D/HLD3QXbwjyDjI6YHPiz5FXiN7SQ8cDu/N9/1h3veEOP/Oe6gvQnmuvYYe+NL3qYyNVDxw2seF8XKa+jrKJREPnFdx56l+xfqpS4pdogZUeQPU91FcKh64GveBeOCaKu8adUM9e4O6reJuU/cUu0c9VM8+pB6r/B5TI+rZEerPUpyhB/6K5lsqHniuyntO1VR5Nb5CU86FHqZOsTqqXrF66o2ojlQ8zDwVN4+aX86FHqYpXg9YLeevWRzPc7LFZG+V1wN6mKXxvMzH6GFaJua5zGNLD7MqmtNcc+hh1oT1oCb5cf6aNj92mbPMGXqY9jCPasLaqQ1hjMv8pYfZpOI2UR9GcYl4mB1RnMtvB9me8N583B5qb3mNoIf5NGJc1+hhPvPrrjybioc5op49Qh0Ldfj8jlHHQ3s9O059Fc3zRDzMmVKcpYfpU+3oI/umxJyH+TYqLxUPc0X13xVqMMovFQ8zpPIbon6MWCoeZljVMUz9VIqz9DAP1Dt6QP0a9gpZ7+lhHhXjysreaOhhfiv1vaGH+T2Mv5rbU+hh/uAaOnlNXv+Hy4/7mtv3OW5hnpTODIYe5mm0xqbiYf4OcbLv08NU1ZyuuqKLOEvm6sjhJkd8TjRustgkrO3uvFGjh60r1uyiPHrY6eH84tb7l/SwM8vrAT3snHgNY9wcsoby+Y8edn5UxxTxsIuitrlcFpG9GcVx/6CHXRrKk72MHrYl3stYB/ceu7I4X02wlWSrCmaF1ehhV7NrovWKHrattI4betj20Fc8r7E87kf2g+gcy32BHnZDfKZmHPco2xnl4vqlk2yz6r/N1EfRPpiKh90d7VGpeNi9inGPst2lNdbSwx4McS8k7iDVE/Ytz3qoXsV6qZOKnaTOBDYqjPuRPRfOkz7uHNUf4uQMQg/7XekMYulhB6JnE/GwP0T1JuJhryrGM6G9HuWSiIcdDnPmhTs70sPeCuPes1vUXcXuUvcDGxV2n/olOisn4mEfhfOVby/3KDsSlZeIh32iGOe0faoY57R9ptgzajTKJREPOx7aJnOfHhUbxov0Mz0qU8v50aMyo/wu6VGZrdhsqqH8fnllHEEz4zj6DNMxK+4X+gyv8cszyoU+4zfmjNAO9zuXrNGXF1gj2ULFFpI1K9ZMtiww//22jGwFXg39535XkK0O+cl5gz7Du6iP5wd9hvfDs9LP9BnWR/U6tp6sU7FOsi1RLo5tIdsWled+t5HtVO3YSdalWBfZftW2/WQHVH4HyA6F9+GfPUR2VOV3lKxXsV6yE4qdIDul2Cmys6ptZ8n6Qi7+m7OP7ELoU/8tdIHsoo8L+V0ku6xyvkw2qNgg2VBgvg+GyK6XyrP0GW5ydE3EuXd5k+xOeOdVibtD9jNm/Qv15O4i”"”
      # courbB08.pbm font file uuencoded
      courB08_pbm =”"”eJxNkntM01cUx8+P2/1apUAZEpECq4KRjKhF0E55FYEp4yG6mglz2Q8Q1BhERhhls/zKI+CID4wbIAPKpk4GAzqZPKKMX2GIUwGJG+ImtKwKjIzXcGuBtncV0Hn+uLnn5Nzv55xv7mdRkbusVjquBACr0N3B+wCQi/m+ijAf4LGl/wgAiwkNDpRIyyABSjGkBQ/fa3c1bfLs4U8ulDcYUs/502rTpIlO9pycKp/Buql6f3rmZ1NqvpO2SZXf0duY3j0563zjoZpW8AvHRmVeZ/Co36mFR8bERzlsxOMJ+oJshsS57rlfzFzmnZFEFnIEZjTGizgLsLzjl4QtrNprBRu10e+u9GgePHjG63bPDw/H87uix0Vtsvkqg9qOlUimPLiOM4z69YfqIu5Pa2Sr/io6n9Xmf9e+57W1Iapo4lLQBdLSWc/z3KOSlgznDXTW/Flh21kXIeUIX8FZVL9dwP4NBH5jglYxkBNFmWgMcfsAxM/9gEL5TTwYpnfElR8qQ+WiCgeTHOAfb2bW/cQC/FozFOOQzAebtjRvQLI7HBtXvaZe25a3Q/1vZpPa+kd1XXKuflr5Cm48YUsUcjMXjsm/sf+22s6zQAbGZ8mEXMzSE4y9AHhRpltwB1N9ynz5H2MOi0MEi4E5O1ov9ogrFU5cMWAcdgQb3xHFtFK+0pkhVnYWxltx92j69p6jJ9OnHr+Cq5×5X6Mz70JcX2tEG5LIShM4EHIGoLIRsHzcvEuGwMYA4DZPn7gPMA1QIgltnt82cTu7j5n76mmz3TU5Bh3PFRTHku52aBgaTnJD7m1c0a3hNjbWWjBtMsP/OFac/LYANAAWepdYodB58NBFIuOjNSQ4cgXplqP2RyOe8fd999T8weqBRwLwNFdQobHgA1/YTV8PH+TwV59vBo7Y1J4rmHFv3T9e8rmmXdGSuPpSbBnhYJ7V8ICz6AfGcdTpRkpCUU8WcOT8wb+dSHIb6QZapx0MY2DO4i7jYV2AUNkkErpQFHVYmFRmYD7OJhDyQSiow4IkrS3TbpQqFA9slE4jnj6peXMTC+N8buJ20Uv5eOothuGIiluyCDtff3miBzJHjncOIC3bPT8FLabRPd0TCWy346Mmn9Rz23WyNMJcsnqhQani3CMFOZuYU7c20zTNVqNbGPNxALWnybeLEcTvXWpc10leI5ae/CI9qBqI686cnO6P6F33e2vAp0nz9+hnbNeueh/261UJK5aVeSf73ZSXA7dOBXvkXODEb9hVww4KtPNAbPvaZbi0q9kICCl+CiBJSzLva8TlntYlC4UHvCRTlaXOy13VAbN0eae2v3hNesWXLsWPkjfOPq7e6zd1fOfc1TckDaylrvleinnT8Ui87ScLMVhhEx7SUJ8U2zKrRR2Z1dEqZlkr7kDTuhFjpkvse9ZXN0R9H+DlYA4TXVm6/kXDQMyTeGnJFXlLlSgva5iLUEcbiyDzNqf4Wr9kKYVUIcY40DrnsW4E4zW9QxnHVYx+bo64mIskDWjZgCrqeVQFrS7Sh/uFLftIidKWbgj6Oq652d4c3v88Dw2JDK7bSWX/ByuaLZI=”"”
      decodeFontFile( courB08_pil, “courB08.pil” )
      bits = makeCode( value )
      bits = bits.split( // )
      code = “”
      for digit in self.ean13
        code += “%d”%digit
      end
      require ‘RMagick’
      position = 8
      # Create a new image
      im = Magick::ImageList.new
      im.new_image( bits.length + position, height )
      # Create a drawer
      draw = Magick::Draw.new
      # Draw the bar codes
      for bit in 0..bits.length
        # Draw normal bar
        if bits[ bit ] == ‘1′
          draw.fill( ‘black’ )
          draw.rectangle( bit + position, 0, bit + position, height - 10 )
        end
        # Draw long bar
        if bits[ bit ] == ‘L’
          draw.fill( ‘black’ )
          draw.rectangle( bit + position, 0, bit + position, height - 3 )
        end
      end
      draw.draw( im )
      im.write( “barcode.” + extension )
      return bits
    end
  def decodeFontFile(data, file)
#     from zlib import decompress
#     from base64 import decodestring
#     from os.path import exists
    data = Base64.decode64( data )
     # If the font file is missing
      if not File.exists?( file )
        # Write font file
        File.open( file, “w+” ).write( data )
      end
  end
  def testWithChecksum()
     bar = EanBarCode()
     assert(bar.makeCode(’0000000000000′)== ‘L0L0001101000110100011010001101000110100011010L0L0111001011100101110010111001011100101110010L0L’ )
     assert(bar.makeCode(’1111111111116′)== ‘L0L0011001001100101100110011001011001101100110L0L0110011011001101100110110011011001101010000L0L’ )
     assert(bar.makeCode(’2222222222222′)== ‘L0L0010011001001100110110011011001001100110110L0L0110110011011001101100110110011011001101100L0L’ )
     assert(bar.makeCode(’3333333333338′)== ‘L0L0111101011110101000010100001010000101111010L0L0100001010000101000010100001010000101001000L0L’ )
     assert(bar.makeCode(’4444444444444′)== ‘L0L0100011001110101000110100011001110100111010L0L0101110010111001011100101110010111001011100L0L’ )
     assert(bar.makeCode(’5555555555550′)== ‘L0L0110001011100101110010110001011000101110010L0L0100111010011101001110100111010011101110010L0L’ )
     assert(bar.makeCode(’6666666666666′)== ‘L0L0101111000010100001010000101010111101011110L0L0101000010100001010000101000010100001010000L0L’ )
     assert(bar.makeCode(’7777777777772′)== ‘L0L0111011001000101110110010001011101100100010L0L0100010010001001000100100010010001001101100L0L’ )
     assert(bar.makeCode(’8888888888888′)== ‘L0L0110111000100101101110001001000100101101110L0L0100100010010001001000100100010010001001000L0L’ )
     assert(bar.makeCode(’9999999999994′)== ‘L0L0001011001011100101110001011001011100010110L0L0111010011101001110100111010011101001011100L0L’ )
  end
  def testWithoutChecksum()
     bar = EanBarCode()
     assert(bar.makeCode(’000000000000′)== ‘L0L0001101000110100011010001101000110100011010L0L0111001011100101110010111001011100101110010L0L’ )
     assert(bar.makeCode(’111111111111′)== ‘L0L0011001001100101100110011001011001101100110L0L0110011011001101100110110011011001101010000L0L’ )
     assert(bar.makeCode(’222222222222′)== ‘L0L0010011001001100110110011011001001100110110L0L0110110011011001101100110110011011001101100L0L’ )
     assert(bar.makeCode(’333333333333′)== ‘L0L0111101011110101000010100001010000101111010L0L0100001010000101000010100001010000101001000L0L’ )
     assert(bar.makeCode(’444444444444′)== ‘L0L0100011001110101000110100011001110100111010L0L0101110010111001011100101110010111001011100L0L’ )
     assert(bar.makeCode(’555555555555′)== ‘L0L0110001011100101110010110001011000101110010L0L0100111010011101001110100111010011101110010L0L’ )
     assert(bar.makeCode(’666666666666′)== ‘L0L0101111000010100001010000101010111101011110L0L0101000010100001010000101000010100001010000L0L’ )
     assert(bar.makeCode(’777777777777′)== ‘L0L0111011001000101110110010001011101100100010L0L0100010010001001000100100010010001001101100L0L’ )
     assert(bar.makeCode(’888888888888′)== ‘L0L0110111000100101101110001001000100101101110L0L0100100010010001001000100100010010001001000L0L’ )
     assert(bar.makeCode(’999999999999′)== ‘L0L0001011001011100101110001011001011100010110L0L0111010011101001110100111010011101001011100L0L’ )
  end
end

barcode.rhtml code

<%= image_tag( "/barcode.png" ) %>

Rails - How to find out who is online

Posted by matt on December 13, 2006

Well, this is my first post. I was frustrated in to starting this blog, through searching the internet for solutions to various programming problems, and finding either nothing, or only half the answer. Hopefully I will be able to help others solve some of their problems faster. Recently I have been writing a dating site in ruby on rails. One quite important feature of this site is a list of members who are currently online. After a couple of days of pain, here’s the pretty simple solution to the problem:

First off this only works if you use ActiveRecord to manage your sessions. To do this you must uncomment the following line in your environment.rb file:

config.action_controller.session_store = :active_record_store

Then create the sessions table by running the following command from your RAILS_ROOT:

rake db:sessions:create

Now, whenever a user logs in to your application add their id to the session. In my app at the moment it looks something like this:

member = Member.new( @params[ :member ] )
member = Member.find( :all, :conditions => "screen_name = '#{member.screen_name}' AND password = '#{member.password}'" )
if member != nil
  session[ :member_id ] = member.id
  redirect_to :action => 'success'
else
  redirect_to :action => 'form'
end

This code will need work for a production environment, for one thing it is open to SQL injection attacks.
Now to find all of the users who are currently online it is just a matter of searching the sessions table for all the users who have been active in the last 10 minutes. This can be done using rails built in session model. Finally the session data needs to be unmarshaled and decoded. This stumped me for a while, but finally I found the answer at http://caboo.se/doc/classes/CGI/Session/ActiveRecordStore/SqlBypass.html. So here is my code to return an array of the member_ids of all the members online and active within the last 10 minutes.

def who_is_online
  @whos_online = Array.new()
  onlines = CGI::Session::ActiveRecordStore::Session.find( :all, :conditions => [ 'updated_at = ?', Time.now() - 10.minutes ] )
  onlines.each do |online|
    id = Marshal.load( Base64.decode64( online.data ) )
    @whos_online << id[ :member_id ]
  end
  return @whos_online
end