* @param {boolean} options.preventBlobURL - do not use a blob: URL for the worker script

/**
 *
 * Tesseract Worker adapter for browser
 *
 * @fileoverview Tesseract Worker adapter for browser
 * @author Kevin Kwok <antimatter15@gmail.com>
 * @author Guillermo Webster <gui@mit.edu>
 * @author Jerome Wu <jeromewus@gmail.com>
 */
const check = require('check-types');
const resolveURL = require('resolve-url');
const axios = require('axios');
const b64toU8Array = require('./b64toU8Array');
const { defaultOptions } = require('../common/options');
const { version } = require('../../package.json');

/**
 * readFromBlobOrFile
 *
 * @name readFromBlobOrFile
 * @function
 * @access private
 * @param {object} blob A blob or file objec to read
 * @param {function} res callback function after reading completes
 */
const readFromBlobOrFile = (blob, res) => {
  const fileReader = new FileReader();
  fileReader.onload = () => {
    res(fileReader.result);
  };
  fileReader.readAsArrayBuffer(blob);
};

/**
 * loadImage
 *
 * @name loadImage
 * @function load image from different source
 * @access private
 * @param {string, object} image - image source, supported formats:
 *   string: URL string, can be relative path
 *   string: base64 image
 *   img HTMLElement: extract image source from src attribute
 *   video HTMLElement: extract image source from poster attribute
 *   canvas HTMLElement: extract image data by converting to Blob
 *   File instance: data from <input type="file" />
 * @returns {array} binary image in array format
 */
const loadImage = (image) => {
  if (check.string(image)) {
    // Base64 Image
    if (/data:image\/([a-zA-Z]*);base64,([^"]*)/.test(image)) {
      return Promise.resolve(b64toU8Array(image.split(',')[1]));
    }
    // Image URL
    return axios.get(resolveURL(image), {
      responseType: 'arraybuffer',
    })
      .then(resp => resp.data);
  }
  if (check.instance(image, HTMLElement)) {
    if (image.tagName === 'IMG') {
      return loadImage(image.src);
    }
    if (image.tagName === 'VIDEO') {
      return loadImage(image.poster);
    }
    if (image.tagName === 'CANVAS') {
      return new Promise((res) => {
        image.toBlob((blob) => {
          readFromBlobOrFile(blob, res);
        });
      });
    }
  }
  if (check.instance(image, File) || check.instance(image, Blob)) {
    return new Promise((res) => {
      readFromBlobOrFile(image, res);
    });
  }
  return Promise.reject();
};

const downloadFile = (path, blob) => {
  if (navigator.msSaveBlob) {
    // IE 10+
    navigator.msSaveBlob(blob, path);
  } else {
    const link = document.createElement('a');
    // Browsers that support HTML5 download attribute
    if (link.download !== undefined) {
      const url = URL.createObjectURL(blob);
      link.setAttribute('href', url);
      link.setAttribute('download', path);
      link.style.visibility = 'hidden';
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    }
  }
};

/*
 * Default options for browser worker
 */
exports.defaultOptions = {
  ...defaultOptions,
  workerPath: (typeof process !== 'undefined' && process.env.TESS_ENV === 'development')
    ? resolveURL(`/dist/worker.dev.js?nocache=${Math.random().toString(36).slice(3)}`)
    : `https://unpkg.com/tesseract.js@v${version}/dist/worker.min.js`,
  /*
   * If browser doesn't support WebAssembly,
   * load ASM version instead
   */
  corePath: `https://unpkg.com/tesseract.js-core@v2.0.0-beta.10/tesseract-core.${typeof WebAssembly === 'object' ? 'wasm' : 'asm'}.js`,
};

/**
 * spawnWorker
 *
 * @name spawnWorker
 * @function create a new Worker in browser
 * @access public
 * @param {object} instance - TesseractWorker instance
 * @param {object} options
 * @param {string} options.workerPath - worker script path
 */
exports.spawnWorker = (instance, { workerPath }) => {
  let worker;
  if (Blob && URL) {
    const blob = new Blob([`importScripts("${workerPath}");`], {
      type: 'application/javascript',
    });
    worker = new Worker(URL.createObjectURL(blob));
  } else {
    worker = new Worker(workerPath);
  }

  worker.onmessage = ({ data }) => {
    if (data.jobId.startsWith('Job')) {
      instance.recv(data);
    } else if (data.jobId.startsWith('Download')) {
      const { path, data: d, type } = data;
      const blob = new Blob([d], { type });
      downloadFile(path, blob);
    }
  };

  return worker;
};

/**
 * terminateWorker
 *
 * @name terminateWorker
 * @function terminate worker
 * @access public
 * @param {object} instance TesseractWorker instance
 */
exports.terminateWorker = (instance) => {
  instance.worker.terminate();
};

/**
 * sendPacket
 *
 * @name sendPacket
 * @function send packet to worker and create a job
 * @access public
 * @param {object} instance TesseractWorker instance
 * @param {object} iPacket data for worker
 */
exports.sendPacket = (instance, iPacket) => {
  const packet = { ...iPacket };
  loadImage(packet.payload.image)
    .then(buf => new Uint8Array(buf))
    .then((img) => {
      packet.payload.image = Array.from(img);
      instance.worker.postMessage(packet);
    });
};


# These are supported funding model platforms

github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: tesseractjs
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
custom: # Replace with a single custom sponsorship URL


/**
 *
 * Tesseract Worker adapter for node
 *
 * @fileoverview Tesseract Worker adapter for node
 * @author Kevin Kwok <antimatter15@gmail.com>
 * @author Guillermo Webster <gui@mit.edu>
 * @author Jerome Wu <jeromewus@gmail.com>
 */
const util = require('util');
const fs = require('fs');
const axios = require('axios');
const isURL = require('is-url');
const { fork } = require('child_process');
const path = require('path');
const b64toU8Array = require('./b64toU8Array');
const { defaultOptions } = require('../common/options');

const readFile = util.promisify(fs.readFile);

/**
 * loadImage
 *
 * @name loadImage
 * @function load image from different source
 * @access public
 * @param {string} image - image source, supported formats:
 *   string: URL string or file path
 *   string: base64 image
 *   buffer: image buffer
 * @returns {array} binary image in array format
 */
const loadImage = (image) => {
  if (isURL(image)) {
    return axios.get(image, {
      responseType: 'arraybuffer',
    })
      .then(resp => resp.data);
  }

  if (/data:image\/([a-zA-Z]*);base64,([^"]*)/.test(image)) {
    return Promise.resolve(b64toU8Array(image.split(',')[1]));
  }

  if (Buffer.isBuffer(image)) {
    return Promise.resolve(image);
  }

  return readFile(image);
};

/*
 * Default options for node worker
 */
exports.defaultOptions = {
  ...defaultOptions,
  workerPath: path.join(__dirname, 'worker.js'),
};

/**
 * spawnWorker
 *
 * @name spawnWorker
 * @function fork a new process in node
 * @access public
 * @param {object} instance - TesseractWorker instance
 * @param {object} options
 * @param {string} options.workerPath - worker script path
 */
exports.spawnWorker = (instance, { workerPath }) => {
  const cp = fork(workerPath);
  cp.on('message', (packet) => {
    instance.recv(packet);
  });
  return cp;
};

/**
 * terminateWorker
 *
 * @name terminateWorker
 * @function kill worker
 * @access public
 * @param {object} instance TesseractWorker instance
 */
exports.terminateWorker = (instance) => {
  instance.worker.kill();
};

/**
 * sendPacket
 *
 * @name sendPacket
 * @function send packet to worker and create a job
 * @access public
 * @param {object} instance TesseractWorker instance
 * @param {object} iPacket data for worker
 */
exports.sendPacket = (instance, iPacket) => {
  const packet = { ...iPacket };
  loadImage(packet.payload.image)
    .then(buf => new Uint8Array(buf))
    .then((img) => {
      packet.payload.image = Array.from(img);
      instance.worker.send(packet);
    });
};


/**
 *
 * Node worker implementation
 *
 * @fileoverview Node worker implementation
 * @author Kevin Kwok <antimatter15@gmail.com>
 * @author Guillermo Webster <gui@mit.edu>
 * @author Jerome Wu <jeromewus@gmail.com>
 */

const check = require('check-types');
const workerUtils = require('../common/workerUtils');
const b64toU8Array = require('./b64toU8Array');

let TesseractCore = null;

/*
 * register message handler
 */
process.on('message', (packet) => {
  workerUtils.dispatchHandlers(packet, obj => process.send(obj));
});

/*
 * getCore is a sync function to load and return
 * TesseractCore.
 */
workerUtils.setAdapter({
  getCore: (corePath, res) => {
    if (check.null(TesseractCore)) {
      res.progress({ status: 'loading tesseract core', progress: 0 });
      TesseractCore = require('tesseract.js-core');
      res.progress({ status: 'loaded tesseract core', progress: 1 });
    }
    return TesseractCore;
  },
  b64toU8Array,
  writeFile: (path, data) => {
    const fs = require('fs');
    fs.writeFile(path, data, (err) => {
      if (err) throw err;
    });
  },
});


exports.spawnWorker = (instance, { workerPath }) => {

exports.spawnWorker = (instance, { workerPath, preventBlobURL }) => {


module.exports = {
  /*
   * OEM = OCR Engine Mode, and there are 5 possible modes.
   *
   * By default tesseract.js uses TESSERACT_LSTM_COMBINED mode, which uses LSTM when possible.
   * If you need to use some tesseract v3 features (like tessedit_char_whitelist),
   * you need to use TESSERACT_ONLY mode.
   *
   */
  OEM: {
    TESSERACT_ONLY: 0,
    LSTM_ONLY: 1,
    TESSERACT_LSTM_COMBINED: 2,
    DEFAULT: 3,
    COUNT: 4,
  },
  /*
   * PSM = Page Segmentation Mode
   */
  PSM: {
    OSD_ONLY: '0',
    AUTO_OSD: '1',
    AUTO_ONLY: '2',
    AUTO: '3',
    SINGLE_COLUMN: '4',
    SINGLE_BLOCK_VERT_TEXT: '5',
    SINGLE_BLOCK: '6',
    SINGLE_LINE: '7',
    SINGLE_WORD: '8',
    SINGLE_CHAR: '9',
    SPARSE_TEXT: '10',
    SPARSE_TEXT_OSD: '11',
    RAW_LINE: '12',
    COUNT: '13',
  },
};


module.exports = {
  module: {
    rules: [
      {
        test: /\.m?js$/,
        // exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              [
                '@babel/preset-env',
                {
                  targets: 'last 2 versions',
                },
              ],
            ],
          },
        },
      },
    ],
  },
};


global.expect = require('expect.js');
global.fs = require('fs');
global.path = require('path');
global.Tesseract = require('../src');


const blob = new Blob([`importScripts("${workerPath}");`], {

worker = new Worker(URL.createObjectURL(blob));


    <meta charset="utf-8">
    <link rel="stylesheet" href="../node_modules/mocha/mocha.css">


<select id="langsel" onchange="window.lastFile && recognizeFile(window.lastFile)">
<option value='afr'     > Afrikaans             </option>
<option value='ara'     > Arabic                </option>
<option value='aze'     > Azerbaijani           </option>
<option value='bel'     > Belarusian            </option>
<option value='ben'     > Bengali               </option>
<option value='bul'     > Bulgarian             </option>
<option value='cat'     > Catalan               </option>
<option value='ces'     > Czech                 </option>
<option value='chi_sim' > Chinese               </option>
<option value='chi_tra' > Traditional Chinese   </option>
<option value='chr'     > Cherokee              </option>
<option value='dan'     > Danish                </option>
<option value='deu'     > German                </option>
<option value='ell'     > Greek                 </option>
<option value='eng'     selected> English                </option>
<option value='enm'     > English (Old)         </option>
<option value='meme'     > Internet Meme                </option>
<option value='epo'     > Esperanto             </option>
<option value='epo_alt' > Esperanto alternative </option>
<option value='equ'     > Math                  </option>
<option value='est'     > Estonian              </option>
<option value='eus'     > Basque                </option>
<option value='fin'     > Finnish               </option>
<option value='fra'     > French                </option>
<option value='frk'     > Frankish              </option>
<option value='frm'     > French (Old)          </option>
<option value='glg'     > Galician              </option>
<option value='grc'     > Ancient Greek         </option>
<option value='heb'     > Hebrew                </option>
<option value='hin'     > Hindi                 </option>
<option value='hrv'     > Croatian              </option>
<option value='hun'     > Hungarian             </option>
<option value='ind'     > Indonesian            </option>
<option value='isl'     > Icelandic             </option>
<option value='ita'     > Italian               </option>
<option value='ita_old' > Italian (Old)         </option>
<option value='jpn'     > Japanese              </option>
<option value='kan'     > Kannada               </option>
<option value='kor'     > Korean                </option>
<option value='lav'     > Latvian               </option>
<option value='lit'     > Lithuanian            </option>
<option value='mal'     > Malayalam             </option>
<option value='mkd'     > Macedonian            </option>
<option value='mlt'     > Maltese               </option>
<option value='msa'     > Malay                 </option>
<option value='nld'     > Dutch                 </option>
<option value='nor'     > Norwegian             </option>
<option value='pol'     > Polish                </option>
<option value='por'     > Portuguese            </option>
<option value='ron'     > Romanian              </option>
<option value='rus'     > Russian               </option>
<option value='slk'     > Slovakian             </option>
<option value='slv'     > Slovenian             </option>
<option value='spa'     > Spanish               </option>
<option value='spa_old' > Old Spanish           </option>
<option value='sqi'     > Albanian              </option>
<option value='srp'     > Serbian (Latin)       </option>
<option value='swa'     > Swahili               </option>
<option value='swe'     > Swedish               </option>
<option value='tam'     > Tamil                 </option>
<option value='tel'     > Telugu                </option>
<option value='tgl'     > Tagalog               </option>
<option value='tha'     > Thai                  </option>
<option value='tur'     > Turkish               </option>
<option value='ukr'     > Ukrainian             </option>
<option value='vie'     > Vietnamese            </option>
</select>

<button onclick="recognizeFile('../../tests/assets/images/simple.png')">Sample Image</button>
<input type="file" onchange="recognizeFile(window.lastFile=this.files[0])">


.DS_Store
node_modules/*
yarn.lock
tesseract.dev.js
worker.dev.js
/*.traineddata
/examples/**/*.traineddata
.nyc_output
dist/


*     Please update this option if you self-host the worker script.

* @param {string} [options.cachePath=.] - @see {@link https://github.com/jeromewu/tesseract.js-utils/blob/master/src/loadLang.js}

* @param {string} [options.cacheMethod=write] - @see {@link https://github.com/jeromewu/tesseract.js-utils/blob/master/src/loadLang.js}

* @param {string} [options.dataPath=.] - @see {@link https://github.com/jeromewu/tesseract.js-utils/blob/master/src/loadLang.js}

* @param {boolean} [options.preventBlobURL=false] - do not use a blob: URL for the worker script


const path = require('path');
const webpack = require('webpack');
const common = require('./webpack.config.common');

const genConfig = ({
  entry, filename, library, libraryTarget,
}) => ({
  ...common,
  mode: 'development',
  entry,
  output: {
    filename,
    library,
    libraryTarget,
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        TESS_ENV: JSON.stringify('development'),
      },
    }),
  ],
  devServer: {
      allowedHosts: ['localhost', '.gitpod.io'],
  },
});

module.exports = [
  genConfig({
    entry: path.resolve(__dirname, '..', 'src', 'index.js'),
    filename: 'tesseract.dev.js',
    library: 'Tesseract',
    libraryTarget: 'umd',
  }),
  genConfig({
    entry: path.resolve(__dirname, '..', 'src', 'browser', 'worker.js'),
    filename: 'worker.dev.js',
  }),
];


const webpack = require('webpack');
const middleware = require('webpack-dev-middleware');
const express = require('express');
const path = require('path');
const cors = require('cors');
const webpackConfig = require('./webpack.config.dev');

const compiler = webpack(webpackConfig);
const app = express();

app.use(cors());
app.use('/', express.static(path.resolve(__dirname, '..')));
app.use(middleware(compiler, { publicPath: '/dist' }));

module.exports = app.listen(3000, () => {
  console.log('Server is running on port 3000');
});


# API

## TesseractWorker.recognize(image, lang, [, options]) -> [TesseractJob](#tesseractjob)
Figures out what words are in `image`, where the words are in `image`, etc.
> Note: `image` should be sufficiently high resolution.
> Often, the same image will get much better results if you upscale it before calling `recognize`.

- `image` see [Image Format](./image-format.md) for more details.
- `lang` property with a value from the [list of lang parameters](./tesseract_lang_list.md), you can use multiple languages separated by '+', ex. `eng+chi_tra`
- `options` a flat json object that may include properties that override some subset of the [default tesseract parameters](./tesseract_parameters.md)

Returns a [TesseractJob](#tesseractjob) whose `then`, `progress`, `catch` and `finally` methods can be used to act on the result.

### Simple Example:
```javascript
const worker = new Tesseract.TesseractWorker();
worker
  .recognize(myImage)
  .then(function(result){
    console.log(result);
  });
```

### More Complicated Example:
```javascript
const worker = new Tesseract.TesseractWorker();
// if we know our image is of spanish words without the letter 'e':
worker
  .recognize(myImage, 'spa', {
    tessedit_char_blacklist: 'e',
  })
  .then(function(result){
    console.log(result);
  });
```

## TesseractWorker.detect(image) -> [TesseractJob](#tesseractjob)

Figures out what script (e.g. 'Latin', 'Chinese') the words in  image are written in.

- `image` see [Image Format](./image-format.md) for more details.

Returns a [TesseractJob](#tesseractjob) whose `then`, `progress`, `catch` and `finally` methods can be used to act on the result of the script.

```javascript
const worker = new Tesseract.TesseractWorker();
worker
  .detect(myImage)
  .then(function(result){
    console.log(result);
  });
```

## TesseractJob

A TesseractJob is an object returned by a call to `recognize` or `detect`. It's inspired by the ES6 Promise interface and provides `then` and `catch` methods. It also provides `finally` method, which will be fired regardless of the job fate. One important difference is that these methods return the job itself (to enable chaining) rather than new.

Typical use is:
```javascript
const worker = new Tesseract.TesseractWorker();
worker.recognize(myImage)
  .progress(message => console.log(message))
  .catch(err => console.error(err))
  .then(result => console.log(result))
  .finally(resultOrError => console.log(resultOrError));
```

Which is equivalent to:
```javascript
const worker = new Tesseract.TesseractWorker();
const job1 = worker.recognize(myImage);

job1.progress(message => console.log(message));

job1.catch(err => console.error(err));

job1.then(result => console.log(result));

job1.finally(resultOrError => console.log(resultOrError));
```

### TesseractJob.progress(callback: function) -> TesseractJob
Sets `callback` as the function that will be called every time the job progresses.
- `callback` is a function with the signature `callback(progress)` where `progress` is a json object.

For example:
```javascript
const worker = new Tesseract.TesseractWorker();
worker.recognize(myImage)
  .progress(function(message){console.log('progress is: ', message)});
```

The console will show something like:
```javascript
progress is: {loaded_lang_model: "eng", from_cache: true}
progress is: {initialized_with_lang: "eng"}
progress is: {set_variable: Object}
progress is: {set_variable: Object}
progress is: {recognized: 0}
progress is: {recognized: 0.3}
progress is: {recognized: 0.6}
progress is: {recognized: 0.9}
progress is: {recognized: 1}
```

### TesseractJob.then(callback: function) -> TesseractJob
Sets `callback` as the function that will be called if and when the job successfully completes.
- `callback` is a function with the signature `callback(result)` where `result` is a json object.

For example:
```javascript
const worker = new Tesseract.TesseractWorker();
worker.recognize(myImage)
  .then(function(result){console.log('result is: ', result)});
```

The console will show something like:
```javascript
result is: {
    blocks: Array[1]
    confidence: 87
    html: "<div class='ocr_page' id='page_1' ..."
    lines: Array[3]
    oem: "DEFAULT"
    paragraphs: Array[1]
    psm: "SINGLE_BLOCK"
    symbols: Array[33]
    text: "Hello World↵from beyond↵the Cosmic Void↵↵"
    version: "3.04.00"
    words: Array[7]
}
```

### TesseractJob.catch(callback: function) -> TesseractJob
Sets `callback` as the function that will be called if the job fails.
- `callback` is a function with the signature `callback(error)` where `error` is a json object.

### TesseractJob.finally(callback: function) -> TesseractJob
Sets `callback` as the function that will be called regardless if the job fails or success.
- `callback` is a function with the signature `callback(resultOrError)` where `resultOrError` is a json object.


Tesseract.js Parameters
=======================

In the 3rd argument of `TesseractWorker.recognize()`, you can pass a params object to customize the result of OCR, below are supported parameters in tesseract.js so far.

Example:

```javascript
import Tesseract from 'tesseract.js';

const { TesseractWorker, OEM, PSM } = Tesseract;
const worker = new TesseractWorker();

worker
  .recognize(image, 'eng', {
    tessedit_ocr_engine_mode: OEM.LSTM_ONLY,
    tessedit_pageseg_mode: PSM.SINGLE_BLOCK,
  })
  .then(result => console.log(result.text));
```

| name | type | default value | description |
| ---- | ---- | ------------- | ----------- |
| tessedit\_ocr\_engine\_mode | enum | OEM.LSTM\_ONLY | Check [HERE](https://github.com/tesseract-ocr/tesseract/blob/4.0.0/src/ccstruct/publictypes.h#L268) for definition of each mode |
| tessedit\_pageseg\_mode | enum | PSM.SINGLE\_BLOCK | Check [HERE](https://github.com/tesseract-ocr/tesseract/blob/4.0.0/src/ccstruct/publictypes.h#L163) for definition of each mode |
| tessedit\_char\_whitelist | string | '' | setting white list characters makes the result only contains these characters, useful the content in image is limited |
| tessjs\_create\_pdf | string | '0' | only 2 values, '0' or '1', when the value is '1', tesseract.js generates a pdf output |
| tessjs\_create\_hocr | string | '1' | only 2 values, '0' or '1', when the value is '1', tesseract.js includes hocr in the result |
| tessjs\_create\_tsv | string | '1' | only 2 values, '0' or '1', when the value is '1', tesseract.js includes tsv in the result |
| tessjs\_create\_box | string | '0' | only 2 values, '0' or '1', when the value is '1', tesseract.js includes box in the result |
| tessjs\_create\_unlv | string | '0' | only 2 values, '0' or '1', when the value is '1', tesseract.js includes unlv in the result |
| tessjs\_create\_osd | string | '0' | only 2 values, '0' or '1', when the value is '1', tesseract.js includes osd in the result |
| tessjs\_pdf\_name | string | 'tesseract.js-ocr-result' | the name of the generated pdf file |
| tessjs\_pdf\_title | string | 'Tesseract.js OCR Result' | the title of the generated pdf file |
| tessjs\_pdf\_auto\_download | boolean | true | If the value is true, tesseract.js will automatic download/writeFile pdf file |
| tessjs\_pdf\_bin | boolean | false | whether to include pdf binary array in the result object (result.files.pdf) |
| tessjs\_image\_rectangle\_left | number | 0 | The left of the sub-rectangle of the image. |
| tessjs\_image\_rectangle\_top | number | 0 | The top of the sub-rectangle of the image. |
| tessjs\_image\_rectangle\_width | number | -1 | The width of the sub-rectangle of the image, -1 means auto width detection |
| tessjs\_image\_rectangle\_height | number | -1 | The height of the sub-rectangle of the image, -1 means auto height detection |