今回はNode.js用にOFSC(一般社団法人オープン・フードサービス・システム・コンソーシアム)で開発されたレシート記述言語 ReceiptLineをReactnative/Expoで使えるようにしてみた方法の紹介です。
発端は下記の記事で面白いなと感じた事で、その後色々調べてみると最近iPad等のタブレットを利用した各種POSレジソフトが普及しつつあるので、スマホやタブレット用のレシート印刷アプリのニーズが在りそうに思ったことと、結構POSレジソフトはプリンターの対応機種を限定しており、AppleのAirPrint対応機種も最近増えてますが古いプリンターは対応できない等の問題が有りそうなこと等です。
リンク:Qiita 簡単レシート印刷 receiptlineと20行のJavaScriptでレジプリンターをインスタントカメラにしてみた
この記事の主な内容はこんな感じです。
1.レシート記述言語receiptlineとは何?
2.node.js用のモジュールをReact-Nativeで使えるようにする方法を試した結果は?
3.今後レシートはどうなるか?
1.レシート記述言語receiptlineとは何?
下手な解説より下記のWebサイトを見て頂いた方が早いと思いますが、receiptlineはQuiitaと同じくMarkdown記法を使ってレシートの印刷内容を記述し、OFSCに参加している各プリンターメーカ製のプリンターに印刷するための言語(Nodeモジュール)で、QRコ―ド等を埋め込んだり仮想プリンターとしてSVGで出力したり出来ます。対応プリンターはEpson TM series、Seiko Instruments RP series、Star MC series、Citizen CT series、Fujitsu FP seriesと記載されてます。日本発でこういうオープンソースの取り組みがグローバルに発信されているのは何か嬉しいですね。
▶OFSCのReceiptLineページ(日本語)
▶OFSCのReceiptLineページ(英語)
▶githubのReceiptLineページ(英語)
2.node.js用のモジュールをReact-Nativeで使えるようにする方法を試した結果は?
本当はこれがこの記事の本題になりますが、receiptlineはNode.jsのnpmモジュールとして開発されており、言語は当然JavaScriptです。Node.js用のnpmモジュールをReactnativeでimportしようとすると、Node.jsのコアモジュール(デフォルトでNodeに含まれる)である'fs'モジュール等が使えないのでエラーになる場合が多く(ReactnativeはJavascriptですがNodeのパッケージ管理であるnpmが使えるだけなので当然ですが)、これまでは諦めていました。receiptlineもコアモジュールを使っておりそのままではエラーが出ますが、あまり特別な外部依存関係が無さそうだったので、幾つかの方法を試行錯誤してみました。
最初はreceiptlineのソース内のコアモジュールの呼び出し箇所を個別にReactnative/Expo用のモジュールに置換えたりしましたが(例えば'crypto'を'expo-crypto'に書き換える等)、延々と修正を繰り返すのが苦痛になり別の方法を探すことにしました。結論から先に言うと'node-libs-expo'という如何にもと言った感じのモジュールを見つけこれで何とか動きました。以下はその具体的な方法です。
・検証環境:Node :v16.14.2, React:"17.0.1", React Native:"0.64.3", Expo:"~44.0.0", receiptline: "^1.8.0", node-libs-expo: "^0.0.3"
・初期設定:まずはexpo-cliをグローバルインストールし、expo initでプロジェクトフォルダを作成します。次にnode-libs-expoをnpmでローカルインストールしたあとプロジェクトフォルダのルートに'metro.config.js'というファイルを新規作成し、下記のように記述し保存します。デフォルトに__dirnameを追加し、更にresolver.extraNodeModulesにnode-libs-expoを追加するイメージです。
const { getDefaultConfig } = require('@expo/metro-config');
const defaultConfig = getDefaultConfig(__dirname);
defaultConfig.resolver.extraNodeModules = require('node-libs-expo');
module.exports = defaultConfig;
node_modules内のnode-libs-expoフォルダ直下にindex.jsファイルがあり、ここで実際に使用するモジュールを読み替えてエクスポートしており、所謂NodeとReact-Native間のモジュール翻訳的な設定をしているようです。ここでrequireされているモジュールは基本的にはnode-libs-expo配下のpackage.jsonのdependenciesでインストールされますが、'expo-crypto'と'expo-file-system'はdependenciesに含まれていなかったので、念のためプロジェクトルート直下でexpo installでインストールしましたが、receiptline.jsの本体ファイル内では'fs'は使用されていないので本来は不要かもしれません。
また今回の検証はプリンター実機が無かったのでreceiptlineの仮想プリンター機能でSVG出力させるため、react-native-svgをexpo installでインストールします。
以上で初期設定は完了です。結果のpackage.jsonのdependenciesを参考に付けておきます。
"dependencies": {
"expo": "~44.0.0",
"expo-crypto": "^10.1.2",
"expo-file-system": "~13.1.4",
"node-libs-expo": "^0.0.3",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-native": "0.64.3",
"react-native-svg": "12.1.1",
"receiptline": "^1.8.0",
"stream": "^0.0.2",
"util": "^0.12.4"
},
最後の"stream"と"util"についてですが、前者はnode-libs-expo内では"readable-stream"が使われておりindex.jsでも同じモジュールがエクスポートされていますが、stream関連のエラーが出たため念のためインストールしたもので、後者はnode-libs-expo内に同じモジュール(バージョンは異なる)があるので重複しますが、これも試行錯誤の過程でインストールしたものです。この2つのモジュールは必須では無いかもしれないので、エラーが出た場合にインストールしてみると良いと思います。
今後色々試される場合の参考ですが、receiptlineには直接プリンターを制御するreceiptioとレシートのデザインがWeb上で可能なdesignersというモジュールがあります。こちらを利用する場合はファイルシステムを使ったりTCP通信やBLE等のシリアル通信をプリンターとの間で行ったりするので、更に手を加えないとReact-Nativeでは使えなさそうです。今後の課題ですね。ちなみにExpoにはreact-native-ble-plxやreact-native-pdfのような通常はejectしてbare-workflowにしないと使えないRNのモジュールをmanagedでも使える(但しExpo Goは使えない無いのでdev clientを使う)config pluginというやり方の具体的なサンプルも以下のリンクにあるので、参考までに。
またreceiptlineの動作確認用の最小限のコードを参考に付けておきますので、利用してみて下さい。ただしiPad/iPhoneでフォントエラーが出るのと段ずれがあるので単なる動作確認用です(汗;多分react-native-svgとreceiptlineの相性問題?⇒receiptlineのSVG出力はhtmlのタグで'svg'は小文字だが、react-native-svgはJSXなので'Svg'は最初が大文字とか?⇒Expo Goで動くのでconsole.log(svg)で動作確認可能!)。
import React from 'react';
import { View } from 'react-native';
import receiptline from 'receiptline';
import {SvgXml} from 'react-native-svg';
const text = `市ヶ谷駅前店
東京都千代田区九段1-Y-X
{border:line; width:18}
^領収証
{border:space; width:10,2,14}
ビール | 2| ¥1,300
千鳥コース | 2| ¥17,280
---------------------------------------
{width:10,18}
合 計 | ¥18,580
現 金 | ¥20,000
お釣り | ¥1,420
{code:20210207210001; option:48,hri}`;
const svg = receiptline.transform(text, { cpl: 52, encoding: 'shiftjis', spacing: true });
console.log(svg);
export default function Transform() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<SvgXml xml={svg} width="80%" height="80%" />
</View>
);
}
3.今後レシートはどうなるか?
レシート印刷に関する投稿記事を書いといて変かもしれませんが、電子レシートって普及しませんね、何故でしょうかね。年間950憶のレシートの紙代が殆どゴミ箱に吸収されてるのってなんか勿体ないですね。某王手の電子レシートアプリも完全にクローズドな感じだし、海外では台湾や韓国みたいに日本より電子化が進んでいる感じだし、技術的な話とは違うと思いますが何とかならんもんですかね。また機会があったら今度は電子レシートについて投稿したいと思います。