JavaでQRCodeを作成 anchor.png

Page Top

作成する方法 anchor.png

JavaでQRCodeを作成してみる。

QRコードを作成するためのJavaのライブラリに,ZXing(ゼブラ・クロッシング)というライブラリがある。
ZXingはGoogleで開発されていて,様々な一次元/二次元のバーコードの生成/読み取りが出来るオープンソースライブラリ。Apache License 2.0で無償で利用出来ます。

サポートされているフォーマット形式は,

1D product1D industrial2D
UPC-ACode 39QR Code
UPC-ECode 93Data Matrix
EAN-8Code 128Aztec (beta)
EAN-13CodabarPDF 417 (beta)
ITFMaxiCode
RSS-14
RSS-Expanded

です。残念ながら,MicroQRコードはサポートしていないようです。

このライブラリを使用して,QRコードを作成してみる。

ZXing Core(ZXingの中核となるライブラリ)

groupId : com.google.zxing
artifactId : core
version : 3.3.2

ZXing Java SE Extensions(Java SEのプロジェクトで,バーコードを操作する場合のライブラリ)

groupId : com.google.zxing
artifactId : javase
version : 3.3.2

Java SEで利用する場合はcoreとjavaseを,Androidの開発で利用する場合はcoreとandroidのコンポネントを利用する。

GitHub https://github.com/zxing/zxing からダウンロードする。
もしくは,jarファイルをMaven Repositoryからだと取得しやすい。
https://mvnrepository.com/artifact/com.google.zxing/core/3.3.2
https://mvnrepository.com/artifact/com.google.zxing/javase/3.3.2

ダウンロードした,以下の2つのjarファイルを使用する。

core-3.3.2.jar
javase-3.3.2.jar

参照できるクラスパスにコピーしておく。

ソースコードからビルドする場合は,Mavenでビルドする。

$ cd work
$ git clone https://github.com/zxing/zxing
$ cd zxing
$ mvn -Dhttps.protocols=TLSv1.2 package

java 7の場合は,TSL 1.2を指定してビルドする。使用するのは,coreとjavaseに作成されたjarファイル。

Page Top

QRCodeの画像をエンコードする anchor.png

QRCodeの画像を作成する方法は,以下のようなプログラムを作成。

QRCodeEncoder.java

Everything is expanded.Everything is shortened.
 
 
 
 
 
 
 
 
 
 
 
 
 
-
|
|
!
-
|
-
-
!
-
!
-
!
-
!
|
-
!
-
!
-
!
-
!
|
|
|
|
|
-
!
!
!
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import javax.imageio.ImageIO;
 
/**
 * JavaでQR Codeを作成
 * @author Yuji Ueno
 */
public class QRCodeEncoder {
 
    public static void main(String[] args) throws WriterException, IOException {
        // QRコード生成したい文字列
        String source = "上野雄二";
        // QRコード生成時のエンコーディング
        String encoding = "Shift_JIS";
        // サイズ(ピクセル)
        int size = 100;
        // 画像ファイルの保存先
        String filePath = "qrcode.png";
        
        // 生成処理
        ConcurrentHashMap hints = new ConcurrentHashMap();
        //エラー訂正レベル指定
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
        //エンコーディング指定
        hints.put(EncodeHintType.CHARACTER_SET, encoding);
        //マージン指定
        hints.put(EncodeHintType.MARGIN, 0);
        
        QRCodeWriter writer = new QRCodeWriter();
        BitMatrix bitMatrix = writer.encode(source, BarcodeFormat.QR_CODE, size, size, hints);
        BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix);
        
        //ファイルへの保存処理
        ImageIO.write(image, "png", new File(filePath));
    }
}

コンパイルして,動作確認してみる。

$ javac -encoding UTF-8 QRCodeEncoder.java
$ java QRCodeEncoder

qrcode.pngのファイル名で画像が作成されている。

qrcode.png

画像の色を変更したい場合には,MatrixToImageWriter.toBufferedImage()の第2引数に,MatrixToImageConfigを指定することで可能。

// 青と白で表示
MatrixToImageConfig config = new MatrixToImageConfig(0xff0000ff, 0xffffffff);
BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(matrix, config);

画像ではなく,文字列でもよければBitMatrix.toString()が使える。

Page Top

ヒント情報(EncodeHintType) anchor.png

QRCodeを作る際に,いくつかヒント情報を指定することが出来る。

ヒント情報は,Mapで指定する。
キーは,列挙型のEncodeHintTypeを使用するが,キーによって値の型が異なるためMap<EncodeHintType, Object>で定義する。

また,キーが列挙型なのでEnumMapが使用出来る。

Page Top

誤り訂正レベル anchor.png

誤り訂正レベルは,以下のように指定する。

キー : EncodeHintType.ERROR_CORRECTION
値 : 列挙型の ErrorCorrectionLevel で指定する
     ErrorCorrectionLevel.L : 7%まで訂正可能(デフォルト)
     ErrorCorrectionLevel.M : 15%まで訂正可能
     ErrorCorrectionLevel.Q : 25%まで訂正可能
     ErrorCorrectionLevel.H : 30%まで訂正可能

ちなみに,QRCodeにイラストなどを載せても読み取れるのは,この誤り訂正能力のおかげ。

Page Top

文字セット anchor.png

文字セットは,以下のように指定します。

キー : EncodeHintType.CHARACTER_SET
値 : 文字列( String )で文字セット名を指定
    デフォルトは "ISO-8859-1"

デフォルトは,上記のように"ISO-8859-1"(いわゆる ISO-Latin-1)なので,日本語が使えない。
必要であれば日本語が扱える文字セットを指定する必要がある。

後述のように,"Shift_JIS"の文字セット名は特別な対応があります(詳細は「モード」を参照)。

Page Top

マージン anchor.png

マージンは,以下のように指定する。

キー : EncodeHintType.MARGIN
値 : 上下左右のマージンをセル数( Integer )で指定
     デフォルトは 4

QRCodeには,周囲に4セル以上のマージンを必要とする。 普通は,デフォルトのままで十分だと思う。 もし,表示する際に画像の周りに余白を別途用意するなら,マージンには0を指定しても大丈夫。

Page Top

モードやバージョンを取得する anchor.png

QRCodeには,「モード」や「バージョン」といった情報がありますが,基本的に自動的に判定される。 上記の方法では,実際に適用された「モード」や「バージョン」は取得出来ない。

もしなんらかの理由で,「モード」や「バージョン」が必要な場合(例えば,「バージョン」によって画像のサイズを 変えるなど)は,Encoderを使用する。

Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 
 
 
 
 
 
 
 
 
 
 
 
 
-
|
|
-
|
|
|
-
-
!
!
|
|
|
|
|
|
!
!
package net.the_blue_pla.net.qrcode;
 
import java.nio.charset.Charset;
import java.util.EnumMap;
import java.util.Map;
import java.util.Objects;
 
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.encoder.Encoder;
import com.google.zxing.qrcode.encoder.QRCode;
 
public class QRCodeUtils {
 
    public static QRCode createQRCode(String contents, ErrorCorrectionLevel level, Charset charset)
            throws WriterException {
        Objects.requireNonNull(contents);
        Objects.requireNonNull(level);
        Objects.requireNonNull(charset);
        if (!charset.canEncode()) {
            // エンコード不可
            throw new IllegalArgumentException("cannot encode: " + charset.displayName());
        }
        
        Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
        hints.put(EncodeHintType.CHARACTER_SET, charset.displayName());
        
        QRCode code = Encoder.encode(contents, level, hints);
        return code;
    }
}

注意:MatrixToImageWriter を使っていないので,「ZXing Java SE Extensions」は不要。

com.google.zxing.qrcode.encoder.QRCode

QRCodeは,以下の情報を持っている。

QRCode#getVersion() - バージョン
QRCode#getECLevel() - 誤り訂正レベル
QRCode#getMode() - モード
QRCode#getMatrix() - QRコードの白黒情報

QRCodeから画像データを直接作るメソッドなどは用意されていないが,QRCode.getMatrix() から必要な情報は取得可能。

QRCodeから独自に画像を作るなら,QRCodeWriterとMatrixToImageWriterが参考になる。(面倒なら,かつ性能的に問題なければ QRCodeWriter を使えばいいのだが)

Page Top

バージョン anchor.png

バージョンとは,QRCodeのセル数の大きさを表す。
一番小さい「バージョン1」は,21セルx21セル。
バージョンが1つ増える毎に,縦横ともに4セルずつ増えていく。
最大の「バージョン40」は,177セルx177セルになる。

バージョン縦横のセル数
バージョン121セル×21セル
バージョン225セル×25セル
バージョンn(17+4*n)セル×(17+4*n)セル
バージョン40177セル×177セル
Page Top

モード anchor.png

QRCodeモード( com.google.zxing.qrcode.decoder.Mode )には,以下のものがある。

モード許容される文字
数字(NUMERIC)数字のみ (0~9)
英数字(ALPHANUMERIC)数字,英大文字と一部の記号 (0~9, A~Z, スペース, '$', '%', '*', '+', '-', '.', '/', ':')
漢字(KANJI)Shift_JISの2バイトコードのみ
バイト(BYTE)8bitのデータすべて

他にも「混在モード」があるが,どうもZXing 3.3.2ではサポートされていないみたい。

モードについては,ちょっと変わった判定方法になっている。

指定した文字セットが,"Shift_JIS"かそうでないかで判定方法が異なる。

Page Top

文字セットが,"Shift_JIS"以外の場合の判定 anchor.png

文字セットが"Shift_JIS"でない場合には,以下のようになっている。

対象のデータが数字のみの場合 ⇒ 数字モード
対象のデータが英数字と一部の記号のみの場合 ⇒ 英数字モード
それ以外のデータが含まれる ⇒ バイトモード

英数字モードでの英字は英大文字のみ。 英小文字があるとバイトモードになってしまう。

Page Top

文字セットが"Shift_JIS"の場合の判定 anchor.png

文字セットが"Shift_JIS"の場合には,以下のようになっている。

対象のデータが"Shift_JIS"の2バイト文字のみの場合 ⇒ 漢字モード
それ以外のデータが含まれる ⇒ バイトモード

つまり,文字セットとして"Shift_JIS"を指定した場合には,対象データが数字だけであってもバイトモードになるようだ。

また,この判定は"Shift_JIS"を指定した場合のみの動作になっている。

例えば,別名の"SJIS"や,"shift_jis"のように小文字で指定した場合は,上記「文字コードが"Shift_JIS"以外の場合の判定」が使われる。

当然だが,"MS932" や "Windows-31J" についても,上記「文字コードが"Shift_JIS"以外の場合の判定」が使われる。 そのため漢字モードでは,「NEC特殊文字」「NEC選定IBM拡張文字」「IBM拡張文字」「ユーザ外字」などは使えない。

ちなみに,このあたりの判定は com.google.zxing.qrcode.encoder.Encoder で行っています。

Encoder.java

Everything is expanded.Everything is shortened.
-
-
-
-
!
!
-
// (略)
private static Mode chooseMode(String content, String encoding) {
    if ("Shift_JIS".equals(encoding)) {
        // Choose Kanji mode if all input are double-byte characters
        return isOnlyDoubleByteKanji(content) ? Mode.KANJI : Mode.BYTE;
    }
// (略)

つまり,文字セットとして"Shift_JIS"を指定した場合には,対象データが数字だけであってもバイトモードになるようだ。
うーむ,この動作はおかしい気がする・・・
3.3.2では,"Shift_JIS"でもNUMERIC/ALPHANUMERICに判定されるように修正されているっぽい。

Page Top

QRCodeの画像を読み取る anchor.png

ZXingは,読み取りにも対応している。

QRCodeの画像を読み取るために,以下のようなプログラムを作成。
QRCodeReader.java

Everything is expanded.Everything is shortened.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
-
|
|
!
-
|
-
|
-
!
|
-
!
|
|
|
|
|
-
!
-
!
!
!
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
 
/**
 *
 * @author Yuji Ueno
 */
public class QRCodeReader {
 
    public static void main(String[] args) throws IOException, NotFoundException, ChecksumException, FormatException {
        
        //読み取りたい画像ファイルの保存場所
        String filePath = "qr_code.png";
        
        //読み取り処理
        BufferedImage image = ImageIO.read(new File(filePath));
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        Reader reader = new MultiFormatReader();
        Result decodeResult = reader.decode(bitmap);
        
        //デコード処理
        String result = decodeResult.getText();
        //標準出力
        System.out.format("読み取り結果=%1$s\r\n", result);
    }
}

コンパイルして,動作確認してみる。

> javac -encoding UTF-8 QRCodeReader.java
> java QRCodeReader
読み取り結果=上野雄二

となって,作成したときの文字列が読めた。


新しくコメントをつける

題名
ゲスト名
投稿本文
より詳細なコメント入力フォームへ

トップ   凍結 差分 バックアップ 複製 名前変更 リロード   ページ新規作成 全ページ一覧 単語検索 最新ページの一覧   ヘルプ   最新ページのRSS 1.0 最新ページのRSS 2.0 最新ページのRSS Atom
Counter: 17, today: 1, yesterday: 0
最終更新: 2024-04-09 (火) 13:49:53 (JST) (24d) by yuji