Create QR-Code in vector image

You can use Apache Batik instead of JFreeSVG if you want a more liberate license. That makes especially sense when you use Apache FOP that includes Apache Batik as a transient dependency.

Here a tweaked version. Credits for the idea and the original code goes to David Gilbert.

import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.svg.SVGDocument;

import java.awt.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

public class QRCodesBatik {

    public static void main(String[] args) throws WriterException, IOException {
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.encode("https://xmlgraphics.apache.org/batik/",
                BarcodeFormat.QR_CODE, 800, 800);

        // Create a document with the appropriate namespace
        DOMImplementation domImpl = SVGDOMImplementation.getDOMImplementation();
        SVGDocument document = (SVGDocument) domImpl.createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, "svg", null);
        // Create an instance of the SVG Generator
        org.apache.batik.svggen.SVGGraphics2D g2 = new org.apache.batik.svggen.SVGGraphics2D(document);

        // draw onto the SVG Graphics object
        g2.setColor(Color.BLACK);

        for (int xIndex = 0; xIndex < bitMatrix.getWidth(); xIndex = xIndex + bitMatrix.getRowSize()) {
            for (int yIndex = 0; yIndex < bitMatrix.getWidth(); yIndex = yIndex + bitMatrix.getRowSize()) {
                if (bitMatrix.get(xIndex, yIndex)) {
                    g2.fillRect(xIndex, yIndex, bitMatrix.getRowSize(), bitMatrix.getRowSize());
                }
            }
        }

        try (Writer out = new OutputStreamWriter(new FileOutputStream(new File("qrtest.svg")), "UTF-8")) {
            g2.stream(out, true);
        }
    }
}

The easiest way I found was to create a PDF with iText and then convert the resulting PDF to EPS or SVG. Here is the code to create the PDF:

   @Test
   public void testQRtoPDF() throws WriterException, FileNotFoundException, DocumentException, UnsupportedEncodingException {
      final int s = 600;
      int r = 1;

      Charset charset = Charset.forName( "UTF-8" );
      CharsetEncoder encoder = charset.newEncoder();
      byte[] b = null;
      try {
         // Convert a string to UTF-8 bytes in a ByteBuffer
         ByteBuffer bbuf = encoder.encode( CharBuffer.wrap(
                     "1éöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùò1" +
                                 "2éöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùò2" +
                                 "3éöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùò3" +
                                 "4éöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùò4" +
                                 "5éöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùò5" +
                                 "6éöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùòïëéöàäèüùò6" ) );
         b = bbuf.array();
      } catch ( CharacterCodingException e ) {
         System.out.println( e.getMessage() );
      }

      String content = new String( b, "UTF-8" );
      QRCodeWriter qrCodeWriter = new QRCodeWriter();
      Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>( 2 );
      hints.put( EncodeHintType.CHARACTER_SET, "UTF-8" );
      BitMatrix qrCode = qrCodeWriter.encode( content, BarcodeFormat.QR_CODE, s, s, hints );

      Document doc = new Document( new Rectangle( s, s ) );
      PdfWriter pdfWriter = PdfWriter.getInstance( doc, new FileOutputStream( "qr-code.pdf" ) );
      doc.open();
      PdfContentByte contentByte = pdfWriter.getDirectContent();
      contentByte.setColorFill( BaseColor.BLACK );

      boolean d = false;
      for ( int x = 0; x < qrCode.getWidth(); x += r ) {
         for ( int y = 0; y < qrCode.getHeight(); y += r ) {
            if ( qrCode.get( x, y ) ) {
               contentByte.rectangle( x, s - y, r, r );
               contentByte.fill();
               contentByte.stroke();
            }
         }
      }

      doc.close();
   }

I then use image magic for the conversion. Like so:

convert qr-code.pdf qr-code.eps

the same can NOT be done for svg

convert qr-code.pdf qr-code.svg

this does not work

I tested this code with some long content and it worked with up to 600 characters. This is probably down to the precision of either camera on the phone or screen.

I hope this helps someone


You can even do it only with zxing withou additional libraries:

QRCodeWriter qrCodeWriter = new QRCodeWriter();
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());

BitMatrix bitMatrix = qrCodeWriter.encode(payload, BarcodeFormat.QR_CODE, 543, 543, hints);

StringBuilder sbPath = new StringBuilder();
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
int rowSize = bitMatrix.getRowSize();
BitArray row = new BitArray(width);
for(int y = 0; y < height; ++y) {
    row = bitMatrix.getRow(y, row);
    for(int x = 0; x < width; ++x) {
        if (row.get(x)) {
            sbPath.append(" M"+x+","+y+"h1v1h-1z");
        }
    }
}

StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
sb.append("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 ").append(width).append(" ").append(height).append("\" stroke=\"none\">\n");
sb.append("<style type=\"text/css\">\n");
sb.append(".black {fill:#000000;}\n");
sb.append("</style>\n");
sb.append("<path class=\"black\"  d=\"").append(sbPath.toString()).append("\"/>\n");
sb.append("</svg>\n");

Please note that the solution above is much more efficient in terms of memory consumption than the one using Batik's DOM.


Old question I know, but for anyone that comes along looking for how to do this...it's pretty easy to connect ZXing to JFreeSVG (http://www.jfree.org/jfreesvg), for example:

package org.jfree.demo;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import org.jfree.graphics2d.svg.SVGGraphics2D;
import org.jfree.graphics2d.svg.SVGUtils;

public class QRCodes {

    public static void main(String[] args) throws WriterException, IOException {
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.encode("http://www.jfree.org/jfreesvg", 
                BarcodeFormat.QR_CODE, 160, 160);
        int w = bitMatrix.getWidth();
        SVGGraphics2D g2 = new SVGGraphics2D(w, w);
        g2.setColor(Color.BLACK);
    for (int xIndex = 0; xIndex < w; xIndex = xIndex + bitMatrix.getRowSize()) {
        for (int yIndex = 0; yIndex < w; yIndex = yIndex + bitMatrix.getRowSize()) {
            if (bitMatrix.get(xIndex, yIndex)) {
                g2.fillRect(xIndex, yIndex, bitMatrix.getRowSize(), bitMatrix.getRowSize());

            }
        }
    }

        SVGUtils.writeToSVG(new File("qrtest.svg"), g2.getSVGElement());
    }

}