Categories
Development Java

QR Codes in Java

Für ein Projekt soll ich einen QR-Code generieren, der eine URL beinhaltet, die den Zugriff auf eine bestimmte Ressource ermöglicht.

In meinem letzten Post habe ich dokumentiert, wie die URL-Parameter verschlüsselt werden können.

Um mich mich dem Thema der QR Code Generierung zu öffnen habe ich insbesondere diese beiden Artikel verwendet: Generating Barcodes and QR Codes in Java auf Baeldung und Generating QR Code in Java auf Javapoint.

Die Wahl der Library fiel auf ZXing (“zebra crossing”), denn das ist die main library that supports QR codes in Java. und ich habe keine Anhaltspunkte finden können, warum ich eine andere Library nehmen sollte.

ZXing

Here, we need to add two Maven dependencies: the core image library and the Java client:


  
  
  
    com.google.zxing
    core
    3.5.1
  
  
  
    com.google.zxing
    javase
    3.5.1
  

QR Code generieren:

package deringo;

import java.awt.Desktop;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.NotFoundException;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

public class TestMain {

    public static void main(String args[]) throws WriterException, IOException, NotFoundException {
        String data = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam";
        Path path = Paths.get("./test.png");
        // Encoding charset to be used
        String charset = "UTF-8";
        Map hashMap = new HashMap();
        // generates QR code with Low level(L) error correction capability
        hashMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
        // invoking the user-defined method that creates the QR code
        generateQRcode(data, path, charset, hashMap, 200, 200);// increase or decrease height and width accodingly

        System.out.println("QR Code created successfully.");
        Desktop.getDesktop().open(path.toFile());
    }

    public static void generateQRcode(String data, Path path, String charset, Map map, int h, int w)
            throws WriterException, IOException {
        BitMatrix matrix = new MultiFormatWriter().encode(new String(data.getBytes(charset), charset),
                BarcodeFormat.QR_CODE, w, h);
        MatrixToImageWriter.writeToPath(matrix, getExtension(path), path);
    }

    /**
     * Own function until we have this in JDK
* Finally, there is a new method Path#getExtension available right in the JDK as of Java 21:
* https://stackoverflow.com/questions/3571223/how-do-i-get-the-file-extension-of-a-file-in-java/74315488#74315488 */ public static String getExtension(Path path) { String extension = path.getFileName().toString().substring(path.getFileName().toString().lastIndexOf('.') + 1); return extension; } }

ZXing und Docx4J

Der QR Code lässt sich schön als PNG generieren. Allerdings brauche ich den QR Code in einem Word Dokument. Das Word Dokument wird mit Docx4J generiert.

Den Einstieg in Docx4J habe ich bereits vollzogen. Besonders hilfreich ist Introduction To Docx4J auf Baeldung.

package deringo;

import java.awt.Desktop;
import java.io.File;
import java.nio.file.Files;

import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.Drawing;
import org.docx4j.wml.Jc;
import org.docx4j.wml.JcEnumeration;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.PPr;
import org.docx4j.wml.R;

public class TestMain {

    public static void main(String[] args) throws Exception {
        WordprocessingMLPackage wordPackage = WordprocessingMLPackage.createPackage();
        MainDocumentPart mainDocumentPart = wordPackage.getMainDocumentPart();
        mainDocumentPart.addStyledParagraphOfText("Title", "Welcome to my QR Code");
        mainDocumentPart.addParagraphOfText("Welcome to my QR Code");

        File image = new File("test.png" );
        byte[] fileContent = Files.readAllBytes(image.toPath());
        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage
          .createImagePart(wordPackage, fileContent);
        Inline inline = imagePart.createImageInline(
          "QR Code Image (filename hint)", "Alt Text", 1, 2, false);
        P Imageparagraph = addImageToParagraph(inline);
        mainDocumentPart.getContent().add(Imageparagraph);

        File exportFile = new File("welcome.docx");
        wordPackage.save(exportFile);

        Desktop.getDesktop().open(exportFile);
    }

    private static P addImageToParagraph(Inline inline) {
        ObjectFactory factory = new ObjectFactory();
        P p = factory.createP();
        R r = factory.createR();
        p.getContent().add(r);
        Drawing drawing = factory.createDrawing();
        r.getContent().add(drawing);
        drawing.getAnchorOrInline().add(inline);
        // center image
        PPr paragraphProperties = factory.createPPr();
        Jc justification = factory.createJc();
        justification.setVal(JcEnumeration.CENTER);
        paragraphProperties.setJc(justification);
        p.setPPr(paragraphProperties);

        return p;
    }
}

Das Ergebnis sieht brauchbar aus:

Allerdings habe ich die zuvor gespeicherte Datei verwendet. Ich möchte das aber on the fly machen, also ohne, dass ich den QR Code erst speichere und dann in das Word Dokument übernehme.

package deringo;

import java.awt.Desktop;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;

import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.Drawing;
import org.docx4j.wml.Jc;
import org.docx4j.wml.JcEnumeration;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.PPr;
import org.docx4j.wml.R;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

public class TestMain {

    public static void main(String[] args) throws Exception {
        WordprocessingMLPackage wordPackage = WordprocessingMLPackage.createPackage();
        MainDocumentPart mainDocumentPart = wordPackage.getMainDocumentPart();
        mainDocumentPart.addStyledParagraphOfText("Title", "Welcome to my QR Code");
        mainDocumentPart.addParagraphOfText("Welcome to my QR Code");

        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage
                .createImagePart(wordPackage, getQRCode());

        Inline inline = imagePart.createImageInline(
          "QR Code Image (filename hint)", "Alt Text", 1, 2, false);
        P Imageparagraph = addImageToParagraph(inline);
        mainDocumentPart.getContent().add(Imageparagraph);

        File exportFile = new File("welcome.docx");
        wordPackage.save(exportFile);

        Desktop.getDesktop().open(exportFile);
    }

    private static P addImageToParagraph(Inline inline) {
        ObjectFactory factory = new ObjectFactory();
        P p = factory.createP();
        R r = factory.createR();
        p.getContent().add(r);
        Drawing drawing = factory.createDrawing();
        r.getContent().add(drawing);
        drawing.getAnchorOrInline().add(inline);
        // center image
        PPr paragraphProperties = factory.createPPr();
        Jc justification = factory.createJc();
        justification.setVal(JcEnumeration.CENTER);
        paragraphProperties.setJc(justification);
        p.setPPr(paragraphProperties);

        return p;
    }

    public static byte[] getQRCode() throws Exception {
        String data = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam";
        // Encoding charset to be used
        String charset = "UTF-8";
        Map hashMap = new HashMap();
        // generates QR code with Low level(L) error correction capability
        hashMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);

        BitMatrix matrix = new MultiFormatWriter().encode(new String(data.getBytes(charset), charset),
                BarcodeFormat.QR_CODE, 200, 200);
        BufferedImage bi = MatrixToImageWriter.toBufferedImage(matrix);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(bi, "png", baos);
        byte[] bytes = baos.toByteArray();
        return bytes;
    }
}