社内のシステムとして導入したkintoneを利用してペーパーレス化を行いたいときなどに、社内に蓄積された紙の書類をデータにするのは一苦労です。
そんなときは、OCRを使って書類の内容をテキストデータへ変換し、kintoneに取り込めると便利です。
今回の記事はJavaScriptライブラリのTesseract.jsを用いて、紙の書類の電子化を楽にするカスタマイズを紹介します。
今回のカスタマイズを適用することで、レコード作成画面とレコード編集画面からOCRで画像ファイルを解析し、読み取ったテキストを文字列(複数行)フィールドに転記できます。
また、解析した画像ファイルも、kintoneの添付ファイルフィールドに保存されます。
画像データを解析する流れ
固定リンクがコピーされました
- 「ファイルを選択」ボタンで、解析したい画像ファイルを読み込みます。
- 「解析する」ボタンを押すと画像解析が始まります。
- 解析された結果が表示されます。
- ← ボタンを押すことで内容をまるごと転記できます。
あるいは、マウスドラッグで必要な箇所のみ選択して転記できます。
解析に使ったファイルは、そのままkintoneに保存されます。
実際の操作の流れは、下記GIF画像を確認してください。
デモ環境で実際に動作を確認できます。
https://dev-demo.cybozu.com/k/340/
ログイン情報は
cybozu developer networkデモ環境
で確認してください。
「はじめから作成」でアプリを新規作成し、文字列(複数行)フィールド、ボタンや読み取ったテキストを配置するスペースフィールドと、添付ファイルフィールドを次のように設定しましょう。
フィールドの種類 |
フィールドコード |
備考 |
スペース |
recognize_space |
ファイルを読み取るInput要素や解析のためのButton要素などを設置します。 |
文字列(複数行) |
text |
kintoneにデータを保持するための文字列フィールドです。 |
添付ファイル |
file |
解析したファイルをkintoneに格納するためのファイルフィールドです。 |
本サンプルのプログラムです。JavaScriptファイルとして保存してください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
/*
* tessaract sample program
* Copyright (c) 2023 Cybozu
*
* Licensed under the MIT License
* https://opensource.org/license/mit/
*/
(() => {
'use strict';
let fileInput;
let fileKey;
kintone.events.on(['app.record.create.show', 'app.record.edit.show'], (event) => {
kintone.app.record.setFieldShown('file', false);
const copyButton = document.createElement('button');
copyButton.innerText = '←';
copyButton.onclick = () => {
event.record.text.value = resultText.innerText;
kintone.app.record.set(event);
};
fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'image/*';
const resultText = document.createElement('div');
const recognizeButton = document.createElement('button');
recognizeButton.innerText = '解析する';
recognizeButton.onclick = () => {
if (!fileInput.files.length > 0) {
alert('読み取りたい文字の画像ファイルを選択してください');
return;
}
const reader = new FileReader();
reader.onload = async () => {
const worker = await Tesseract.createWorker({
logger: m => {
console.log(m);
resultText.innerText = `${m.status} : ${Math.round(m.progress * 100)}%`;
},
});
await worker.loadLanguage('jpn');
await worker.initialize('jpn');
await worker.setParameters({
preserve_interword_spaces: '1',
});
const {data: {text}} = await worker.recognize(reader.result);
resultText.innerText = text;
await worker.terminate();
};
reader.readAsDataURL(fileInput.files[0]);
};
const recognizeSpace = kintone.app.record.getSpaceElement('recognize_space');
recognizeSpace.style.width = '600px';
recognizeSpace.appendChild(copyButton);
recognizeSpace.appendChild(fileInput);
recognizeSpace.appendChild(recognizeButton);
recognizeSpace.appendChild(resultText);
return event;
});
kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], async (event) => {
if (fileInput.files.length === 0) {
fileKey = undefined;
return event;
}
const formData = new FormData();
formData.append('__REQUEST_TOKEN__', kintone.getRequestToken());
formData.append('file', fileInput.files[0]);
const headers = {
'X-Requested-With': 'XMLHttpRequest',
};
const resp = await fetch('/k/v1/file.json', {
method: 'POST',
headers,
body: formData,
});
const respData = await resp.json();
fileKey = respData.fileKey;
return event;
});
kintone.events.on(['app.record.create.submit.success', 'app.record.edit.submit.success'], async (event) => {
if (fileKey == null) return event;
const recordId = event.recordId;
const params = {
app: kintone.app.getId(),
id: recordId,
record: {
file: {
value: [{fileKey: fileKey}]
}
}
};
await kintone.api(kintone.api.url('/k/v1/record.json', true), 'PUT', params);
return event;
});
})();
|
サンプルコードの解説
固定リンクがコピーされました
レコード追加画面を表示した後のイベント
、
レコード編集画面を表示した後のイベント
で、下記画像のように、スペースフィールド(recognize _space)の中に4つの要素を用意します。
ファイルを読み込む要素を作ります。画像ファイルだけを選択できるよう、MIMEタイプで絞り込んでいます。
また、今回は他のイベントでもこの変数を利用するため、レコード追加画面表示・レコード編集画面表示イベントの外のスコープに、変数の宣言をしています。
1
2
3
|
fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'image/*';
|
画像ファイルを読み込ませたあと、解析ボタンを押すことで画像解析をさせるためのものです。
onclickイベントに、解析をするためのコードを定義します。
tessaractのAPIそれぞれの詳細は
ドキュメント
を確認してください。(英語)
進捗や結果などをテキスト表示用の要素(resultText)にinnerTextで挿入しています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
const recognizeButton = document.createElement('button');
recognizeButton.innerText = '解析する';
recognizeButton.onclick = () => {
if (!fileInput.files.length > 0) {
alert('読み取りたい文字の画像ファイルを選択してください');
return;
}
const reader = new FileReader();
reader.onload = async () => {
const worker = await Tesseract.createWorker({
logger: m => {
console.log(m);
resultText.innerText = `${m.status} : ${Math.round(m.progress * 100)}%`;
},
});
await worker.loadLanguage('jpn');
await worker.initialize('jpn');
await worker.setParameters({
preserve_interword_spaces: '1',
});
const {data: {text}} = await worker.recognize(reader.result);
resultText.innerText = text;
await worker.terminate();
};
reader.readAsDataURL(fileInput.files[0]);
};
|
テキスト表示
画像解析状況の進捗、画像解析した結果を表示するためのものです。今回はDiv要素を利用します。
1
|
const resultText = document.createElement('div');
|
解析したテキストを文字列フィールドにコピーするためのものです。
onclickイベントで、kintone.app.record.set()
関数を用いて文字列フィールドに解析結果をコピーしています。
1
2
3
4
5
6
|
const copyButton = document.createElement('button');
copyButton.innerText = '←';
copyButton.onclick = () => {
event.record.text.value = resultText.innerText;
kintone.app.record.set(event);
};
|
上記用意したものを、次のようにスペースフィールドにそれぞれ追加します。
1
2
3
4
5
6
|
const recognizeSpace = kintone.app.record.getSpaceElement('recognize_space');
recognizeSpace.style.width = '600px';
recognizeSpace.appendChild(copyButton);
recognizeSpace.appendChild(fileInput);
recognizeSpace.appendChild(recognizeButton);
recognizeSpace.appendChild(resultText);
|
ファイルアップロード処理
固定リンクがコピーされました
レコード保存前のイベントで、解析に使われた画像ファイルのアップロードを行います。
ファイルアップロードの方法詳細については「
ファイルをアップロードする
」記事を確認してください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], async (event) => {
if (fileInput.files.length === 0) {
fileKey = undefined;
return event;
}
const formData = new FormData();
formData.append('__REQUEST_TOKEN__', kintone.getRequestToken());
formData.append('file', fileInput.files[0]);
const headers = {
'X-Requested-With': 'XMLHttpRequest',
};
const resp = await fetch('/k/v1/file.json', {
method: 'POST',
headers,
body: formData,
});
const respData = await resp.json();
fileKey = respData.fileKey;
return event;
});
|
アップロードしたファイルとレコードの関連付け
固定リンクがコピーされました
アップロードしたファイルとレコードの関連付けは、REST API経由でしか行えないため
レコード追加画面で保存に成功した後のイベント
で行います。
アップロードしたファイルとレコードの関連付けについての詳細は「
ファイルアップロードで必須となる3つの手順
」記事を確認してください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
kintone.events.on(['app.record.create.submit.success', 'app.record.edit.submit.success'], async (event) => {
if (fileKey == null) return event;
const recordId = event.recordId;
const params = {
app: kintone.app.getId(),
id: recordId,
record: {
file: {
value: [{fileKey: fileKey}]
}
}
};
await kintone.api(kintone.api.url('/k/v1/record.json', true), 'PUT', params);
return event;
});
|
ライブラリファイルの準備
固定リンクがコピーされました
今回はOCRのために、Tesseract.js v4.0.2を利用します。
CDNが次に公開されているため、下記JavaScriptファイルをリンクとして設定します。
https://unpkg.com/tesseract.js@4.0.2/dist/tesseract.min.js
さきほど用意したサンプルコードとOCRのためのライブラリをkintone環境に適用します。適用方法はヘルプ記事「
JavaScriptやCSSでアプリをカスタマイズする
」を参照してください。
サンプルコードを正常に動作させるために、ライブラリファイルを先に読み込む必要があります。
こちらからサンプルの画像ファイルをダウンロードし、カスタマイズしたアプリ上で読み込ませ、動作を確認してください。
今回の記事は、Tesseract.jsを利用して画像から既存の資産をkintoneに移しやすくするカスタマイズを紹介しました。
今回は画像のみでPDFには対応していませんが、PDFも一度画像に変換する処理などをすることによりPDFにも対応できます。興味のある方はぜひ確認してみてください。