How to generate 2D barcode as vector image?

Category: 
Tags: bar codePDF417iText 5

I am generating 2D barcodes using iText API, but these barcodes are placed into the PDF document as raster images, hence reducing the quality of the barcode on low resolution printers. As a result, we're unable to scan the barcode. This is our code:

BarcodePDF417 pdf417 = new BarcodePDF417();
String text = "BarcodePDF417 barcode";
pdf417.setText(text);
Image img = pdf417.getImage();        
document.add(img);
I discoved the placeBarcode() method that is supposed to create a vector image. I tried using it like this:

Rectangle pageSize = new Rectangle(w * 72, h * 72);
Document doc = new Document(pageSize, 1f, 1f, 1f, 1f);
PdfWriter writer = PdfWriter.getInstance(doc, getOutputStream());
doc.open();
PdfContentByte cb = writer.getDirectContent();
BarcodePDF417 pf = new BarcodePDF417();
pf.setText("BarcodePDF417 barcode");
Rectangle rc = pf.getBarcodeSize();
pf.placeBarcode(cb, BaseColor.BLACK, rc.getHeight(), rc.getWidth());
doc.close();
This result in a page that is completely black.

Posted on StackOverflow on May 12, 2015 by Dhorrairaajj

Please take a look at the BarcodePlacement example. In this example, we create three PDF417 barcodes:

PdfContentByte cb = writer.getDirectContent();
Image img = createBarcode(cb, "This is a 2D barcode", 1, 1);
document.add(new Paragraph(
    String.format("This barcode measures %s by %s user units",
       img.getScaledWidth(), img.getScaledHeight())));
document.add(img);
img = createBarcode(cb, "This is NOT a raster image", 3, 3);
document.add(new Paragraph(
    String.format("This barcode measures %s by %s user units",
        img.getScaledWidth(), img.getScaledHeight())));
document.add(img);
img = createBarcode(cb, "This is vector data drawn on a PDF page", 1, 3);
document.add(new Paragraph(
    String.format("This barcode measures %s by %s user units",
        img.getScaledWidth(), img.getScaledHeight())));

The result looks like this on the outside:

Bar codes
Bar codes

One particular barcode looks like this on the inside:

Vector data
Vector data

I'm adding this inside view to show that the 2D barcode is not added as a raster image (as was the case with the initial approach you've tried). It is a vector image consisting of a series of small rectangles. You can check this for yourself by taking a look at the barcode_placement.pdf file.

Please don't be confused because I use an Image object. If you look at the createBarcode() method, you can see that the Image is, in fact, a vector image:

public Image createBarcode(PdfContentByte cb, String text,
    float mh, float mw) throws BadElementException {
    BarcodePDF417 pf = new BarcodePDF417();
    pf.setText("BarcodePDF417 barcode");
    Rectangle size = pf.getBarcodeSize();
    PdfTemplate template = cb.createTemplate(
        mw * size.getWidth(), mh * size.getHeight());
    pf.placeBarcode(template, BaseColor.BLACK, mh, mw);
    return Image.getInstance(template);
}

The height and the width passed to the placeBarcode() method, define the height and the width of the small rectangles that are drawn. If you look at the inside view, you can see for instance:

0 21 3 1 re

This is a rectangle with x = 0, y = 21, width 3 and height 1.

When you ask the barcode for its size, you get the number of rectangles that will be drawn. Hence the dimensions of the barcode is:

Rectangle size = pf.getBarcodeSize();
float width = mw * size.getWidth();
float height = mh * size.getHeight();

Your assumption that size is a size in user units is only correct if mw and mh are equal to 1.

I use these values to create a PdfTemplate instance and I draw the barcode to this Form XObject. Most of the times, it's easier to work with the Image class than working with PdfTemplate, so I wrap the PdfTemplate inside an Image.

I can then add this Image to the document just like any other image. The main difference with ordinary images, is that this image is a vector image.