|
現: 2024-04-09 (火) 14:49:53 yuji |
| + | * JavaでQRCodeを作成 [#i1074d87] |
| | | |
| + | ** 作成する方法 [#rec9e0a2] |
| + | JavaでQRCodeを作成してみる。 |
| + | |
| + | QRコードを作成するためのJavaのライブラリに,ZXing(ゼブラ・クロッシング)というライブラリがある。~ |
| + | ZXingはGoogleで開発されていて,様々な一次元/二次元のバーコードの生成/読み取りが出来るオープンソースライブラリ。Apache License 2.0で無償で利用出来ます。 |
| + | |
| + | サポートされているフォーマット形式は, |
| + | |1D product |1D industrial |2D |h |
| + | |UPC-A |Code 39 |QR Code | |
| + | |UPC-E |Code 93 |Data Matrix | |
| + | |EAN-8 |Code 128 |Aztec (beta) | |
| + | |EAN-13 |Codabar |PDF 417 (beta)| |
| + | | |ITF |MaxiCode | |
| + | | |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ファイル。 |
| + | |
| + | ** QRCodeの画像をエンコードする [#l17e292a] |
| + | QRCodeの画像を作成する方法は,以下のようなプログラムを作成。 |
| + | |
| + | ''QRCodeEncoder.java'' |
| + | #code(java,nonumber){{ |
| + | 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のファイル名で画像が作成されている。 |
| + | #ref(qrcode.png) |
| + | |
| + | 画像の色を変更したい場合には,MatrixToImageWriter.toBufferedImage()の第2引数に,MatrixToImageConfigを指定することで可能。 |
| + | // 青と白で表示 |
| + | MatrixToImageConfig config = new MatrixToImageConfig(0xff0000ff, 0xffffffff); |
| + | BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(matrix, config); |
| + | |
| + | 画像ではなく,文字列でもよければBitMatrix.toString()が使える。 |
| + | |
| + | ** ヒント情報(EncodeHintType) [#hc73705d] |
| + | QRCodeを作る際に,いくつかヒント情報を指定することが出来る。 |
| + | |
| + | ヒント情報は,Mapで指定する。~ |
| + | キーは,列挙型のEncodeHintTypeを使用するが,キーによって値の型が異なるためMap<EncodeHintType, Object>で定義する。 |
| + | |
| + | また,キーが列挙型なのでEnumMapが使用出来る。 |
| + | |
| + | ** 誤り訂正レベル [#j7652ff7] |
| + | 誤り訂正レベルは,以下のように指定する。~ |
| + | キー : EncodeHintType.ERROR_CORRECTION |
| + | 値 : 列挙型の ErrorCorrectionLevel で指定する |
| + | ErrorCorrectionLevel.L : 7%まで訂正可能(デフォルト) |
| + | ErrorCorrectionLevel.M : 15%まで訂正可能 |
| + | ErrorCorrectionLevel.Q : 25%まで訂正可能 |
| + | ErrorCorrectionLevel.H : 30%まで訂正可能 |
| + | |
| + | ちなみに,QRCodeにイラストなどを載せても読み取れるのは,この誤り訂正能力のおかげ。 |
| + | |
| + | ** 文字セット [#wb0d775a] |
| + | 文字セットは,以下のように指定します。 |
| + | キー : EncodeHintType.CHARACTER_SET |
| + | 値 : 文字列( String )で文字セット名を指定 |
| + | デフォルトは "ISO-8859-1" |
| + | |
| + | デフォルトは,上記のように"ISO-8859-1"(いわゆる ISO-Latin-1)なので,日本語が使えない。~ |
| + | 必要であれば日本語が扱える文字セットを指定する必要がある。 |
| + | |
| + | 後述のように,"Shift_JIS"の文字セット名は特別な対応があります(詳細は「モード」を参照)。 |
| + | |
| + | ** マージン [#we109dc3] |
| + | マージンは,以下のように指定する。 |
| + | キー : EncodeHintType.MARGIN |
| + | 値 : 上下左右のマージンをセル数( Integer )で指定 |
| + | デフォルトは 4 |
| + | |
| + | QRCodeには,周囲に4セル以上のマージンを必要とする。 |
| + | 普通は,デフォルトのままで十分だと思う。 |
| + | もし,表示する際に画像の周りに余白を別途用意するなら,マージンには0を指定しても大丈夫。 |
| + | |
| + | ** モードやバージョンを取得する [#m2860f45] |
| + | QRCodeには,「モード」や「バージョン」といった情報がありますが,基本的に自動的に判定される。 |
| + | 上記の方法では,実際に適用された「モード」や「バージョン」は取得出来ない。 |
| + | |
| + | もしなんらかの理由で,「モード」や「バージョン」が必要な場合(例えば,「バージョン」によって画像のサイズを |
| + | 変えるなど)は,Encoderを使用する。 |
| + | #code(java){{ |
| + | 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 を使えばいいのだが) |
| + | |
| + | ** バージョン [#w2e91795] |
| + | バージョンとは,QRCodeのセル数の大きさを表す。~ |
| + | 一番小さい「バージョン1」は,21セルx21セル。~ |
| + | バージョンが1つ増える毎に,縦横ともに4セルずつ増えていく。~ |
| + | 最大の「バージョン40」は,177セルx177セルになる。~ |
| + | |バージョン |縦横のセル数 |h |
| + | |バージョン1 |21セル×21セル | |
| + | |バージョン2 |25セル×25セル | |
| + | |… |… | |
| + | |バージョンn |(17+4*n)セル×(17+4*n)セル| |
| + | |… |… | |
| + | |バージョン40|177セル×177セル | |
| + | |
| + | ** モード [#ob7ca99e] |
| + | QRCodeモード( com.google.zxing.qrcode.decoder.Mode )には,以下のものがある。 |
| + | |モード |許容される文字|h |
| + | |数字(NUMERIC) |数字のみ (0~9)| |
| + | |英数字(ALPHANUMERIC) |数字,英大文字と一部の記号 (0~9, A~Z, スペース, '$', '%', '*', '+', '-', '.', '/', ':')| |
| + | |漢字(KANJI) |Shift_JISの2バイトコードのみ| |
| + | |バイト(BYTE) |8bitのデータすべて| |
| + | |
| + | 他にも「混在モード」があるが,どうもZXing 3.3.2ではサポートされていないみたい。~ |
| + | |
| + | モードについては,ちょっと変わった判定方法になっている。 |
| + | |
| + | 指定した文字セットが,"Shift_JIS"かそうでないかで判定方法が異なる。 |
| + | |
| + | *** 文字セットが,"Shift_JIS"以外の場合の判定 [#m9d621ec] |
| + | 文字セットが"Shift_JIS"でない場合には,以下のようになっている。 |
| + | 対象のデータが数字のみの場合 ⇒ 数字モード |
| + | 対象のデータが英数字と一部の記号のみの場合 ⇒ 英数字モード |
| + | それ以外のデータが含まれる ⇒ バイトモード |
| + | |
| + | 英数字モードでの英字は英大文字のみ。 |
| + | 英小文字があるとバイトモードになってしまう。 |
| + | |
| + | *** 文字セットが"Shift_JIS"の場合の判定 [#n922eac0] |
| + | 文字セットが"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'' |
| + | #code(java,nonumber){{ |
| + | // (略) |
| + | 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に判定されるように修正されているっぽい。 |
| + | |
| + | * QRCodeの画像を読み取る [#lb703bf2] |
| + | ZXingは,読み取りにも対応している。 |
| + | |
| + | QRCodeの画像を読み取るために,以下のようなプログラムを作成。~ |
| + | ''QRCodeReader.java'' |
| + | #code(java,nonumber){{ |
| + | 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 |
| + | 読み取り結果=上野雄二 |
| + | となって,作成したときの文字列が読めた。 |