Kevin Kwok
8 years ago
31 changed files with 13339 additions and 711 deletions
@ -1,26 +0,0 @@
@@ -1,26 +0,0 @@
|
||||
var path = require('path'); |
||||
var express = require('express'); |
||||
var webpack = require('webpack'); |
||||
var config = require('./webpack.config.dev'); |
||||
|
||||
var app = express(); |
||||
var compiler = webpack(config); |
||||
|
||||
app.use(require('webpack-dev-middleware')(compiler, { |
||||
noInfo: true, |
||||
publicPath: config[0].output.publicPath |
||||
})); |
||||
|
||||
// app.use(require('webpack-hot-middleware')(compiler));
|
||||
|
||||
app.use('/', express.static('./')); |
||||
|
||||
var port = 7355 |
||||
app.listen(port, 'localhost', function(err) { |
||||
if (err) { |
||||
console.log(err); |
||||
return; |
||||
} |
||||
|
||||
console.log('Listening at http://localhost:' + port); |
||||
}); |
@ -1 +1,251 @@
@@ -1 +1,251 @@
|
||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Tesseract=t():e.Tesseract=t()}(this,function(){return function(e){function t(n){if(r[n])return r[n].exports;var o=r[n]={exports:{},id:n,loaded:!1};return e[n].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t){"use strict";function r(e,t){return a||(a=o(g.coreUrl,g.workerUrl,g.langUrl)),a.recognize(e,t)}function n(e){return a||(a=o(g.coreUrl,g.workerUrl,g.langUrl)),a.detect(e)}function o(){function e(e,t){var r=s++;u[r]={};var n=0;return Object.getOwnPropertyNames(t).filter(function(e){return"function"==typeof t[e]}).forEach(function(o){n++,t[o](function(a){t[o]=a,0==--n&&i.postMessage({jobId:r,action:e,args:t})})}),0==n&&i.postMessage({jobId:r,action:e,args:t}),{then:function(e){return u[r].result=e,this},error:function(e){return u[r].error=e,this},progress:function(e){return u[r].progress=e,this}}}function t(e){if(e.match&&e.match(/^https?:\/\//))return function(r){var n=new Image;n.src=e,n.onload=function(){return r(t(n))}};if("string"==typeof e&&(e=document.querySelector(e)),e.getContext)e=e.getContext("2d");else if("IMG"==e.tagName||"VIDEO"==e.tagName){var r=document.createElement("canvas");r.width=e.naturalWidth||e.videoWidth,r.height=e.naturalHeight||e.videoHeight;var n=r.getContext("2d");n.drawImage(e,0,0),e=n}return e.getImageData&&(e=e.getImageData(0,0,e.canvas.width,e.canvas.height)),e}var r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:g.coreUrl,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:g.workerUrl,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:g.langUrl,a=new Blob(["importScripts('"+r+"');\n\t\t importScripts('"+n+"');"]),i=new Worker(window.URL.createObjectURL(a)),c=!1,s=0,u={};return i.onmessage=function(e){var t=e.data,r=t.jobId,n=t.progress,o=t.error,a=t.result,i=u[r];n&&i.progress&&i.progress(n),o&&i.error&&i.error(o),a&&i.result&&i.result(a)},e("init",{mem:100663296,langUrl:o}),{detect:function(r){return e("detect",{image:t(r)})},recognize:function(r){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"eng";return"string"==typeof n?n={lang:n}:n.lang=n.lang||"eng",c||["chi_sim","chi_tra","jpn"].indexOf(n.lang)==-1||(e("init",{mem:167772160,langUrl:o}),c=!0),e("recognize",{options:n,image:t(r)})}}}var a,i="https://cdn.rawgit.com/naptha/tesseract.js-core/master/index.js",c="https://cdn.rawgit.com/naptha/tesseract.js/8b915dc/dist/tesseract.worker.js",s="https://cdn.rawgit.com/naptha/tessdata/gh-pages/3.02/",g={coreUrl:i,workerUrl:c,langUrl:s,recognize:r,detect:n,createWorker:o};e.exports=g}])}); |
||||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Tesseract = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ |
||||
'use strict'; |
||||
|
||||
exports.defaultOptions = { |
||||
langPath: 'https://cdn.rawgit.com/naptha/tessdata/gh-pages/3.02/', |
||||
workerPath: 'dist/worker.js', |
||||
tesseractPath: 'https://cdn.rawgit.com/naptha/tesseract.js-core/0.1.0/index.js' |
||||
}; |
||||
|
||||
exports.spawnWorker = function spawnWorker(instance, workerOptions) { |
||||
var worker = new Worker(workerOptions.workerPath); |
||||
worker.onmessage = function (e) { |
||||
instance._recv(e.data); |
||||
}; |
||||
return worker; |
||||
}; |
||||
|
||||
exports.terminateWorker = function (instance) { |
||||
instance.worker.terminate(); |
||||
}; |
||||
|
||||
exports.sendPacket = function sendPacket(instance, packet) { |
||||
loadImage(packet.payload.image, function (img) { |
||||
packet.payload.image = img; |
||||
instance.worker.postMessage(packet); |
||||
}); |
||||
}; |
||||
|
||||
function loadImage(image, cb) { |
||||
if (typeof image === 'string') { |
||||
if (/^\#/.test(image)) { |
||||
// element css selector
|
||||
return loadImage(document.querySelector(image), cb); |
||||
} else { |
||||
// url or path
|
||||
var im = new Image(); |
||||
im.src = image; |
||||
im.onload = function (e) { |
||||
return loadImage(im, cb); |
||||
}; |
||||
return; |
||||
} |
||||
} else if (image instanceof File) { |
||||
// files
|
||||
var fr = new FileReader(); |
||||
fr.onload = function (e) { |
||||
return loadImage(fr.result, cb); |
||||
}; |
||||
fr.readAsDataURL(image); |
||||
return; |
||||
} else if (image instanceof Blob) { |
||||
return loadImage(URL.createObjectURL(image), cb); |
||||
} else if (image.getContext) { |
||||
// canvas element
|
||||
return loadImage(image.getContext('2d'), cb); |
||||
} else if (image.tagName == "IMG" || image.tagName == "VIDEO") { |
||||
// image element or video element
|
||||
var c = document.createElement('canvas'); |
||||
c.width = image.naturalWidth || image.videoWidth; |
||||
c.height = image.naturalHeight || image.videoHeight; |
||||
var ctx = c.getContext('2d'); |
||||
ctx.drawImage(image, 0, 0); |
||||
return loadImage(ctx, cb); |
||||
} else if (image.getImageData) { |
||||
// canvas context
|
||||
var data = image.getImageData(0, 0, image.canvas.width, image.canvas.height); |
||||
return loadImage(data, cb); |
||||
} |
||||
cb(image); |
||||
} |
||||
|
||||
},{}],2:[function(require,module,exports){ |
||||
"use strict"; |
||||
|
||||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); |
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } |
||||
|
||||
var adapter = require('./node/index.js'); |
||||
|
||||
function createWorker(workerOptions) { |
||||
return new TesseractWorker(workerOptions); |
||||
} |
||||
|
||||
var TesseractWorker = function () { |
||||
function TesseractWorker(workerOptions) { |
||||
_classCallCheck(this, TesseractWorker); |
||||
|
||||
this.worker = null; |
||||
this.workerOptions = workerOptions; |
||||
this._currentJob = null; |
||||
this._queue = []; |
||||
} |
||||
|
||||
_createClass(TesseractWorker, [{ |
||||
key: 'recognize', |
||||
value: function recognize(image, options) { |
||||
var _this = this; |
||||
|
||||
return this._delay(function (job) { |
||||
options = options || {}; |
||||
options.lang = options.lang || 'eng'; |
||||
job._send('recognize', { image: image, options: options, workerOptions: _this.workerOptions }); |
||||
}); |
||||
} |
||||
}, { |
||||
key: 'detect', |
||||
value: function detect(image, options) { |
||||
var _this2 = this; |
||||
|
||||
options = options || {}; |
||||
return this._delay(function (job) { |
||||
job._send('detect', { image: image, options: options, workerOptions: _this2.workerOptions }); |
||||
}); |
||||
} |
||||
}, { |
||||
key: 'terminate', |
||||
value: function terminate() { |
||||
if (this.worker) adapter.terminateWorker(this); |
||||
this.worker = null; |
||||
} |
||||
}, { |
||||
key: '_delay', |
||||
value: function _delay(fn) { |
||||
var _this3 = this; |
||||
|
||||
if (!this.worker) this.worker = adapter.spawnWorker(this, this.workerOptions); |
||||
|
||||
var job = new TesseractJob(this); |
||||
this._queue.push(function (e) { |
||||
_this3._queue.shift(); |
||||
_this3._currentJob = job; |
||||
fn(job); |
||||
}); |
||||
if (!this._currentJob) this._dequeue(); |
||||
return job; |
||||
} |
||||
}, { |
||||
key: '_dequeue', |
||||
value: function _dequeue() { |
||||
this._currentJob = null; |
||||
if (this._queue.length > 0) { |
||||
this._queue[0](); |
||||
} |
||||
} |
||||
}, { |
||||
key: '_recv', |
||||
value: function _recv(packet) { |
||||
if (this._currentJob.id === packet.jobId) { |
||||
this._currentJob._handle(packet); |
||||
} else { |
||||
console.warn('Job ID ' + packet.jobId + ' not known.'); |
||||
} |
||||
} |
||||
}]); |
||||
|
||||
return TesseractWorker; |
||||
}(); |
||||
|
||||
var jobCounter = 0; |
||||
|
||||
var TesseractJob = function () { |
||||
function TesseractJob(instance) { |
||||
_classCallCheck(this, TesseractJob); |
||||
|
||||
this.id = 'Job-' + ++jobCounter + '-' + Math.random().toString(16).slice(3, 8); |
||||
|
||||
this._instance = instance; |
||||
this._resolve = []; |
||||
this._reject = []; |
||||
this._progress = []; |
||||
} |
||||
|
||||
_createClass(TesseractJob, [{ |
||||
key: 'then', |
||||
value: function then(resolve, reject) { |
||||
if (this._resolve.push) { |
||||
this._resolve.push(resolve); |
||||
} else { |
||||
resolve(this._resolve); |
||||
} |
||||
|
||||
if (reject) this.catch(reject); |
||||
return this; |
||||
} |
||||
}, { |
||||
key: 'catch', |
||||
value: function _catch(reject) { |
||||
if (this._reject.push) { |
||||
this._reject.push(reject); |
||||
} else { |
||||
reject(this._reject); |
||||
} |
||||
return this; |
||||
} |
||||
}, { |
||||
key: 'progress', |
||||
value: function progress(fn) { |
||||
this._progress.push(fn); |
||||
return this; |
||||
} |
||||
}, { |
||||
key: '_send', |
||||
value: function _send(action, payload) { |
||||
adapter.sendPacket(this._instance, { |
||||
jobId: this.id, |
||||
action: action, |
||||
payload: payload |
||||
}); |
||||
} |
||||
}, { |
||||
key: '_handle', |
||||
value: function _handle(packet) { |
||||
var data = packet.data; |
||||
if (packet.status === 'resolve') { |
||||
if (this._resolve.length === 0) console.debug(data); |
||||
this._resolve.forEach(function (fn) { |
||||
var ret = fn(data); |
||||
if (ret && typeof ret.then == 'function') { |
||||
console.warn('TesseractJob instances do not chain like ES6 Promises. To convert it into a real promise, use Promise.resolve.'); |
||||
} |
||||
}); |
||||
this._resolve = data; |
||||
this._instance._dequeue(); |
||||
} else if (packet.status === 'reject') { |
||||
if (this._reject.length === 0) console.error(data); |
||||
this._reject.forEach(function (fn) { |
||||
return fn(data); |
||||
}); |
||||
this._reject = data; |
||||
this._instance._dequeue(); |
||||
} else if (packet.status === 'progress') { |
||||
this._progress.forEach(function (fn) { |
||||
return fn(data); |
||||
}); |
||||
} else { |
||||
console.warn('Message type unknown', packet.status); |
||||
} |
||||
} |
||||
}]); |
||||
|
||||
return TesseractJob; |
||||
}(); |
||||
|
||||
var DefaultTesseract = createWorker(adapter.defaultOptions); |
||||
DefaultTesseract.createWorker = createWorker; |
||||
|
||||
module.exports = DefaultTesseract; |
||||
|
||||
},{"./node/index.js":1}]},{},[2])(2) |
||||
}); |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
<script src="dist/tesseract.js"></script> |
||||
<input type="file" onchange="Tesseract.recognize(this.files[0]).progress(console.log)"> |
||||
|
||||
|
||||
<input type="file" onchange="Tesseract.recognize(this.files[0], {lang: 'chi_sim'}).progress(console.log)"> |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
var Tesseract = require('./src/index.js') |
||||
global.Tesseract = Tesseract; |
||||
|
||||
// Tesseract.recognize('yolop.png', {
|
||||
// lang: 'eng'
|
||||
// }).progress(function(info){
|
||||
// console.log('--', info)
|
||||
// })
|
||||
// .then(function(data){
|
||||
// console.log('--', data)
|
||||
// })
|
||||
|
||||
|
||||
// Tesseract.recognize('cosmic.jpg', {
|
||||
// lang: 'eng'
|
||||
// })
|
||||
Tesseract.detect('cosmic.jpg') |
||||
.progress(function(info){ |
||||
console.log(info) |
||||
}) |
||||
.then(function(data){ |
||||
console.log('done', data) |
||||
}) |
@ -1,30 +0,0 @@
@@ -1,30 +0,0 @@
|
||||
<canvas id="c"></canvas> |
||||
<script type="text/javascript" src="./tesseract/tesseract.js"></script> |
||||
<script type="text/javascript"> |
||||
var canvas = document.getElementById('c') |
||||
canvas.width = 400 |
||||
canvas.height = 400 |
||||
var ctx = canvas.getContext('2d'); |
||||
ctx.font = '30px "Arial Black"' |
||||
ctx.fillText('Hell0 World', 100, 40) |
||||
// ctx.fillText("囚犯離奇掙脫囚犯離奇掙脫", 100, 40) |
||||
ctx.font = '30px "Times New Roman"' |
||||
ctx.fillText('from beyond', 100, 80) |
||||
// ctx.fillText('2小時可換乘2次2小時可換乘2次', 100, 80) |
||||
ctx.font = '30px sans-serif' |
||||
ctx.fillText('the Cosmic Void', 100, 120) |
||||
|
||||
|
||||
Tesseract.workerUrl = location.protocol+'//'+location.host+'/tesseract/tesseract.worker.js' |
||||
|
||||
// Tesseract.detect(canvas) |
||||
// Tesseract.recognize('http://localhost:7355/westmorland.jpg') |
||||
// Tesseract.recognize(canvas, { tessedit_char_blacklist: 'e' }) |
||||
Tesseract.recognize(canvas) |
||||
.progress(function(e){ |
||||
console.log('progress', e) |
||||
}) |
||||
.then(function(e){ |
||||
console.log('result', e) |
||||
}) |
||||
</script> |
@ -1,106 +1,64 @@
@@ -1,106 +1,64 @@
|
||||
var coreUrl = 'https://cdn.rawgit.com/naptha/tesseract.js-core/master/index.js', |
||||
workerUrl = 'https://cdn.rawgit.com/naptha/tesseract.js/8b915dc/dist/tesseract.worker.js', |
||||
langUrl = 'https://cdn.rawgit.com/naptha/tessdata/gh-pages/3.02/', |
||||
worker; |
||||
|
||||
function recognize(image, options){ |
||||
if(!worker) worker = createWorker( Tesseract.coreUrl, Tesseract.workerUrl, Tesseract.langUrl ) |
||||
return worker.recognize(image, options) |
||||
exports.defaultOptions = { |
||||
langPath: 'https://cdn.rawgit.com/naptha/tessdata/gh-pages/3.02/', |
||||
workerPath: 'dist/worker.js', |
||||
tesseractPath: 'https://cdn.rawgit.com/naptha/tesseract.js-core/0.1.0/index.js', |
||||
} |
||||
|
||||
function detect(image){ |
||||
if(!worker) worker = createWorker( Tesseract.coreUrl, Tesseract.workerUrl, Tesseract.langUrl ) |
||||
return worker.detect(image) |
||||
exports.spawnWorker = function spawnWorker(instance, workerOptions){ |
||||
var worker = new Worker(workerOptions.workerPath) |
||||
worker.onmessage = function(e){ |
||||
instance._recv(e.data) |
||||
} |
||||
return worker |
||||
} |
||||
|
||||
function createWorker(coreUrl=Tesseract.coreUrl, workerUrl=Tesseract.workerUrl, langUrl=Tesseract.langUrl){ |
||||
|
||||
var blob = new Blob([`importScripts('${coreUrl}');
|
||||
importScripts('${workerUrl}');`])
|
||||
|
||||
var worker = new Worker(window.URL.createObjectURL(blob)); |
||||
|
||||
var bigworker = false |
||||
var jobCounter = 0 |
||||
var handlers = {} |
||||
|
||||
function runAsync(action, args){ |
||||
|
||||
var jobId = jobCounter++ |
||||
handlers[jobId] = {} |
||||
exports.terminateWorker = function(instance){ |
||||
instance.worker.terminate() |
||||
} |
||||
|
||||
var waitingCount = 0 |
||||
Object.getOwnPropertyNames(args) |
||||
.filter(name => typeof args[name] === 'function') |
||||
.forEach(name => { |
||||
waitingCount++ |
||||
args[name](value => { |
||||
args[name] = value |
||||
if(--waitingCount == 0) worker.postMessage({jobId, action, args}) |
||||
}) |
||||
exports.sendPacket = function sendPacket(instance, packet){ |
||||
loadImage(packet.payload.image, function(img){ |
||||
packet.payload.image = img |
||||
instance.worker.postMessage(packet) |
||||
}) |
||||
} |
||||
|
||||
if(waitingCount == 0) worker.postMessage({jobId, action, args}) |
||||
return { |
||||
then (f){ handlers[jobId].result = f; return this}, |
||||
error (f){ handlers[jobId].error = f; return this}, |
||||
progress(f){ handlers[jobId].progress = f; return this} |
||||
} |
||||
} |
||||
|
||||
worker.onmessage = function(e){ |
||||
var {jobId, progress, error, result} = e.data |
||||
var handler = handlers[jobId] |
||||
if(progress && handler.progress) handler.progress(progress); |
||||
if(error && handler.error) handler.error(error); |
||||
if(result && handler.result) handler.result(result); |
||||
} |
||||
|
||||
function convertToImageData(image){ |
||||
if(image.match && image.match(/^https?:\/\//)) { |
||||
return function thunk(cb){ |
||||
var img = new Image() |
||||
img.src = image |
||||
img.onload = () => cb(convertToImageData(img)) |
||||
} |
||||
function loadImage(image, cb){ |
||||
if(typeof image === 'string'){ |
||||
if(/^\#/.test(image)){ |
||||
// element css selector
|
||||
return loadImage(document.querySelector(image), cb) |
||||
}else{ |
||||
// url or path
|
||||
var im = new Image |
||||
im.src = image; |
||||
im.onload = e => loadImage(im, cb); |
||||
return |
||||
} |
||||
|
||||
if(typeof image === 'string') image = document.querySelector(image) |
||||
if(image.getContext) image = image.getContext('2d'); |
||||
else if(image.tagName == "IMG" || image.tagName == "VIDEO"){ |
||||
}else if(image instanceof File){ |
||||
// files
|
||||
var fr = new FileReader() |
||||
fr.onload = e => loadImage(fr.result, cb); |
||||
fr.readAsDataURL(image) |
||||
return |
||||
}else if(image instanceof Blob){ |
||||
return loadImage(URL.createObjectURL(image), cb) |
||||
}else if(image.getContext){ |
||||
// canvas element
|
||||
return loadImage(image.getContext('2d'), cb) |
||||
}else if(image.tagName == "IMG" || image.tagName == "VIDEO"){ |
||||
// image element or video element
|
||||
var c = document.createElement('canvas'); |
||||
c.width = image.naturalWidth || image.videoWidth; |
||||
c.height = image.naturalHeight || image.videoHeight; |
||||
var ctx = c.getContext('2d'); |
||||
ctx.drawImage(image, 0, 0); |
||||
image = ctx; |
||||
} |
||||
if(image.getImageData) image = image.getImageData(0, 0, image.canvas.width, image.canvas.height); |
||||
return image |
||||
} |
||||
|
||||
runAsync('init', {mem: (1<<24) * 6, langUrl}) |
||||
|
||||
return { |
||||
detect(image){ |
||||
return runAsync('detect', {image: convertToImageData(image)}) |
||||
}, |
||||
|
||||
recognize(image, options='eng'){ |
||||
|
||||
if (typeof options === 'string') options = {lang: options}; |
||||
else options.lang = options.lang || 'eng'; |
||||
|
||||
if (!bigworker && ['chi_sim', 'chi_tra', 'jpn'].indexOf(options.lang) != -1){ |
||||
runAsync('init', {mem: (1<<24) * 10, langUrl}) |
||||
bigworker = true |
||||
} |
||||
|
||||
return runAsync('recognize', {options, image: convertToImageData(image)}) |
||||
} |
||||
return loadImage(ctx, cb) |
||||
}else if(image.getImageData){ |
||||
// canvas context
|
||||
var data = image.getImageData(0, 0, image.canvas.width, image.canvas.height); |
||||
return loadImage(data, cb) |
||||
} |
||||
cb(image) |
||||
} |
||||
|
||||
var Tesseract = {coreUrl, workerUrl, langUrl, recognize, detect, createWorker} |
||||
|
||||
module.exports = Tesseract |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
var leveljs = require('level-js') |
||||
var db = typeof indexedDB === 'undefined' ? { open: (_, cb) => cb(true) } : leveljs('./tessdata2') |
||||
|
||||
var langdata = require('../common/langdata.json') |
||||
|
||||
module.exports = function getLanguageData(req, res, cb){ |
||||
var lang = req.options.lang; |
||||
|
||||
function saveDataFile(data){ |
||||
db.put(lang, data, err => console.log('cached', lang, err)) |
||||
cb(data) |
||||
} |
||||
|
||||
db.open({ compression: false }, err => { |
||||
if (err) return fetchLanguageData(req, res, cb); |
||||
db.get(lang, (err, data) => { |
||||
if (err) return fetchLanguageData(req, res, saveDataFile); |
||||
res.progress({ status: 'found in cache ' + lang + '.traineddata' }) |
||||
cb(data) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
|
||||
var ungzip = require('pako').ungzip; |
||||
|
||||
function fetchLanguageData(req, res, cb){ |
||||
var lang = req.options.lang; |
||||
var langfile = lang + '.traineddata.gz'; |
||||
var url = req.workerOptions.langPath + langfile; |
||||
|
||||
var xhr = new XMLHttpRequest(); |
||||
xhr.responseType = 'arraybuffer'; |
||||
xhr.open('GET', url, true); |
||||
xhr.onerror = e => { |
||||
xhr.onprogress = xhr.onload = null |
||||
cb(xhr, null) |
||||
} |
||||
xhr.onprogress = e => |
||||
res.progress({ |
||||
status: 'downloading ' + langfile, |
||||
loaded: e.loaded, |
||||
progress: Math.min(1, e.loaded / langdata[lang]) |
||||
}); |
||||
|
||||
xhr.onload = e => { |
||||
if (!(xhr.status == 200 || (xhr.status == 0 && xhr.response))) return res.reject('Error downloading language ' + url); |
||||
res.progress({ status: 'unzipping ' + langfile }) |
||||
|
||||
// in case the gzips are already ungzipped or extra gzipped
|
||||
var response = new Uint8Array(xhr.response) |
||||
try { |
||||
while(response[0] == 0x1f && response[1] == 0x8b) response = ungzip(response); |
||||
} catch (err) { |
||||
return res.reject('Error unzipping language file ' + langfile + '\n' + err.message) |
||||
} |
||||
|
||||
cb(response) |
||||
} |
||||
xhr.send() |
||||
} |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
"use strict"; |
||||
|
||||
var workerUtils = require('../common/worker.js') |
||||
|
||||
global.addEventListener('message', function(e){ |
||||
var packet = e.data; |
||||
workerUtils.dispatchHandlers(packet, obj => postMessage(obj)) |
||||
}) |
||||
|
||||
exports.getLanguageData = require('./lang.js') |
||||
|
||||
exports.getCore = function(req, res){ |
||||
if(!global.TesseractCore){ |
||||
res.progress({ status: 'loading tesseract core' }) |
||||
importScripts(req.workerOptions.tesseractPath) |
||||
res.progress({ status: 'loaded tesseract core' }) |
||||
} |
||||
return TesseractCore |
||||
} |
||||
|
||||
workerUtils.setAdapter(module.exports); |
@ -1,4 +1,4 @@
@@ -1,4 +1,4 @@
|
||||
export default function circularize(page){ |
||||
module.exports = function circularize(page){ |
||||
page.paragraphs = [] |
||||
page.lines = [] |
||||
page.words = [] |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
module.exports = function desaturate(image){ |
||||
var width, height; |
||||
if(image.data){ |
||||
var src = image.data; |
||||
width = image.width, height = image.height; |
||||
var dst = new Uint8Array(width * height); |
||||
var srcLength = src.length | 0, srcLength_16 = (srcLength - 16) | 0; |
||||
|
||||
for (var i = 0, j = 0; i <= srcLength_16; i += 16, j += 4) { |
||||
// convert to grayscale 4 pixels at a time; eveything with alpha gets put in front of 50% gray
|
||||
dst[j] = (((src[i] * 77 + src[i+1] * 151 + src[i+2] * 28) * src[i+3]) + ((255-src[i+3]) << 15) + 32768) >> 16 |
||||
dst[j+1] = (((src[i+4] * 77 + src[i+5] * 151 + src[i+6] * 28) * src[i+7]) + ((255-src[i+7]) << 15) + 32768) >> 16 |
||||
dst[j+2] = (((src[i+8] * 77 + src[i+9] * 151 + src[i+10] * 28) * src[i+11]) + ((255-src[i+11]) << 15) + 32768) >> 16 |
||||
dst[j+3] = (((src[i+12] * 77 + src[i+13] * 151 + src[i+14] * 28) * src[i+15]) + ((255-src[i+15]) << 15) + 32768) >> 16 |
||||
} |
||||
for (; i < srcLength; i += 4, ++j) //finish up
|
||||
dst[j] = (((src[i] * 77 + src[i+1] * 151 + src[i+2] * 28) * src[i+3]) + ((255-src[i+3]) << 15) + 32768) >> 16 |
||||
image = dst; |
||||
} else { |
||||
throw 'Expected ImageData' |
||||
} |
||||
return image |
||||
} |
@ -0,0 +1,161 @@
@@ -0,0 +1,161 @@
|
||||
function deindent(html){ |
||||
var lines = html.split('\n') |
||||
if(lines[0].substring(0,2) === " "){ |
||||
for (var i = 0; i < lines.length; i++) { |
||||
if (lines[i].substring(0,2) === " ") { |
||||
lines[i] = lines[i].slice(2) |
||||
} |
||||
}; |
||||
} |
||||
return lines.join('\n') |
||||
} |
||||
|
||||
module.exports = function DumpLiterallyEverything(Module, base){ |
||||
var ri = base.GetIterator(); |
||||
var blocks = []; |
||||
var block, para, textline, word, symbol; |
||||
|
||||
function enumToString(value, prefix){ |
||||
return (Object.keys(Module) |
||||
.filter(function(e){ return e.substr(0, prefix.length + 1) == prefix + '_' }) |
||||
.filter(function(e){ return Module[e] === value }) |
||||
.map(function(e){ return e.slice(prefix.length + 1) })[0]) |
||||
} |
||||
|
||||
ri.Begin() |
||||
do { |
||||
if(ri.IsAtBeginningOf(Module.RIL_BLOCK)){ |
||||
var poly = ri.BlockPolygon(); |
||||
var polygon = null; |
||||
// BlockPolygon() returns null when automatic page segmentation is off
|
||||
if(Module.getPointer(poly) > 0){ |
||||
var n = poly.get_n(), |
||||
px = poly.get_x(), |
||||
py = poly.get_y(), |
||||
polygon = []; |
||||
for(var i = 0; i < n; i++){ |
||||
polygon.push([px.getValue(i), py.getValue(i)]); |
||||
} |
||||
Module._ptaDestroy(Module.getPointer(poly)); |
||||
} |
||||
|
||||
block = { |
||||
paragraphs: [], |
||||
|
||||
text: ri.GetUTF8Text(Module.RIL_BLOCK), |
||||
confidence: ri.Confidence(Module.RIL_BLOCK), |
||||
baseline: ri.getBaseline(Module.RIL_BLOCK), |
||||
bbox: ri.getBoundingBox(Module.RIL_BLOCK), |
||||
|
||||
blocktype: enumToString(ri.BlockType(), 'PT'), |
||||
polygon: polygon |
||||
} |
||||
blocks.push(block) |
||||
} |
||||
if(ri.IsAtBeginningOf(Module.RIL_PARA)){ |
||||
para = { |
||||
lines: [], |
||||
|
||||
text: ri.GetUTF8Text(Module.RIL_PARA), |
||||
confidence: ri.Confidence(Module.RIL_PARA), |
||||
baseline: ri.getBaseline(Module.RIL_PARA), |
||||
bbox: ri.getBoundingBox(Module.RIL_PARA), |
||||
|
||||
is_ltr: !!ri.ParagraphIsLtr() |
||||
} |
||||
block.paragraphs.push(para) |
||||
} |
||||
if(ri.IsAtBeginningOf(Module.RIL_TEXTLINE)){ |
||||
textline = { |
||||
words: [], |
||||
|
||||
text: ri.GetUTF8Text(Module.RIL_TEXTLINE), |
||||
confidence: ri.Confidence(Module.RIL_TEXTLINE), |
||||
baseline: ri.getBaseline(Module.RIL_TEXTLINE), |
||||
bbox: ri.getBoundingBox(Module.RIL_TEXTLINE) |
||||
} |
||||
para.lines.push(textline) |
||||
} |
||||
if(ri.IsAtBeginningOf(Module.RIL_WORD)){ |
||||
var fontInfo = ri.getWordFontAttributes(), |
||||
wordDir = ri.WordDirection(); |
||||
word = { |
||||
symbols: [], |
||||
choices: [], |
||||
|
||||
text: ri.GetUTF8Text(Module.RIL_WORD), |
||||
confidence: ri.Confidence(Module.RIL_WORD), |
||||
baseline: ri.getBaseline(Module.RIL_WORD), |
||||
bbox: ri.getBoundingBox(Module.RIL_WORD), |
||||
|
||||
is_numeric: !!ri.WordIsNumeric(), |
||||
in_dictionary: !!ri.WordIsFromDictionary(), |
||||
direction: enumToString(wordDir, 'DIR'), |
||||
language: ri.WordRecognitionLanguage(), |
||||
|
||||
is_bold: fontInfo.is_bold, |
||||
is_italic: fontInfo.is_italic, |
||||
is_underlined: fontInfo.is_underlined, |
||||
is_monospace: fontInfo.is_monospace, |
||||
is_serif: fontInfo.is_serif, |
||||
is_smallcaps: fontInfo.is_smallcaps, |
||||
font_size: fontInfo.pointsize, |
||||
font_id: fontInfo.font_id, |
||||
font_name: fontInfo.font_name, |
||||
} |
||||
var wc = new Module.WordChoiceIterator(ri); |
||||
do { |
||||
word.choices.push({ |
||||
text: wc.GetUTF8Text(), |
||||
confidence: wc.Confidence() |
||||
}) |
||||
} while (wc.Next()); |
||||
Module.destroy(wc) |
||||
textline.words.push(word) |
||||
} |
||||
|
||||
var image = null; |
||||
// var pix = ri.GetBinaryImage(Module.RIL_SYMBOL)
|
||||
// var image = pix2array(pix);
|
||||
// // for some reason it seems that things stop working if you destroy pics
|
||||
// Module._pixDestroy(Module.getPointer(pix));
|
||||
if(ri.IsAtBeginningOf(Module.RIL_SYMBOL)){ |
||||
symbol = { |
||||
choices: [], |
||||
image: image, |
||||
|
||||
text: ri.GetUTF8Text(Module.RIL_SYMBOL), |
||||
confidence: ri.Confidence(Module.RIL_SYMBOL), |
||||
baseline: ri.getBaseline(Module.RIL_SYMBOL), |
||||
bbox: ri.getBoundingBox(Module.RIL_SYMBOL), |
||||
|
||||
is_superscript: !!ri.SymbolIsSuperscript(), |
||||
is_subscript: !!ri.SymbolIsSubscript(), |
||||
is_dropcap: !!ri.SymbolIsDropcap(), |
||||
} |
||||
word.symbols.push(symbol) |
||||
var ci = new Module.ChoiceIterator(ri); |
||||
do { |
||||
symbol.choices.push({ |
||||
text: ci.GetUTF8Text(), |
||||
confidence: ci.Confidence() |
||||
}) |
||||
} while (ci.Next()); |
||||
Module.destroy(ci) |
||||
} |
||||
} while (ri.Next(Module.RIL_SYMBOL)); |
||||
Module.destroy(ri) |
||||
|
||||
return { |
||||
text: base.GetUTF8Text(), |
||||
html: deindent(base.GetHOCRText()), |
||||
|
||||
confidence: base.MeanTextConf(), |
||||
|
||||
blocks: blocks, |
||||
|
||||
psm: enumToString(base.GetPageSegMode(), 'PSM'), |
||||
oem: enumToString(base.oem(), 'OEM'), |
||||
version: base.Version(), |
||||
} |
||||
} |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
{"afr": 1079573, "ara": 1701536, "aze": 1420865, "bel": 1276820, "ben": 6772012, "bul": 1605615, "cat": 1652368, "ces": 1035441, "chi_sim": 17710414, "chi_tra": 24717749, "chr": 320649, "dan-frak": 677656, "dan": 1972936, "deu-frak": 822644, "deu": 991656, "ell": 859719, "eng": 9453554, "enm": 619254, "epo": 1241212, "equ": 821130, "est": 1905040, "eus": 1641190, "fin": 979418, "fra": 1376221, "frk": 5912963, "frm": 5147082, "glg": 1674938, "grc": 3012615, "heb": 1051501, "hin": 6590065, "hrv": 1926995, "hun": 3074473, "ind": 1874776, "isl": 1634041, "ita": 948593, "ita_old": 3436571, "jpn": 13507168, "kan": 4390317, "kor": 5353098, "lav": 1843944, "lit": 1779240, "mal": 5966263, "meme": 88453, "mkd": 1163087, "mlt": 1463001, "msa": 1665427, "nld": 1134708, "nor": 2191610, "osd": 4274649, "pol": 7024662, "por": 909359, "ron": 915680, "rus": 5969957, "slk-frak": 289885, "slk": 2217342, "slv": 1611338, "spa": 883170, "spa_old": 5647453, "sqi": 1667041, "srp": 1770244, "swa": 757916, "swe": 2451917, "tam": 3498763, "tel": 5795246, "tgl": 1496256, "tha": 3811136, "tur": 3563264, "ukr": 937566, "vie": 2195922} |
@ -0,0 +1,143 @@
@@ -0,0 +1,143 @@
|
||||
var latestJob; |
||||
var Module; |
||||
var base; |
||||
var adapter = {}; |
||||
|
||||
function dispatchHandlers(packet, send){ |
||||
function respond(status, data){ |
||||
send({ |
||||
jobId: packet.jobId, |
||||
status: status, |
||||
data: data |
||||
}) |
||||
} |
||||
respond.resolve = respond.bind(this, 'resolve') |
||||
respond.reject = respond.bind(this, 'reject') |
||||
respond.progress = respond.bind(this, 'progress') |
||||
|
||||
latestJob = respond; |
||||
|
||||
if(packet.action === 'recognize'){ |
||||
handleRecognize(packet.payload, respond) |
||||
}else if(packet.action === 'detect'){ |
||||
handleDetect(packet.payload, respond) |
||||
} |
||||
} |
||||
exports.dispatchHandlers = dispatchHandlers; |
||||
|
||||
exports.setAdapter = function setAdapter(impl){ |
||||
adapter = impl; |
||||
} |
||||
|
||||
|
||||
function handleInit(req, res){ |
||||
if(!Module){ |
||||
var Core = adapter.getCore(req, res); |
||||
|
||||
res.progress({ status: 'initializing tesseract api' }) |
||||
Module = Core({ |
||||
TOTAL_MEMORY: req.memory, |
||||
TesseractProgress(percent){ |
||||
latestJob.progress({ status: 'recognizing text', progress: Math.max(0, (percent-30)/70) }) |
||||
}, |
||||
onRuntimeInitialized() {} |
||||
}) |
||||
Module.FS_createPath("/", "tessdata", true, true) |
||||
base = new Module.TessBaseAPI() |
||||
res.progress({ status: 'initialized tesseract api' }) |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
var dump = require('./dump.js') |
||||
var desaturate = require('./desaturate.js') |
||||
|
||||
|
||||
function setImage(Module, base, image){ |
||||
var imgbin = desaturate(image), |
||||
width = image.width, |
||||
height = image.height; |
||||
|
||||
var ptr = Module.allocate(imgbin, 'i8', Module.ALLOC_NORMAL); |
||||
base.SetImage(Module.wrapPointer(ptr), width, height, 1, width); |
||||
base.SetRectangle(0, 0, width, height) |
||||
return ptr; |
||||
} |
||||
|
||||
function loadLanguage(req, res, cb){ |
||||
var lang = req.options.lang; |
||||
|
||||
if(!Module._loadedLanguages) Module._loadedLanguages = {}; |
||||
if(lang in Module._loadedLanguages) return cb(); |
||||
|
||||
adapter.getLanguageData(req, res, function(data){ |
||||
Module.FS_createDataFile('tessdata', lang + ".traineddata", data, true, false); |
||||
res.progress({ status: 'loaded ' + lang + '.traineddata' }) |
||||
Module._loadedLanguages[lang] = true; |
||||
cb() |
||||
}) |
||||
} |
||||
|
||||
|
||||
|
||||
function handleRecognize(req, res){ |
||||
handleInit(req, res) |
||||
|
||||
loadLanguage(req, res, function(){ |
||||
var lang = req.options.lang; |
||||
|
||||
base.Init(null, lang) |
||||
res.progress({ status: 'initialized with language' }) |
||||
|
||||
var ptr = setImage(Module, base, req.image); |
||||
base.Recognize(null) |
||||
|
||||
var result = dump(Module, base) |
||||
|
||||
base.End(); |
||||
Module._free(ptr); |
||||
|
||||
res.resolve(result); |
||||
}) |
||||
} |
||||
|
||||
|
||||
function handleDetect(req, res){ |
||||
handleInit(req, res) |
||||
req.options.lang = 'osd'; |
||||
loadLanguage(req, res, function(){ |
||||
|
||||
base.Init(null, 'osd') |
||||
base.SetPageSegMode(Module.PSM_OSD_ONLY) |
||||
|
||||
var ptr = setImage(Module, base, req.image); |
||||
|
||||
var results = new Module.OSResults(); |
||||
var success = base.DetectOS(results); |
||||
if(!success){ |
||||
base.End(); |
||||
Module._free(ptr); |
||||
res.reject("failed to detect os") |
||||
} else { |
||||
var charset = results.get_unicharset() |
||||
|
||||
var best = results.get_best_result() |
||||
var oid = best.get_orientation_id(), |
||||
sid = best.get_script_id(); |
||||
|
||||
var result = { |
||||
tesseract_script_id: sid, |
||||
script: charset.get_script_from_script_id(sid), |
||||
script_confidence: best.get_sconfidence(), |
||||
orientation_degrees: [0, 270, 180, 90][oid], |
||||
orientation_confidence: best.get_oconfidence() |
||||
} |
||||
|
||||
base.End(); |
||||
Module._free(ptr); |
||||
|
||||
res.resolve(result) |
||||
} |
||||
}) |
||||
} |
@ -0,0 +1,137 @@
@@ -0,0 +1,137 @@
|
||||
"use strict"; |
||||
|
||||
var adapter = require('./node/index.js') |
||||
|
||||
function createWorker(workerOptions){ |
||||
return new TesseractWorker(workerOptions) |
||||
} |
||||
|
||||
class TesseractWorker { |
||||
constructor(workerOptions){ |
||||
this.worker = null; |
||||
this.workerOptions = workerOptions; |
||||
this._currentJob = null; |
||||
this._queue = [] |
||||
} |
||||
|
||||
recognize(image, options){ |
||||
return this._delay(job => { |
||||
options = options || {} |
||||
options.lang = options.lang || 'eng'; |
||||
job._send('recognize', { image: image, options: options, workerOptions: this.workerOptions }) |
||||
}) |
||||
} |
||||
detect(image, options){ |
||||
options = options || {} |
||||
return this._delay(job => { |
||||
job._send('detect', { image: image, options: options, workerOptions: this.workerOptions }) |
||||
}) |
||||
} |
||||
|
||||
terminate(){ |
||||
if(this.worker) adapter.terminateWorker(this); |
||||
this.worker = null; |
||||
} |
||||
|
||||
_delay(fn){ |
||||
if(!this.worker) this.worker = adapter.spawnWorker(this, this.workerOptions); |
||||
|
||||
var job = new TesseractJob(this); |
||||
this._queue.push(e => { |
||||
this._queue.shift() |
||||
this._currentJob = job; |
||||
fn(job) |
||||
}) |
||||
if(!this._currentJob) this._dequeue(); |
||||
return job |
||||
} |
||||
|
||||
_dequeue(){ |
||||
this._currentJob = null; |
||||
if(this._queue.length > 0){ |
||||
this._queue[0]() |
||||
} |
||||
} |
||||
|
||||
_recv(packet){ |
||||
if(this._currentJob.id === packet.jobId){ |
||||
this._currentJob._handle(packet) |
||||
}else{ |
||||
console.warn('Job ID ' + packet.jobId + ' not known.') |
||||
} |
||||
} |
||||
} |
||||
|
||||
var jobCounter = 0; |
||||
|
||||
class TesseractJob { |
||||
constructor(instance){ |
||||
this.id = 'Job-' + (++jobCounter) + '-' + Math.random().toString(16).slice(3, 8) |
||||
|
||||
this._instance = instance; |
||||
this._resolve = [] |
||||
this._reject = [] |
||||
this._progress = [] |
||||
} |
||||
|
||||
then(resolve, reject){ |
||||
if(this._resolve.push){ |
||||
this._resolve.push(resolve) |
||||
}else{ |
||||
resolve(this._resolve) |
||||
} |
||||
|
||||
if(reject) this.catch(reject); |
||||
return this; |
||||
} |
||||
catch(reject){ |
||||
if(this._reject.push){ |
||||
this._reject.push(reject) |
||||
}else{ |
||||
reject(this._reject) |
||||
} |
||||
return this; |
||||
} |
||||
progress(fn){ |
||||
this._progress.push(fn) |
||||
return this; |
||||
} |
||||
_send(action, payload){ |
||||
adapter.sendPacket(this._instance, { |
||||
jobId: this.id, |
||||
action: action, |
||||
payload: payload |
||||
}) |
||||
} |
||||
|
||||
_handle(packet){ |
||||
var data = packet.data; |
||||
if(packet.status === 'resolve'){ |
||||
if(this._resolve.length === 0) console.debug(data); |
||||
this._resolve.forEach(fn => { |
||||
var ret = fn(data); |
||||
if(ret && typeof ret.then == 'function'){ |
||||
console.warn('TesseractJob instances do not chain like ES6 Promises. To convert it into a real promise, use Promise.resolve.') |
||||
} |
||||
}) |
||||
this._resolve = data; |
||||
this._instance._dequeue() |
||||
}else if(packet.status === 'reject'){ |
||||
if(this._reject.length === 0) console.error(data); |
||||
this._reject.forEach(fn => fn(data)) |
||||
this._reject = data; |
||||
this._instance._dequeue() |
||||
}else if(packet.status === 'progress'){ |
||||
this._progress.forEach(fn => fn(data)) |
||||
}else{ |
||||
console.warn('Message type unknown', packet.status) |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
var DefaultTesseract = createWorker(adapter.defaultOptions) |
||||
DefaultTesseract.createWorker = createWorker; |
||||
|
||||
module.exports = DefaultTesseract |
||||
|
@ -0,0 +1,83 @@
@@ -0,0 +1,83 @@
|
||||
var path = require('path') |
||||
exports.defaultOptions = { |
||||
workerPath: path.join(__dirname, 'worker.js'), |
||||
langPath: 'http://cdn.rawgit.com/naptha/tessdata/gh-pages/3.02/', |
||||
} |
||||
|
||||
var fork = require('child_process').fork; |
||||
var fs = require('fs') |
||||
|
||||
exports.spawnWorker = function spawnWorker(instance, workerOptions){ |
||||
var cp = fork(workerOptions.workerPath); |
||||
cp.on('message', function(packet){ |
||||
instance._recv(packet) |
||||
}) |
||||
return cp; |
||||
} |
||||
|
||||
exports.terminateWorker = function(instance){ |
||||
instance.worker.kill() |
||||
} |
||||
|
||||
exports.sendPacket = function sendPacket(instance, packet){ |
||||
loadImage(packet.payload.image, function(img){ |
||||
packet.payload.image = img |
||||
instance.worker.send(packet) |
||||
}) |
||||
} |
||||
|
||||
|
||||
function loadImage(image, cb){ |
||||
if(typeof image === 'string'){ |
||||
fs.readFile(image, function(err, buffer){ |
||||
loadImage(buffer, cb) |
||||
}) |
||||
return |
||||
}else if(image instanceof Buffer){ |
||||
var fileType = require('file-type'); |
||||
var mime = fileType(image).mime |
||||
|
||||
|
||||
if(mime === 'image/png'){ |
||||
var PNGReader = require('png.js'); |
||||
var reader = new PNGReader(image); |
||||
reader.parse(function(err, png){ |
||||
if (err) throw err; |
||||
|
||||
var image = { |
||||
width: png.getWidth(), |
||||
height: png.getHeight() |
||||
} |
||||
image.data = new Uint8Array(image.width * image.height * 4) |
||||
for(var j = 0; j < image.height; j++){ |
||||
for(var i = 0; i < image.width; i++){ |
||||
var offset = 4 * (i + j * image.width), |
||||
pix = png.getPixel(i, j); |
||||
|
||||
image.data[offset] = pix[0] |
||||
image.data[offset + 1] = pix[1] |
||||
image.data[offset + 2] = pix[2] |
||||
image.data[offset + 3] = pix[3]; |
||||
} |
||||
} |
||||
// console.log(image)
|
||||
loadImage(image, cb) |
||||
}); |
||||
return |
||||
}else if(mime === 'image/jpeg'){ |
||||
var jpeg = require('jpeg-js'); |
||||
loadImage(jpeg.decode(image), cb) |
||||
return |
||||
} |
||||
|
||||
// TODO: support for TIFF, NetPBM, BMP, etc.
|
||||
} |
||||
|
||||
// node uses json.stringify for ipc which means we need to turn
|
||||
// fancy arrays into raw arrays
|
||||
if(image && image.data && image.data.length && !Array.isArray(image.data)){ |
||||
image.data = Array.from(image.data) |
||||
return loadImage(image, cb) |
||||
} |
||||
cb(image) |
||||
} |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
var http = require("http"), |
||||
zlib = require("zlib"), |
||||
fs = require("fs"), |
||||
path = require("path"); |
||||
|
||||
var langdata = require('../common/langdata.json') |
||||
|
||||
function getLanguageData(req, res, cb){ |
||||
var lang = req.options.lang; |
||||
var langfile = lang + '.traineddata.gz'; |
||||
var url = req.workerOptions.langPath + langfile; |
||||
|
||||
fs.readFile(lang + '.traineddata', function (err, data) { |
||||
if(!err) return cb(new Uint8Array(data)); |
||||
|
||||
http.get(url, function(stream){ |
||||
var received_bytes = 0; |
||||
stream.on('data', function(chunk) { |
||||
received_bytes += chunk.length; |
||||
res.progress({ |
||||
status: 'downloading ' + langfile, |
||||
loaded: received_bytes, |
||||
progress: Math.min(1, received_bytes / langdata[lang]) |
||||
}); |
||||
|
||||
}); |
||||
|
||||
var gunzip = zlib.createGunzip(); |
||||
stream.pipe(gunzip).pipe(fs.createWriteStream(lang + '.traineddata')) |
||||
gunzip.on('end', function(){ getLanguageData(req, stream, cb) }) |
||||
}) |
||||
}); |
||||
} |
||||
|
||||
|
||||
module.exports = getLanguageData; |
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
"use strict"; |
||||
|
||||
var workerUtils = require('../common/worker.js') |
||||
|
||||
process.on('message', function(packet){ |
||||
workerUtils.dispatchHandlers(packet, obj => process.send(obj)) |
||||
}) |
||||
|
||||
exports.getLanguageData = require('./lang.js') |
||||
|
||||
|
||||
var TesseractCore; |
||||
exports.getCore = function(req, res){ |
||||
if(!TesseractCore){ |
||||
res.progress({ status: 'loading tesseract core' }) |
||||
TesseractCore = require('tesseract.js-core') |
||||
res.progress({ status: 'loaded tesseract core' }) |
||||
} |
||||
return TesseractCore |
||||
} |
||||
|
||||
workerUtils.setAdapter(module.exports); |
@ -1,26 +0,0 @@
@@ -1,26 +0,0 @@
|
||||
export default function desaturate(image){ |
||||
var width, height; |
||||
if(image.data){ |
||||
var src = image.data; |
||||
width = image.width, height = image.height; |
||||
var dst = new Uint8Array(width * height); |
||||
var srcLength = src.length | 0, srcLength_16 = (srcLength - 16) | 0; |
||||
|
||||
for (var i = 0, j = 0; i <= srcLength_16; i += 16, j += 4) { |
||||
// convert to grayscale 4 pixels at a time; eveything with alpha gets put in front of 50% gray
|
||||
dst[j] = (((src[i] * 77 + src[i+1] * 151 + src[i+2] * 28) * src[i+3]) + ((255-src[i+3]) << 15) + 32768) >> 16 |
||||
dst[j+1] = (((src[i+4] * 77 + src[i+5] * 151 + src[i+6] * 28) * src[i+7]) + ((255-src[i+7]) << 15) + 32768) >> 16 |
||||
dst[j+2] = (((src[i+8] * 77 + src[i+9] * 151 + src[i+10] * 28) * src[i+11]) + ((255-src[i+11]) << 15) + 32768) >> 16 |
||||
dst[j+3] = (((src[i+12] * 77 + src[i+13] * 151 + src[i+14] * 28) * src[i+15]) + ((255-src[i+15]) << 15) + 32768) >> 16 |
||||
|
||||
} |
||||
for (; i < srcLength; i += 4, ++j) //finish up
|
||||
dst[j] = (((src[i] * 77 + src[i+1] * 151 + src[i+2] * 28) * src[i+3]) + ((255-src[i+3]) << 15) + 32768) >> 16 |
||||
|
||||
image = dst; |
||||
} |
||||
else { |
||||
throw 'Expected ImageData' |
||||
} |
||||
return image |
||||
} |
@ -1,166 +0,0 @@
@@ -1,166 +0,0 @@
|
||||
function deindent(html){ |
||||
var lines = html.split('\n') |
||||
if(lines[0].substring(0,2) === " "){ |
||||
for (var i = 0; i < lines.length; i++) { |
||||
if (lines[i].substring(0,2) === " ") { |
||||
lines[i] = lines[i].slice(2) |
||||
} |
||||
}; |
||||
} |
||||
return lines.join('\n') |
||||
} |
||||
|
||||
export default function DumpLiterallyEverything(){ |
||||
|
||||
var {module, base} = self |
||||
|
||||
var ri = base.GetIterator(); |
||||
var blocks = []; |
||||
var block, para, textline, word, symbol; |
||||
|
||||
function enumToString(value, prefix){ |
||||
return (Object.keys(module) |
||||
.filter(function(e){ return e.substr(0, prefix.length + 1) == prefix + '_' }) |
||||
.filter(function(e){ return module[e] === value }) |
||||
.map(function(e){ return e.slice(prefix.length + 1) })[0]) |
||||
} |
||||
|
||||
const {RIL_BLOCK, RIL_PARA, RIL_TEXTLINE, RIL_WORD, RIL_SYMBOL} = module |
||||
|
||||
ri.Begin() |
||||
do { |
||||
if(ri.IsAtBeginningOf(RIL_BLOCK)){ |
||||
var poly = ri.BlockPolygon(); |
||||
var polygon = null; |
||||
// BlockPolygon() returns null when automatic page segmentation is off
|
||||
if(module.getPointer(poly) > 0){ |
||||
var n = poly.get_n(), |
||||
px = poly.get_x(), |
||||
py = poly.get_y(), |
||||
polygon = []; |
||||
for(var i = 0; i < n; i++){ |
||||
polygon.push([px.getValue(i), py.getValue(i)]); |
||||
} |
||||
module._ptaDestroy(module.getPointer(poly)); |
||||
} |
||||
|
||||
block = { |
||||
paragraphs: [], |
||||
|
||||
text: ri.GetUTF8Text(RIL_BLOCK), |
||||
confidence: ri.Confidence(RIL_BLOCK), |
||||
baseline: ri.getBaseline(RIL_BLOCK), |
||||
bbox: ri.getBoundingBox(RIL_BLOCK), |
||||
|
||||
blocktype: enumToString(ri.BlockType(), 'PT'), |
||||
polygon: polygon |
||||
} |
||||
blocks.push(block) |
||||
} |
||||
if(ri.IsAtBeginningOf(RIL_PARA)){ |
||||
para = { |
||||
lines: [], |
||||
|
||||
text: ri.GetUTF8Text(RIL_PARA), |
||||
confidence: ri.Confidence(RIL_PARA), |
||||
baseline: ri.getBaseline(RIL_PARA), |
||||
bbox: ri.getBoundingBox(RIL_PARA), |
||||
|
||||
is_ltr: !!ri.ParagraphIsLtr() |
||||
} |
||||
block.paragraphs.push(para) |
||||
} |
||||
if(ri.IsAtBeginningOf(RIL_TEXTLINE)){ |
||||
textline = { |
||||
words: [], |
||||
|
||||
text: ri.GetUTF8Text(RIL_TEXTLINE), |
||||
confidence: ri.Confidence(RIL_TEXTLINE), |
||||
baseline: ri.getBaseline(RIL_TEXTLINE), |
||||
bbox: ri.getBoundingBox(RIL_TEXTLINE) |
||||
} |
||||
para.lines.push(textline) |
||||
} |
||||
if(ri.IsAtBeginningOf(RIL_WORD)){ |
||||
var fontInfo = ri.getWordFontAttributes(), |
||||
wordDir = ri.WordDirection(); |
||||
word = { |
||||
symbols: [], |
||||
choices: [], |
||||
|
||||
text: ri.GetUTF8Text(RIL_WORD), |
||||
confidence: ri.Confidence(RIL_WORD), |
||||
baseline: ri.getBaseline(RIL_WORD), |
||||
bbox: ri.getBoundingBox(RIL_WORD), |
||||
|
||||
is_numeric: !!ri.WordIsNumeric(), |
||||
in_dictionary: !!ri.WordIsFromDictionary(), |
||||
direction: enumToString(wordDir, 'DIR'), |
||||
language: ri.WordRecognitionLanguage(), |
||||
|
||||
is_bold: fontInfo.is_bold, |
||||
is_italic: fontInfo.is_italic, |
||||
is_underlined: fontInfo.is_underlined, |
||||
is_monospace: fontInfo.is_monospace, |
||||
is_serif: fontInfo.is_serif, |
||||
is_smallcaps: fontInfo.is_smallcaps, |
||||
font_size: fontInfo.pointsize, |
||||
font_id: fontInfo.font_id, |
||||
font_name: fontInfo.font_name, |
||||
} |
||||
var wc = new module.WordChoiceIterator(ri); |
||||
do { |
||||
word.choices.push({ |
||||
text: wc.GetUTF8Text(), |
||||
confidence: wc.Confidence() |
||||
}) |
||||
} while (wc.Next()); |
||||
module.destroy(wc) |
||||
textline.words.push(word) |
||||
} |
||||
|
||||
var image = null; |
||||
// var pix = ri.GetBinaryImage(RIL_SYMBOL)
|
||||
// var image = pix2array(pix);
|
||||
// // for some reason it seems that things stop working if you destroy pics
|
||||
// module._pixDestroy(module.getPointer(pix));
|
||||
if(ri.IsAtBeginningOf(RIL_SYMBOL)){ |
||||
symbol = { |
||||
choices: [], |
||||
image: image, |
||||
|
||||
text: ri.GetUTF8Text(RIL_SYMBOL), |
||||
confidence: ri.Confidence(RIL_SYMBOL), |
||||
baseline: ri.getBaseline(RIL_SYMBOL), |
||||
bbox: ri.getBoundingBox(RIL_SYMBOL), |
||||
|
||||
is_superscript: !!ri.SymbolIsSuperscript(), |
||||
is_subscript: !!ri.SymbolIsSubscript(), |
||||
is_dropcap: !!ri.SymbolIsDropcap(), |
||||
} |
||||
word.symbols.push(symbol) |
||||
var ci = new module.ChoiceIterator(ri); |
||||
do { |
||||
symbol.choices.push({ |
||||
text: ci.GetUTF8Text(), |
||||
confidence: ci.Confidence() |
||||
}) |
||||
} while (ci.Next()); |
||||
module.destroy(ci) |
||||
} |
||||
} while (ri.Next(RIL_SYMBOL)); |
||||
module.destroy(ri) |
||||
|
||||
return { |
||||
text: base.GetUTF8Text(), |
||||
html: deindent(base.GetHOCRText()), |
||||
|
||||
confidence: base.MeanTextConf(), |
||||
|
||||
blocks: blocks, |
||||
|
||||
psm: enumToString(base.GetPageSegMode(), 'PSM'), |
||||
oem: enumToString(base.oem(), 'OEM'), |
||||
version: base.Version(), |
||||
} |
||||
} |
@ -1,2 +0,0 @@
@@ -1,2 +0,0 @@
|
||||
const fileSizes = {"afr": 1079573, "ara": 1701536, "aze": 1420865, "bel": 1276820, "ben": 6772012, "bul": 1605615, "cat": 1652368, "ces": 1035441, "chi_sim": 17710414, "chi_tra": 24717749, "chr": 320649, "dan-frak": 677656, "dan": 1972936, "deu-frak": 822644, "deu": 991656, "ell": 859719, "eng": 9453554, "enm": 619254, "epo": 1241212, "equ": 821130, "est": 1905040, "eus": 1641190, "fin": 979418, "fra": 1376221, "frk": 5912963, "frm": 5147082, "glg": 1674938, "grc": 3012615, "heb": 1051501, "hin": 6590065, "hrv": 1926995, "hun": 3074473, "ind": 1874776, "isl": 1634041, "ita": 948593, "ita_old": 3436571, "jpn": 13507168, "kan": 4390317, "kor": 5353098, "lav": 1843944, "lit": 1779240, "mal": 5966263, "meme": 88453, "mkd": 1163087, "mlt": 1463001, "msa": 1665427, "nld": 1134708, "nor": 2191610, "osd": 4274649, "pol": 7024662, "por": 909359, "ron": 915680, "rus": 5969957, "slk-frak": 289885, "slk": 2217342, "slv": 1611338, "spa": 883170, "spa_old": 5647453, "sqi": 1667041, "srp": 1770244, "swa": 757916, "swe": 2451917, "tam": 3498763, "tel": 5795246, "tgl": 1496256, "tha": 3811136, "tur": 3563264, "ukr": 937566, "vie": 2195922} |
||||
export default fileSizes; |
@ -1,3 +0,0 @@
@@ -1,3 +0,0 @@
|
||||
import leveljs from 'level-js' |
||||
var db = typeof indexedDB === 'undefined' ? { open: (_, cb) => cb(true) } : leveljs('./tessdata') |
||||
export default db |
@ -1,53 +0,0 @@
@@ -1,53 +0,0 @@
|
||||
import desaturate from '../shared/desaturate' |
||||
import loadLanguage from './loadLanguage' |
||||
|
||||
export default function detect(jobId, image, cb){ |
||||
var width = image.width, height = image.height; |
||||
image = desaturate(image) |
||||
|
||||
var ptr = self.module.allocate(image, 'i8', self.module.ALLOC_NORMAL); |
||||
// console.log('allocated image')
|
||||
|
||||
loadLanguage(jobId, 'osd', err => { |
||||
self.module._free(ptr); |
||||
cb(err) |
||||
}, success => { |
||||
self.base.Init(null, 'osd') |
||||
self.base.SetPageSegMode(self.module.PSM_OSD_ONLY) |
||||
// console.log('loaded language')
|
||||
|
||||
self.base.SetImage(self.module.wrapPointer(ptr), width, height, 1, width) |
||||
self.base.SetRectangle(0, 0, width, height) |
||||
|
||||
var results = new self.module.OSResults(); |
||||
var success = self.base.DetectOS(results); |
||||
if(!success){ |
||||
self.base.End(); |
||||
self.module._free(ptr); |
||||
cb("failed to detect os") |
||||
} |
||||
else { |
||||
var charset = results.get_unicharset() |
||||
// console.log(charset)
|
||||
// results.print_scores()
|
||||
|
||||
var best = results.get_best_result() |
||||
var oid = best.get_orientation_id(), |
||||
sid = best.get_script_id(); |
||||
// console.log('orientation id', oid, [0, 270, 180, 90][oid], best.get_oconfidence())
|
||||
// console.log('script id', sid, charset.get_script_from_script_id(sid), best.get_sconfidence())
|
||||
// console.log(best)
|
||||
|
||||
cb(null, { |
||||
tesseract_script_id: sid, |
||||
script: charset.get_script_from_script_id(sid), |
||||
script_confidence: best.get_sconfidence(), |
||||
orientation_degrees: [0, 270, 180, 90][oid], |
||||
orientation_confidence: best.get_oconfidence() |
||||
}) |
||||
|
||||
self.base.End(); |
||||
self.module._free(ptr); |
||||
} |
||||
}) |
||||
} |
@ -1,37 +0,0 @@
@@ -1,37 +0,0 @@
|
||||
import recognize from './recognize' |
||||
import detect from './detect' |
||||
|
||||
var module, base, jobId |
||||
|
||||
onmessage = function(e) { |
||||
var {action, args} = e.data; |
||||
jobId = e.data.jobId |
||||
|
||||
console.log('worker got action', action) |
||||
|
||||
if(action == 'init'){ |
||||
|
||||
self.langUrl = args.langUrl |
||||
self.module = TesseractCore({ |
||||
TOTAL_MEMORY: args.mem, //must be a multiple of 10 megabytes
|
||||
TesseractProgress(percent){ |
||||
postMessage({ jobId, |
||||
'progress': { |
||||
'recognized': Math.max(0,(percent-30)/70) |
||||
} |
||||
}) |
||||
}, |
||||
onRuntimeInitialized() {} |
||||
}) |
||||
self.module.FS_createPath("/","tessdata",true,true) |
||||
self.base = new self.module.TessBaseAPI() |
||||
|
||||
} else if(action === 'recognize'){ |
||||
var {image, options} = args |
||||
recognize(jobId, image, options, |
||||
(error, result) => postMessage({jobId, error: error.message, result})) |
||||
} else if(action === 'detect'){ |
||||
detect(jobId, args.image, |
||||
(error, result) => postMessage({jobId, error: error.message, result})) |
||||
} |
||||
} |
@ -1,99 +0,0 @@
@@ -1,99 +0,0 @@
|
||||
import {ungzip} from 'pako' |
||||
import db from './db' |
||||
import fileSizes from '../shared/fileSizes' |
||||
|
||||
function getLanguageData(lang, progress, cb){ |
||||
var xhr = new XMLHttpRequest(); |
||||
xhr.responseType = 'arraybuffer'; |
||||
xhr.open('GET', self.langUrl + lang + '.traineddata.gz', true); |
||||
xhr.onerror = e => { |
||||
xhr.onprogress = xhr.onload = null |
||||
cb(xhr, null) |
||||
} |
||||
xhr.onprogress = e => progress({ |
||||
'loaded_lang_model': e.loaded/fileSizes[lang], //this is kinda wrong on safari
|
||||
cached: false |
||||
}) |
||||
xhr.onload = e => { |
||||
if (!(xhr.status == 200 || (xhr.status == 0 && xhr.response))) return cb(xhr, null); |
||||
|
||||
progress({'unzipping_lang_model': true}) |
||||
|
||||
var response = new Uint8Array(xhr.response) |
||||
while(response[0] == 0x1f && response[1] == 0x8b) response = ungzip(response); |
||||
|
||||
progress({ |
||||
'unzipped_lang_model': true, |
||||
'lang_model_size': response.length |
||||
}) |
||||
|
||||
cb(null, response) |
||||
} |
||||
|
||||
progress({ |
||||
'loaded_lang_model': 0, |
||||
cached: false, |
||||
requesting: true |
||||
}) |
||||
|
||||
xhr.send() |
||||
} |
||||
|
||||
|
||||
function load(lang, jobId, cb){ |
||||
|
||||
console.log('loadLanguage jobId', jobId) |
||||
|
||||
function progressMessage(progress){ |
||||
postMessage({ jobId, progress }) |
||||
} |
||||
|
||||
function finish(err, data) { |
||||
if(err) return cb(err); |
||||
// loaded_langs.push(lang)
|
||||
cb(null, data) |
||||
} |
||||
|
||||
function createDataFile(err, data){ |
||||
progressMessage({ created_virtual_datafile: true}) |
||||
finish(err, data) |
||||
} |
||||
|
||||
function createDataFileCached(err, data) { |
||||
if(err) return createDataFile(err); |
||||
|
||||
db.put(lang, data, err => console.log('cached', lang, err)) |
||||
progressMessage({cached_lang: lang}) |
||||
createDataFile(null, data) |
||||
} |
||||
|
||||
|
||||
db.open({compression: false}, err => { |
||||
if (err) return getLanguageData(lang, progressMessage, createDataFile); |
||||
|
||||
db.get(lang, (err, data) => { |
||||
|
||||
if (err) return getLanguageData(lang, progressMessage, createDataFileCached) |
||||
|
||||
while(data[0] == 0x1f && data[1] == 0x8b) data = ungzip(data); |
||||
|
||||
progressMessage({ loaded_lang_model: lang, from_cache: true }) |
||||
|
||||
cb(null, data) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
var loaded_langs = [] |
||||
|
||||
export default function loadLanguage(jobId, lang, error, success){ |
||||
if(loaded_langs.indexOf(lang) == -1) load(lang, jobId, function(err, result){ |
||||
if(err) return error(err) |
||||
|
||||
loaded_langs.push(lang) |
||||
self.module.FS_createDataFile('tessdata', lang +".traineddata", result, true, false); |
||||
|
||||
success() |
||||
}) |
||||
else success(); |
||||
} |
@ -1,56 +0,0 @@
@@ -1,56 +0,0 @@
|
||||
import desaturate from '../shared/desaturate' |
||||
import loadLanguage from './loadLanguage' |
||||
import circularize from '../shared/circularize' |
||||
import dump from '../shared/dump' |
||||
|
||||
var loaded_langs = [] |
||||
|
||||
export default function recognize(jobId, image, options, cb){ |
||||
|
||||
console.log('recognize id', jobId) |
||||
var {lang} = options |
||||
var width = image.width, height = image.height; |
||||
|
||||
image = desaturate(image) |
||||
|
||||
var ptr = self.module.allocate(image, 'i8', self.module.ALLOC_NORMAL); |
||||
|
||||
loadLanguage(jobId, lang, err => { |
||||
self.module._free(ptr) |
||||
cb(err) |
||||
}, success => { |
||||
self.base.Init(null, lang) |
||||
|
||||
postMessage({ |
||||
jobId, |
||||
'progress': { |
||||
'initialized_with_lang': lang |
||||
} |
||||
}) |
||||
|
||||
for (var option in options) { |
||||
if (options.hasOwnProperty(option)) { |
||||
self.base.SetVariable(option, options[option]); |
||||
postMessage({ |
||||
jobId: jobId, |
||||
'progress': { |
||||
'set_variable': { |
||||
variable: option, |
||||
value: options[option] |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
|
||||
self.base.SetImage(self.module.wrapPointer(ptr), width, height, 1, width) |
||||
self.base.SetRectangle(0, 0, width, height) |
||||
// self.base.GetUTF8Text()
|
||||
self.base.Recognize(null) |
||||
var everything = circularize(dump()) |
||||
self.base.End(); |
||||
self.module._free(ptr); |
||||
cb(null, everything) |
||||
}) |
||||
} |
@ -1,42 +0,0 @@
@@ -1,42 +0,0 @@
|
||||
var path = require('path'); |
||||
var webpack = require('webpack'); |
||||
|
||||
function config(opt) { |
||||
return { |
||||
devtool: 'cheap-module-eval-source-map', |
||||
entry: opt.entry, |
||||
output: Object.assign({}, opt.output, { |
||||
path: path.join(__dirname, 'build'), |
||||
publicPath: '/tesseract/', |
||||
}), |
||||
plugins: [ |
||||
new webpack.NoErrorsPlugin() |
||||
], |
||||
module: { |
||||
loaders: [{ |
||||
test: /\.js$/, |
||||
loaders: ['babel'], |
||||
include: opt.include |
||||
}] |
||||
}, |
||||
node: { |
||||
fs: "empty" |
||||
} |
||||
} |
||||
} |
||||
|
||||
module.exports = [{ |
||||
entry: './src/browser/index.js', |
||||
output: { |
||||
filename: 'tesseract.js', |
||||
library: "Tesseract", |
||||
libraryTarget: "umd" |
||||
}, |
||||
include: [path.join(__dirname, 'src/browser'), path.join(__dirname, 'src/shared')] |
||||
}, { |
||||
entry: './src/worker/index.js', |
||||
output: { |
||||
filename: 'tesseract.worker.js', |
||||
}, |
||||
include: [path.join(__dirname, 'src/worker'), path.join(__dirname, 'src/shared')] |
||||
}].map(config); |
@ -1,46 +0,0 @@
@@ -1,46 +0,0 @@
|
||||
var path = require('path'); |
||||
var webpack = require('webpack'); |
||||
|
||||
function config({entry, output, include}) { |
||||
return { |
||||
entry, |
||||
output: Object.assign({}, output, { |
||||
path: path.join(__dirname, 'dist') |
||||
}), |
||||
plugins: [ |
||||
new webpack.optimize.OccurenceOrderPlugin(), |
||||
new webpack.optimize.DedupePlugin(), |
||||
new webpack.optimize.UglifyJsPlugin({ |
||||
compressor: { |
||||
warnings: false |
||||
} |
||||
}) |
||||
], |
||||
module: { |
||||
loaders: [{ |
||||
test: /\.js$/, |
||||
loaders: ['babel'], |
||||
include |
||||
}] |
||||
}, |
||||
node: { |
||||
fs: "empty" |
||||
} |
||||
} |
||||
} |
||||
|
||||
module.exports = [{ |
||||
entry: './src/browser/index.js', |
||||
output: { |
||||
filename: 'tesseract.js', |
||||
library: "Tesseract", |
||||
libraryTarget: "umd" |
||||
}, |
||||
include: [path.join(__dirname, 'src/browser')] |
||||
}, { |
||||
entry: './src/worker/index.js', |
||||
output: { |
||||
filename: 'tesseract.worker.js', |
||||
}, |
||||
include: [path.join(__dirname, 'src/worker')] |
||||
}].map(config); |
Loading…
Reference in new issue