Generate Barcodes with JRuby and Barcodes4J
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:
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.