TeX Alchemist Online

TeX を使って化学のお仕事をしています。

連番のQRコードを大量生成する

f:id:doraTeX:20180727224927p:plain

顧客ID・請求コード・受験番号といった番号や記号をQRコードにエンコードして用紙に印刷しておき,相手ごとに個別化された用紙を配りたい」という状況はよく生じると思います。 それをLaTeXで実現する場合の一例を示します。

また,LaTeX初心者の人にとっては,QRコードは無関係であったとしても「Wordでいう差し込み印刷のようなことをLaTeXでどうやって実現するか」のサンプルにもなるでしょう。

なお,このサンプルに掲載の個人情報は,ダミー個人情報生成サイト なんちゃって個人情報 を活用してランダム生成しました。サンプルデータ作成にとても便利なサイトです。

方法1:qrcodeパッケージでその場で生成する

TeX Live には,qrcodeパッケージ が収録されています。これは,(ASCII文字限定ではありますが)引数に与えられた文字列を

\qrcode{https://ctan.org}

のようにしてQRコードに変換できる便利なパッケージです。これで生成されるQRコードは画像ではなく,なんと TeX の \rule によって描かれた黒正方形の集合体となっています。 (qrcode.sty のソースを見ると,これをよくTeXで実装したなぁと感心させられます……!)

なお,TeX Forum のQ&A にあったように,\lineskiplimit\normallineskiplimit を 0 に設定しておかないと表示が崩れる場合がありますので,\qrcode を使う際には常に

\setlength\lineskiplimit{0pt}
\setlength\normallineskiplimit{0pt}

としておくのが無難です。

以下では,

  • LaTeXで顧客IDをキーにした差し込み印刷を実現し,
  • その顧客IDをQRコードに変換したものを各ページに埋め込む

という方法のサンプル (upLaTeX + dvipdfmx) を示します。

v2.overleaf.com

方法2:事前にqrcodeパッケージでQRコードだけを束ねたPDFファイルを作成しておく

用紙の枚数が1000枚といった大量のページ数に及ぶ場合は,コンパイルのたびにQRコードへのエンコード処理を毎回行っていると負荷が大きいので,事前にQRコードだけを束ねたPDFを生成しておき,それを用紙の側から \includegraphics[page=...] でページ指定で呼び出すのがよいでしょう。

以下では,

  • カウンタを回しながら \qrcode でQRコードを順次生成し,
  • preview パッケージでQRコード部分だけを切り出す

という方法で連番のQRコードを大量生成して束ねたPDFを生成するサンプル (pdfLaTeX) を示します。 1ページ目は 0001,……,100ページ目は 0100 という文字列をエンコードしたQRコードとなっています。

v2.overleaf.com

方法3:事前にQRコードのPNG画像を大量生成しておく

世間的にはこれが最も普通の方法でしょう。事前にQRコードの画像を用意しておき,それを \includegraphics で読み込むという方法です。

大量のQRコードを一括生成するにはどうすればよいでしょうか。検索してみたところ,コマンドラインで使えるQRコード生成ツールがいろいろあるようです。シェルスクリプトでそのコマンドを for ループで回せば,大量のQRコードを一括生成できるでしょう。

ですが,今回は新規ツールをわざわざインストールするのが面倒でしたので,Swift で自分で書くことにしました。macOS の CoreImage API にはQRコード生成機能が用意されていますので,それを利用すれば簡単にQRコードを生成できます。この方法ならUTF8にも対応でき,日本語を含む任意の文字列をQRコードにエンコードできます。

import Cocoa
extension NSBitmapImageRep {
func representation(type: CFString, dpi: CGFloat) -> NSData {
let prop: [CFString:Any] = [kCGImagePropertyIPTCImageType: 1.0,
kCGImagePropertyDPIWidth: dpi,
kCGImagePropertyDPIHeight: dpi]
let outputData = NSMutableData()
let destination = CGImageDestinationCreateWithData(outputData, type, 1, nil)!
CGImageDestinationAddImage(destination, self.cgImage!, prop as CFDictionary)
CGImageDestinationFinalize(destination)
return outputData
}
}
extension NSImage {
class func qrCodeImage(from string: String, scale: CGFloat = 1) -> NSImage? {
let stringData = string.data(using: .utf8)
let transform = CGAffineTransform(scaleX: scale, y: scale)
guard let filter = CIFilter(name: "CIQRCodeGenerator") else {
return nil
}
filter.setValue(stringData, forKey: "inputMessage")
guard let ciImage = filter.outputImage?.transformed(by: transform) else {
return nil
}
let ciImageRep = NSCIImageRep(ciImage: ciImage)
let nsImage = NSImage(size: ciImageRep.size)
nsImage.addRepresentation(ciImageRep)
return nsImage
}
func pngData(dpi: CGFloat = 72) -> NSData? {
guard let tiffData = self.tiffRepresentation,
let bitmapImageRep = NSBitmapImageRep(data: tiffData) else {
return nil
}
return bitmapImageRep.representation(type: kUTTypePNG, dpi: dpi)
}
}
// QRコード連続並列生成
let scale: CGFloat = 1
let dpi: CGFloat = 72
let maxCount = 1000
// 並列処理で一括生成
DispatchQueue.concurrentPerform(iterations: maxCount) { num in
autoreleasepool {
let numberString = String(format: "%04d", num)
if let pngData = NSImage.qrCodeImage(from: numberString, scale: scale)?.pngData(dpi: dpi) {
pngData.write(toFile: "/tmp/\(numberString).png", atomically: true)
}
}
}
view raw generateQRcode.swift hosted with ❤ by GitHub

QR Code Generator written in Swift 4