はじめに
JavaでQRコードを作成するライブラリと言えば、Google製のZXingがよく導入されている印象ですが、
実際使ってみるとソースコードが割りと低レイヤな感じになるところがちょっと気になるなと思ってました。
ざっくり書くとこんな感じです。
BitMatrix matrix = new QRCodeWriter().encode(
"https://qiita.com/nimzo6689", BarcodeFormat.QR_CODE, 320, 320
);
BufferedImage qrCodeImage = MatrixToImageWriter.toBufferedImage(matrix);
ImageIO.write(qrCodeImage, "png", Files.newOutputStream(Paths.get("qrcode.png")));
ただ、実際はもっと複雑になるケースがほとんどで、
例えば、余白を調整したり、文字コードを変えたり、白黒ではなく別の色に変更したり、などなど。
あと、伸ばしても画質が下がらないようにSVGファイルとして出力したいケースもあると思います。
上記のことを実現しようとすると、余計レイヤーの低いコードが増えるわけですが、
ZXingをラップして作成されたQRGenというライブラリを使えば、
メソッドチェーンで簡単にQR画像を生成する処理を書くことができるということを最近知ったので、
ちょっと使い方についてまとめてみました。
インストール方法
基本的にはMaven、Gradleなどのパッケージ管理ツールにいつものように
dependencyを追加することで利用可能となります。
ただ、一点注意すべきポイントがありまして、バージョン2.1.0以降は、
Maven Centralにはリリースをしていないため、
jitpack.ioをリポジトリに追加しておく必要があるとのことです。
公式ページに載っている通りですが、Gradleだと以下のようになります。
apply plugin: 'java'
sourceCompatibility = 1.12
repositories {
maven {
url "https://jitpack.io"
}
}
dependencies {
// JavaSEで利用する場合。AndroidだとartifactIdがandroidになります。
compile 'com.github.kenglxn.QRGen:javase:2.6.0'
}
ZXingだとcoreとjavase、または、androidと依存性を2つ書く必要がありますが、
QRGenだと一つで済むので少し楽できます。
使用例
ローカルで試したソースコードを以降載せていきます。
なお、QRGenは名前の通り、QRコードを生成する専用のライブラリのため、
スキャンしたい場合はZXingのAPIを利用する必要があるようです。
なお、本記事で載せているコードは以下の環境で検証しました。
Windows10 Pro, Java12(AdoptOpenJDK with HotSpot), IntelliJ IDEA 2019
※マイナーバージョン以降は 2019/08/07 付けで最新版のもの。
記事内のサンプルコードはimport文等を省略しているため、ファイル全体を確認したい場合はGistをご参照ください。
また、生成したQR画像はこちらのAndroidアプリで読み取れることを確認しました。
Fileオブジェクトを生成して出力
まずは、Fileオブジェクトで作成する方法です。
QRCode#file
でtempディレクトリにQRCode.png
という画像ファイルが作成されるので、
それを本来出力したいパスへ移動しています。
同じファイル名で何回でも書き込めるようにするためにREPLACE_EXISTING
を指定しました。
File qrCode = QRCode.from("https://qiita.com/nimzo6689").file();
Files.copy(qrCode.toPath(), Paths.get("qrcode.png"), StandardCopyOption.REPLACE_EXISTING);
冒頭で書いたサンプルコードと同じ内容のものですが、かなりシンプルになり、
ZXingのAPIを把握する必要もないので、実装のしやすさや可読性が高まりますね。
実際に生成できたQR画像はこちらです。
サイズは何も指定しなければ、125x125になるようです。
ByteArrayOutputStreamオブジェクトを生成して出力
続いて、ByteArrayOutputStreamオブジェクトを作成する方法です。
try (ByteArrayOutputStream qrCode = QRCode.from("https://qiita.com/nimzo6689").stream();
OutputStream outputStream = Files.newOutputStream(Paths.get("qrcode.png"))) {
qrCode.writeTo(outputStream);
qrCode.flush();
}
QRCode#file
と比べてコード量が多くなってしまいますが、
例えば、ImageIO.read(new ByteArrayInputStream(qrCode.toByteArray()))
などで
BufferedImage
を生成して何かしらの画像処理を行う場合はこちらが便利そうです。
あと、tempファイルを作成することもないので、パフォーマンスもこちらの方がよさそうではあります。
生成済みのOutputStreamオブジェクトに対して出力
QRCode#stream
では新しいOutputStreamを生成していましたが、
既に生成済みのOutputStreamにQR画像を書き込みたい場合はQRCode#writeTo
が使えます。
try (OutputStream os = Files.newOutputStream(Paths.get("qrcode.png"))) {
QRCode.from("https://qiita.com/nimzo6689").writeTo(os);
os.flush();
}
QRCode#stream
よりも記述量は少なくて済みます。
いろいろ設定値を指定して出力
生成するQR画像の画像形式やサイズ、色を指定できます。
また、ZXingでEncodeHintType
で指定していた設定内容もQRCodeオブジェクト経由で設定可能です。
File qrCode = QRCode.from("https://qiita.com/nimzo6689")
.to(ImageType.GIF)
.withSize(320, 320)
.withColor(0xFF6876B4, 0xFFF7F4F7)
.withCharset("UTF-8")
.withErrorCorrection(ErrorCorrectionLevel.L)
.withHint(EncodeHintType.MARGIN, 2)
.file();
Files.copy(qrCode.toPath(), Paths.get("qrcode.gif"), StandardCopyOption.REPLACE_EXISTING);
withColor
で指定している値は透明度(2 bytes)+ RGB(6 bytes)の形式となります。
生成したQR画像はこちらです。
svgファイルとして出力
SVG変換はJavaSEのみ利用可能です。
Androidは利用不可ですが、代わりにファイル生成なしでImageViewにQR画像を表示できる
QRCode#bitmap
が利用可能だそうです。(今回は未検証)
File qrCode = QRCode.from("https://qiita.com/nimzo6689").svg();
Files.copy(qrCode.toPath(), Paths.get("qrcode.svg"), StandardCopyOption.REPLACE_EXISTING);
生成したqrcode.svg
はChrome等で開くと適切にレンダリングしてくれます。
QR画像の画質に拘る場合や大きな紙で引き伸ばして印刷したい場合に
サイズの大きいQR画像を準備する必要がなくなるので助かりますね。
いろいろなスキーマを試す
QR画像はURLのほかにメアドや位置情報、Wifiなどに使われていますが、
そういったデータ形式ごとの情報を保持するSchemaオブジェクトもいくつか提供されています。
README
では全部で16個のスキーマがサポートされているようですが、
ソースコード見ると、実際はもう少し多いようです。
KddiAu
とかもあるんですね。意識高すぎ!
Urlスキーマ
WebページのURLを保持するスキーマです。
http、または、httpsから始まる文字列が格納できます。
あえて、このオブジェクト経由でQRCode::from
に食わせる必要はない気がします。
Url url = Url.parse("https://qiita.com/nimzo6689");
File qrCode = QRCode.from(url).file();
Files.copy(qrCode.toPath(), Paths.get("qrcode.png"), StandardCopyOption.REPLACE_EXISTING);
Emailスキーマ
メールアドレスを保持するスキーマです。
mailto:メールアドレス
の書式の文字列を格納できます。
一応、new EMail("mailto:taro.sato@example.org")
という書き方もできますが、
これだとmailto:
から始まる文字列かどうかといった検証処理が行われないので、
基本的にはEMail::parse
を使うのが適切だと思います。
EMail email = EMail.parse("mailto:taro.sato@example.com");
File qrCode = QRCode.from(email).file();
Files.copy(qrCode.toPath(), Paths.get("qrcode.png"), StandardCopyOption.REPLACE_EXISTING);
読み取って起動するアプリにGmailを選択すると、ToにQR画像で埋め込んだメールアドレスが入力されていました。
GeoInfoスキーマ
位置情報を保持するスキーマです。
geo:latitude,longitude
の書式の文字列を格納できます。
QR画像はだいたいWebサイト配布ではなく、店頭などからWeb情報にアクセスしたいときに使われるので、
いまいち位置情報のQR画像を使用する場面が想像しにくいです。
使いどころが気になります。
GeoInfo geoInfo = GeoInfo.parse("geo:35.6761775,139.7173954");
File qrCode = QRCode.from(geoInfo).file();
Files.copy(qrCode.toPath(), Paths.get("qrcode.png"), StandardCopyOption.REPLACE_EXISTING);
検証で使用したQRコードリーダーでは、「地図を表示」と「経路を検索」のメニューが表示され、
「地図を表示」だと該当の位置情報の周辺地図が表示され、
「経路を検索」にすると現在地から該当の位置までの経路がGoogle Mapで表示されました。
おわりに
ロゴ画像を中心にいい具合で配置することもできれば、なおよし。