🖨️

ブラウザからラベルプリンターへのバーコード出力

に公開

はじめに

レンティオ株式会社でエンジニアをしているKIMIです。

お役に立つかわかりませんが最近開発した、ブラウザからラベルプリンターへのバーコード出力に関する共有をしたいと思います。

これまでは複雑な工程を経て印刷していましたが、テプラ公式のテプラクリエイターWeb APIを活用し、システムからダイレクトに印刷できるようにしました。

なぜ?

レンティオでは、さまざまな商品をお試しいただけます。在庫数が膨大なうえ、「レンタル」という性質上、通常のECとは異なり商品は返却されてきます(Rentioにはそのまま購入™という、お試し後に気に入れば購入できる便利な制度がありますので、すべてが返却されるわけではない)。そのため、在庫を一つ一つ個品管理する必要があります。

しかし、製品の外観だけでは個品の識別が困難です。そのため、製品一つ一つに在庫IDのバーコードを貼付し、バーコードリーダーやスマートフォンでスキャンすることで、正確に在庫を識別しています。
(さらなる大規模化を見据えるとRFIDの導入も選択肢に入りますが、コスト面などの課題もありますし)

barcode

バーコードの表示とテプラでの印刷

バーコードを画像として生成し、それをテプラで印刷することを考えました。テプラには(ほかのラベルプリンターでも?)テキストデータを渡すとバーコードに変換する機能も備わっていますが、あえて画像として生成することで、ディスプレイ上での表示や紙への印刷など、デバイスを選ばず汎用的に利用できるというメリットがあります。

バーコードの表示は、Barcode Writer in Pure JavaScript を活用すれば簡単に生成できます。

import bwipjs from '@bwip-js/browser'

const barcodeCanvas = bwipjs.toCanvas('mycanvas', {
  bcid: 'code128',
  text: stockId,
  scale: window.devicePixelRatio,
  height: 18, // mmで指定、18だったとしてpx変換すると 18mm * 72dpi / 25.4mm ≒ 51.02px
  includetext: true, // textデータを一緒に表示してくれる
})

次にテプラで印刷します。

上に書いたテプラクリエイターWeb APIをダウンロードし、適当に配置します。

import { TepraPrint, TepraPrintError, TepraPrintTapeID } from 'tepra_creator_webapi_sdk/tepraprint'

テプラの情報が取得できるので、

const result = await TepraPrint.getPrinter()
if (result.errorCode === TepraPrintError.SUCCESS) {
  result.printers.forEach((printerName) => {
    // ネットワーク上にあるprinterが取得できる
  })
}

Canvas をFile に変換してテプラに送信

// Canvas => Blob => File
const imageBlob = await new Promise(resolve => barcodeCanvas.toBlob(resolve, 'image/png'))
const imageFile = new File([imageBlob], 'myImage.png', {
  type: imageBlob.type,
  lastModified: Date.now(),
})

const printerResult = await TepraPrint.createPrinter(printerName)
if (printerResult.errorCode === TepraPrintError.SUCCESS) {
  const printer = printerResult.printer
  const printParameter = await printer.createPrintParameter()
  if (printParameter.errorCode === TepraPrintError.SUCCESS) {
    const param = printParameter.printParameter
    param.stretchImage = true // 画像が収まるようにいい感じに合わせてくれる
    param.tape = TepraPrintTapeID._18MMTAPE // テープ幅

    const printFile = { imageFile }
    const result = await printer.doPrint(param, printFile)
    if (result.errorCode !== TepraPrintError.SUCCESS) {
      alert('印刷に失敗しました')
    }
  }
}

以上、非常に簡単です。

のはずだったのだが…

stretchImage オプションを使うとバーコードの描画精度やテキストの差し込み位置など、細かな要望を完璧に満たすことができませんでした。レイアウトの柔軟性を担保し、想定通りの出力を実現するため、最終的にはこのようなコードに落ち着いてます。

const scale = window.devicePixelRatio || 1

const barcodeCanvas = bwipjs.toCanvas(document.createElement('CANVAS'), {
  bcid: 'code128',
  text: stockId,
  scale,
  height: 18, // mmで指定、18だったとしてpx変換すると 18mm * 72dpi / 25.4mm ≒ 51.02px
  includetext: true, // textデータを一緒に表示してくれる
})

// さらに、人の確認用にSKUも表示する
const textCanvas = document.createElement('CANVAS')

const textCanvasContext = textCanvas.getContext('2d')
textCanvasContext.font = `18px Arial`

const metrics = textCanvasContext.measureText('ITEM_SKU')
textCanvas.width = metrics.actualBoundingBoxRight * scale
textCanvas.height = (metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent) * scale

textCanvasContext.scale(scale, scale)
textCanvasContext.font = `18px Arial`
textCanvasContext.textAlign = 'left'
textCanvasContext.textBaseline = 'top'
textCanvasContext.fillText('ITEM_SKU', 0, 0)

// バーコードとテキストを合成する
const myCanvas = document.getElementById('myCanvas')
myCanvas.width = barcodeCanvas.width * scale
myCanvas.height = (barcodeCanvas.height + textCanvas.height) * scale

const myCanvasContext = myCanvas.getContext('2d')
myCanvasContext.scale(scale, scale)
myCanvasContext.drawImage(barcodeCanvas, 0, 0)
myCanvasContext.drawImage(textCanvas, 0, barcodeCanvas.height)

採用情報

まだまだやれることはたくさんあります!物流のIoTに興味があるという方、ぜひ一度お話しませんか?

Discussion