Generate Barcodes with JRuby and Barcodes4J

Devdatta Kane
Share

barcode4j-logo

Ruby has a very vibrant ecosystem that provides solutions for almost all common problems. But one particularly lagging area is barcode generation. Ruby only has solutions for generating QR Codes, which are undoubtedly popular, but do not enjoy mass acceptance in industry segments. Older barcode symbologies, such as Code 128, DataMatrix, and PDF417, enjoy more acceptance in business applications than QR Codes. What should we do if need to generate a Code 128 in a Ruby/Rails application?

Fortunately we have a solution: JRuby. JRuby allows us to leverage excellent Java libraries and use them in Ruby applications. One such library is Barcode4J which is popular in the Java world for generating barcodes. Today, we will learn how to integrate and use Barcode4J in Ruby. So, let’s get started.

Basic Rails appliation

We will create a basic Rails application. The application will use JRuby, Rails 4.2, and SQLite as thedatabase to keep things simple. First of all, install JRuby with RVM or rbenv.

Switch to JRuby and gem install the latest Rails gem. Now create a new Rails application, like so:

rails new barcode4j_rails -T

After the application is generated, switch to the application directory and check out how it’s working:

rails s

Point your favorite browser to http://localhost:3000 and we should see the default Rails page.

Integrating Barcode4J

We will now integrate Barcode4J into our Rails app. First, download the Barcode4J files from theBarcode4J Project Page page. We will be using the 2.1.0 version. Download barcode4j-2.1.0-bin.zip and unzip the file. Copy barcode4j.jar from the build folder into lib/barcode4j in our application directory. Also, copy all files from the lib folder into lib/barcode4j.

Finally, our directory structure should look as follows:

barcode4j_rails
  - app
  ...
  - lib
    - barcode4j
      - barcode4j.jar
      ...

Now that we have all required files in place, create a class named barcode_generator.rb in lib directory with following code:

# Load all Java libraries related to Barcode4J
Dir.entries("#{Rails.root}/lib/barcode4j").each do |lib|
  require "barcode4j/#{lib}" if lib =~ /\.jar$/
end

require 'java'

# Load all class references
java_import Java::java.io.ByteArrayOutputStream
java_import Java::java.awt.image.BufferedImage
java_import Java::org.krysalis.barcode4j.impl.code128.Code128Bean
java_import Java::org.krysalis.barcode4j.impl.datamatrix.DataMatrixBean
java_import Java::org.krysalis.barcode4j.impl.code39.Code39Bean
java_import Java::org.krysalis.barcode4j.impl.pdf417.PDF417Bean
java_import Java::org.krysalis.barcode4j.output.bitmap.BitmapCanvasProvider
java_import Java::org.krysalis.barcode4j.tools.UnitConv

class BarcodeGenerator
  def self.generate(symbology, data)
    # Create a ByteArrayOutputStream object for storing the image
    bos = ByteArrayOutputStream.new

    # Set DPI
    dpi = 160

    bean = nil

    # Set the Canvas object for barcode rendering
    canvas = BitmapCanvasProvider.new(bos, "image/x-png", dpi, BufferedImage::TYPE_BYTE_BINARY, false, 0)

    # Set barcode symbology
    case symbology
    when "code128"
      bean = Code128Bean.new
    when "datamatrix"
      bean = DataMatrixBean.new
    when "code39"
      bean = Code39Bean.new
    when "pdf417"
      bean = PDF417Bean.new
    end

    # Configure the barcode generator
    bean.set_module_width(UnitConv.in2mm(2.8 / dpi))
    bean.do_quiet_zone(true)

    # Generate barcode 
    bean.generateBarcode(canvas, data)
    canvas.finish()

    # Convert image to byte array for streaming
    bytes = bos.toByteArray
    bos.close

    return String.from_java_bytes(bytes)
  end
end

We have created a simple utility class that can generate four types of barcodes, sending send back an image file in PNG format. All we have to pass is the symbology and data for the barcode. Let’s break down the inner workings of the class.

First, we load all .jar files into the classpath:

Dir.entries("#{Rails.root}/lib/barcode4j").each do |lib|
  require "barcode4j/#{lib}" if lib =~ /\.jar$/
end

Then, import all required references:

java_import Java::java.io.ByteArrayOutputStream
java_import Java::java.awt.image.BufferedImage
java_import Java::org.krysalis.barcode4j.impl.code128.Code128Bean
java_import Java::org.krysalis.barcode4j.impl.datamatrix.DataMatrixBean
java_import Java::org.krysalis.barcode4j.impl.code39.Code39Bean
java_import Java::org.krysalis.barcode4j.impl.pdf417.PDF417Bean
java_import Java::org.krysalis.barcode4j.output.bitmap.BitmapCanvasProvider
java_import Java::org.krysalis.barcode4j.tools.UnitConv

Next, set the DPI for the barcode image and create a ByteArrayOutputStream object to hold the rendered image. Then, create a BitmapCanvas on which to actually draw the barcode. Like so:

# Create a ByteArrayOutputStream object for storing the image
bos = ByteArrayOutputStream.new

# Set DPI
dpi = 160

bean = nil

# Set the Canvas object for barcode rendering
canvas = BitmapCanvasProvider.new(bos, "image/x-png", dpi, BufferedImage::TYPE_BYTE_BINARY, false, 0)

Initialize the barcode symbology as provided:

# Set barcode symbology
case symbology
when "code128"
  bean = Code128Bean.new
when "datamatrix"
  bean = DataMatrixBean.new
when "code39"
  bean = Code39Bean.new
when "pdf417"
  bean = PDF417Bean.new
end

Configure the barcode generator for settings such as quiet zone and module width:

# Configure the barcode generator
bean.set_module_width(UnitConv.in2mm(2.8 / dpi))
bean.do_quiet_zone(true)

Next, invoke the actual barcode generation method:

# Generate barcode 
bean.generateBarcode(canvas, data)
canvas.finish()

Lastly, convert the barcode image into byte stream and return the image:

# Convert image to byte array for streaming
bytes = bos.toByteArray
bos.close

return String.from_java_bytes(bytes)

Now we have our barcode generator in place. Let’s create an initializer in config/initializers/barcode.rb to load the class in Rails:

require 'barcode_generator'

Testing the Barcode Generator

Now we will create a BarcodesController class in app/controllers with the following code:

class BarcodesController < ApplicationController
  def index
  end

  def generate
    send_data BarcodeGenerator.generate(params[:symbology], params[:data]), :type => :png, :disposition => 'inline'
  end
end

We defined two actions here. First, an index action to render the actual page of the Barcode Generator, which we will create shortly. Second, a generate action to call and render the barcode image. The generate action invokes the BarcodeGenerate.generate method with the symbology and data parameters from the URL. Then, generate uses the send_data method to stream the image to browser.

Now update config/routes.rb to add routes to those actions:

Rails.application.routes.draw do
  root 'barcodes#index'
  get '/barcodes/:symbology' => 'barcodes#generate'
end

the last thing to do is to create the actual UI for the Barcode Generator. Create a view in app/views/barcodes/index.html.erb with the following code:

<table border="0">
  <tr>
    <td colspan="2"><h2>Barcode Generator</h2></td>
  </tr>
  <tr>
    <td><strong>Data:</strong></td>
    <td><input name="data" id="data" value="Hello World" /></td>
  </tr>
  <tr>
    <td><strong>Symbology:</strong></td>
    <td>
      <select name="symbology" id="symbology">
        <option value="code128">Code 128</option>
        <option value="datamatrix">Datamatrix</option>
        <option value="code39">Code 39</option>
        <option value="pdf417">PDF 417</option>
      </select> 
    </td>
  </tr>
  <tr>
    <td colspan="2" align="center"><button name="submit" id="submit">Generate Barcode</button></td>
  </tr>
  <tr>
    <td colspan="2" align="center"><img src="" id="barcode_image"></td>
  </tr>
</table>

<script type="text/javascript">
  $(document).ready(function() {
    $('button#submit').on('click', function() {
      $('img#barcode_image').attr('src', '/barcodes/' + $('select#symbology').val() + '?data=' + $('input#data').val());
    })
  });
</script>

We simply create a form with a field for entering data and another for selecting the barcode symbology. When we click the ‘Generate Barcode’ button, we simply update the img tag’s src attribute with the relevent URL to generate our barcode image.

Now, let’s take it for a spin. Start (or restart) the server with rails s and visit http://localhost:3000. We should see the form with Hello World already in the ‘Data’ field. Click the ‘Generate Barcode’ button and you’ll receive a freshly mintedl Code 128 barcode!

Change the symbology to ‘Datamatrix’ and click ‘Generate Barcode’ again. Now we should see a Datamatrix barcode. Here’s what it looks like:

datamatrix

Wrapping Up

Today we learned how to integrate and generate various types of barcodes using Barcode4J. We can improve the generator class by including more symbology specific parameters or more symbologies as well. But that I leave this as an exercise for you. I sincerely hope you liked the tutorial.

Comments and suggestions welcome, as always.

CSS Master, 3rd Edition