Generating an EAN-13 barcode using Ruby on Rails and RMagick
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
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