2018年以降の記事はGitHub Pagesに移行しました

RubyでQRコードを生成してみる

あらすじ

この記事は Ruby Advent Calendar 2013 - Qiita [キータ] の 24 日目の記事です。

  • 23 日目: mrknさん
  • 24 日目: ここ
  • 25 日目: aerealさん

とりあえず、私用で URL から QR コードを生成したいので、 Ruby で実現できるか調べてみた。

成果物

とりあえず、以降の手順の QR コード作成方をもう少しゴチャゴチャやって Heroku にデプロイ。

課題
  • 特にエラーチェックとかしてない
  • 日本語がうまく読み取れない

今回使った rqrcode の Issue の中にこんなものを発見した。 UTF8 strings under Ruby 1.9.2 · Issue #3 · whomwah/rqrcode · GitHub

で、やってみると、確かに日本語を QR コードに落とす事ができた!

ただし、ここでさらにわからないことが増えた…。

  • UTF-8 文字列を安全に CP852 に変換する方法(今は force_encoding で無理やり変換している)
  • なんで CP852 なのか
  • そもそも CP852 とは

参考サイト

環境

手順

  1. barby という Ruby のバーコード生成ライブラリを使用する
  2. barby から QR コードを生成するために rqrcode が必要
  3. QR コードを画像で吐き出すために chunky_png が必要(画像なら何形式でもよかった)
  4. 吐き出した png をブラウザから確認するために sinatra を使用

ソースコード

という事で、必要なgem。

gem 'sinatra'
gem 'barby'
gem 'rqrcode'
gem 'chunky_png'

URL を QR コードに変換するには、

  • QR コードにしたい文字列を用意
  • QR コードとして読み込む
  • 変換したい形式の Outputter で読み込む(今回は png )
  • 読み込んだ バイナリデータ を書き出す

コードはこんな感じ。これは google の Url を QR に変換している。

# encoding: utf-8

require "barby"
require "barby/barcode/qr_code"
require "barby/outputter/ascii_outputter"
require "barby/outputter/png_outputter"
require "rqrcode"

module QR
  extend self

  def barcode(type, data=nil)
    if data.nil? then
      data = <<-"EOS"
        http://google.com
      EOS
    end

    code = Barby::QrCode.new(data.encode("UTF-8"))
    case type
    when :png
      blob = Barby::PngOutputter.new(code)
      File.open('./barcode.png', 'wb') do |f|
        png = blob.to_png

        f.write png
      end
    when :ascii
      code.to_ascii
    end
  end
end

if $0 == __FILE__ then
  puts QR::barcode(:ascii)
end

一番初めは ASCII で出力してみるといいかも。

出した結果。

$ ruby qr.rb
 X  X  X  X  X  X  X        X  X  X        X           X  X  X  X  X  X  X
 X                 X              X  X  X  X     X     X                 X
 X     X  X  X     X        X  X  X        X     X     X     X  X  X     X
 X     X  X  X     X     X     X  X     X     X  X     X     X  X  X     X
 X     X  X  X     X     X     X     X           X     X     X  X  X     X
 X                 X        X  X        X  X     X     X                 X
 X  X  X  X  X  X  X     X     X     X     X     X     X  X  X  X  X  X  X
                            X        X     X  X  X
 X  X           X  X  X     X  X     X  X  X  X              X  X
    X           X              X        X  X  X     X     X  X     X
 X  X  X  X  X     X  X  X  X     X  X     X  X  X  X  X        X        X
 X     X  X  X                 X  X           X  X     X     X           X
    X     X  X  X  X  X  X  X  X     X           X     X  X     X     X  X
 X  X  X     X                 X     X        X     X     X
 X                 X        X  X     X     X  X  X  X  X        X        X
 X              X     X  X  X  X  X  X        X        X     X     X     X
 X     X  X  X  X  X  X  X  X     X  X  X        X  X  X  X  X  X  X     X
                         X  X  X     X  X        X           X
 X  X  X  X  X  X  X     X        X              X     X     X  X  X  X  X
 X                 X     X     X                 X           X
 X     X  X  X     X              X  X  X  X  X  X  X  X  X  X  X  X
 X     X  X  X     X              X        X        X  X           X  X  X
 X     X  X  X     X        X  X        X     X  X  X           X        X
 X                 X     X     X  X        X  X  X     X                 X
 X  X  X  X  X  X  X     X  X  X     X        X  X  X  X  X  X  X        X

ここに sinatra ( start.rb )から呼ぶように。

equire 'sinatra'
require 'qr'

get '/' do
  code = QR::barcode(:png)
  '<img src="barcode.png">'
end

はまったとこ

バイナリファイルを書き込む時は b を忘れないようにする!

画像ファイルは生成されるんだけど、開いてみると「こわれています」と表示されてしまう……。

処理の途中でバイナリを確認していくと、こうなっていた。

正常な場合のバイナリデータ

ソース内で file オープンして write する直前のものを確認

89 50 4E 47 0D 0A 1A 0A ...

「こわれている」画像のバイナリデータ

実際に生成されたブツをバイナリエディタで確認

89 50 4E 47 0D 0D 0A 1A 0D 0A ...

ん?なんか違う。

シグネチャPNG ファイルであることを確認するために使用されます。 シグネチャは必ず以下の8バイトとなっています。

0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a

なるほど。シグネチャからしておかしい。そもそもはじめから PNG になっていないのか。

なんでだろう?

…と、よくよくコードを見直してみると、 File.open した時のオプションが w だった。

File.open('./barcode.png', 'w') do |f|

b オプションを付加すると正常な画像になった!

File.open('./barcode.png', 'wb') do |f|

バイナリで書き込むの初めてだから失念していた。注意しないといかん。