libs/bidiEngine.js

/**
* Unicode Bidi Engine based on the work of Alex Shensis (@asthensis)
* MIT License
*/

(function (jsPDF) {
    "use strict";
/**
 * Table of Unicode types.
 *
 * Generated by:
 *
 * var bidi = require("./bidi/index");
 * var bidi_accumulate = bidi.slice(0, 256).concat(bidi.slice(0x0500, 0x0500 + 256 * 3)).
 * concat(bidi.slice(0x2000, 0x2000 + 256)).concat(bidi.slice(0xFB00, 0xFB00 + 256)).
 * concat(bidi.slice(0xFE00, 0xFE00 + 2 * 256));
 *
 * for( var i = 0; i < bidi_accumulate.length; i++) {
 * 	if(bidi_accumulate[i] === undefined || bidi_accumulate[i] === 'ON')
 * 		bidi_accumulate[i] = 'N'; //mark as neutral to conserve space and substitute undefined
 * }
 * var bidiAccumulateStr = 'return [ "' + bidi_accumulate.toString().replace(/,/g, '", "') + '" ];';
 * require("fs").writeFile('unicode-types.js', bidiAccumulateStr);
 *
 * Based on:
 * https://github.com/mathiasbynens/unicode-8.0.0
 */
var bidiUnicodeTypes = [ "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "S", "B", "S", "WS", "B", "BN",
		"BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "B", "B",
		"B", "S", "WS", "N", "N", "ET", "ET", "ET", "N", "N", "N", "N", "N", "ES", "CS", "ES",
		"CS", "CS", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "CS", "N", "N",
		"N", "N", "N", "N", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "N", "N", "N", "N",
		"N", "N", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "N", "N", "N", "N", "BN", "BN",
		"BN", "BN", "BN", "BN", "B", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN",
		"BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN",
		"BN", "CS", "N", "ET", "ET", "ET", "ET", "N", "N", "N", "N", "L", "N", "N", "BN", "N",
		"N", "ET", "ET", "EN", "EN", "N", "L", "N", "N", "N", "EN", "L", "N", "N", "N", "N", "N",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "N", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "N", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "N", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "N", "N", "L", "L", "L", "L", "L", "L", "L", "N", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "N", "L", "N", "N", "N", "N", "N", "ET", "N", "NSM", "NSM", "NSM", "NSM", "NSM",
		"NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM",
		"NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM",
		"NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM",
		"NSM", "R", "NSM", "R", "NSM", "NSM", "R", "NSM", "NSM", "R", "NSM", "N", "N", "N", "N",
		"N", "N", "N", "N", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R",
		"R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "N", "N", "N", "N", "N",
		"R", "R", "R", "R", "R", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "AN",
		"AN", "AN", "AN", "AN", "AN", "N", "N", "AL", "ET", "ET", "AL", "CS", "AL", "N", "N",
		"NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL",
		"N", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM",
		"NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AN", "AN", "AN", "AN",
		"AN", "AN", "AN", "AN", "AN", "AN", "ET", "AN", "AN", "AL", "AL", "AL", "NSM", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AN", "N", "NSM", "NSM",
		"NSM", "NSM", "NSM", "NSM", "AL", "AL", "NSM", "NSM", "N", "NSM", "NSM", "NSM", "NSM",
		"AL", "AL", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "N", "AL", "AL", "NSM", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM",
		"NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM",
		"NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "N", "N", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM",
		"AL", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "R", "R",
		"R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R",
		"R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R",
		"R", "R", "R", "R", "R", "R", "R", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM",
		"NSM", "NSM", "R", "R", "N", "N", "N", "N", "R", "N", "N", "N", "N", "N", "WS", "WS",
		"WS", "WS", "WS", "WS", "WS", "WS", "WS", "WS", "WS", "BN", "BN", "BN", "L", "R", "N",
		"N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N",
		"N", "N", "N", "N", "N", "N", "WS", "B", "LRE", "RLE", "PDF", "LRO", "RLO", "CS", "ET",
		"ET", "ET", "ET", "ET", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N",
		"N", "N", "CS", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N",
		"N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "WS", "BN", "BN", "BN", "BN", "BN",
		"N", "LRI", "RLI", "FSI", "PDI", "BN", "BN", "BN", "BN", "BN", "BN", "EN", "L", "N", "N",
		"EN", "EN", "EN", "EN", "EN", "EN", "ES", "ES", "N", "N", "N", "L", "EN", "EN", "EN", "EN",
		"EN", "EN", "EN", "EN", "EN", "EN", "ES", "ES", "N", "N", "N", "N", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "N", "N", "N", "ET", "ET", "ET", "ET", "ET",
		"ET", "ET", "ET", "ET", "ET", "ET", "ET", "ET", "ET", "ET", "ET", "ET", "ET", "ET", "ET",
		"ET", "ET", "ET", "ET", "ET", "ET", "ET", "ET", "ET", "ET", "ET", "N", "N", "N", "N", "N",
		"N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "NSM", "NSM", "NSM", "NSM",
		"NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM",
		"NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM",
		"NSM", "NSM", "NSM", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N",
		"N", "L", "L", "L", "L", "L", "L", "L", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N",
		"N", "N", "L", "L", "L", "L", "L", "N", "N", "N", "N", "N", "R", "NSM", "R", "R", "R",
		"R", "R", "R", "R", "R", "R", "R", "ES", "R", "R", "R", "R", "R", "R", "R", "R", "R",
		"R", "R", "R", "R", "N", "R", "R", "R", "R", "R", "N", "R", "N", "R", "R", "N", "R", "R",
		"N", "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N",
		"N", "N", "N", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM",
		"NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "N", "N", "N", "N", "N", "N", "N", "N", "N",
		"N", "N", "N", "N", "N", "N", "N", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM",
		"NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "N", "N", "N", "N", "N", "N", "N",
		"N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N",
		"N", "N", "N", "N", "N", "N", "N", "CS", "N", "CS", "N", "N", "CS", "N", "N", "N", "N",
		"N", "N", "N", "N", "N", "ET", "N", "N", "ES", "ES", "N", "N", "N", "N", "N", "ET", "ET",
		"N", "N", "N", "N", "N", "AL", "AL", "AL", "AL", "AL", "N", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL",
		"AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "N", "N", "BN", "N", "N", "N",
		"ET", "ET", "ET", "N", "N", "N", "N", "N", "ES", "CS", "ES", "CS", "CS", "EN", "EN", "EN",
		"EN", "EN", "EN", "EN", "EN", "EN", "EN", "CS", "N", "N", "N", "N", "N", "N", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "N", "N", "N", "N", "N", "N", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L",
		"L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "N", "N", "N", "L", "L",
		"L", "L", "L", "L", "N", "N", "L", "L", "L", "L", "L", "L", "N", "N", "L", "L", "L", "L",
		"L", "L", "N", "N", "L", "L", "L", "N", "N", "N", "ET", "ET", "N", "N", "N", "ET", "ET",
		"N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N",
		"N", "N", "N", "N", "N", "N", "N" ];
		
		
/**
 * Unicode Bidi algorithm compliant Bidi engine.
 * For reference see http://unicode.org/reports/tr9/
*/

/**
 * constructor ( options )
 *
 * Initializes Bidi engine
 *
 * @param {Object} See 'setOptions' below for detailed description.
 * options are cashed between invocation of 'doBidiReorder' method
 *
 * sample usage pattern of BidiEngine:
 * var opt = {
 * 	isInputVisual: true,
 * 	isInputRtl: false,
 * 	isOutputVisual: false,
 * 	isOutputRtl: false,
 * 	isSymmetricSwapping: true
 * }
 * var sourceToTarget = [], levels = [];
 * var bidiEng = Globalize.bidiEngine(opt);
 * var src = "text string to be reordered";
 * var ret = bidiEng.doBidiReorder(src, sourceToTarget, levels);
 */

jsPDF.__bidiEngine__ = jsPDF.prototype.__bidiEngine__ = function( options ) {

	var _UNICODE_TYPES = _bidiUnicodeTypes;

	var _STATE_TABLE_LTR = [
		[ 0, 3, 0, 1, 0, 0, 0 ],
		[ 0, 3, 0, 1, 2, 2, 0 ],
		[ 0, 3, 0, 0x11, 2, 0, 1 ],
		[ 0, 3, 5, 5, 4, 1, 0 ],
		[ 0, 3, 0x15, 0x15, 4, 0, 1 ],
		[ 0, 3, 5, 5, 4, 2, 0 ] ];

	var _STATE_TABLE_RTL = [
		[ 2, 0, 1, 1, 0, 1, 0 ],
		[ 2, 0, 1, 1, 0, 2, 0 ],
		[ 2, 0, 2, 1, 3, 2, 0 ],
		[ 2, 0, 2, 0x21, 3, 1, 1 ] ];

	var _TYPE_NAMES_MAP = { "L": 0, "R": 1, "EN": 2, "AN": 3, "N": 4, "B": 5, "S": 6 };

	var _UNICODE_RANGES_MAP = { 0: 0, 5: 1, 6: 2, 7: 3, 0x20: 4, 0xFB: 5, 0xFE: 6, 0xFF: 7 };

	var _SWAP_TABLE = [
		"\u0028", "\u0029", "\u0028",
		"\u003C", "\u003E", "\u003C",
		"\u005B", "\u005D", "\u005B",
		"\u007B", "\u007D", "\u007B",
		"\u00AB", "\u00BB", "\u00AB",
		"\u2039", "\u203A", "\u2039",
		"\u2045", "\u2046", "\u2045",
		"\u207D", "\u207E", "\u207D",
		"\u208D", "\u208E", "\u208D",
		"\u2264", "\u2265", "\u2264",
		"\u2329", "\u232A", "\u2329",
		"\uFE59", "\uFE5A", "\uFE59",
		"\uFE5B", "\uFE5C", "\uFE5B",
		"\uFE5D", "\uFE5E", "\uFE5D",
		"\uFE64", "\uFE65", "\uFE64" ];

	var _LTR_RANGES_REG_EXPR = new RegExp( /^([1-4|9]|1[0-9]|2[0-9]|3[0168]|4[04589]|5[012]|7[78]|159|16[0-9]|17[0-2]|21[569]|22[03489]|250)$/ );

	var _lastArabic = false, _hasUbatAl, _hasUbatB, _hasUbatS, DIR_LTR = 0, DIR_RTL = 1,
		_isInVisual, _isInRtl, _isOutVisual, _isOutRtl, _isSymmetricSwapping, _dir = DIR_LTR;

	this.__bidiEngine__ = {};

	var _init = function( text, sourceToTargetMap ) {
		if ( sourceToTargetMap ) {
			for ( var i = 0; i < text.length; i++ ) {
				sourceToTargetMap[ i ] = i;
			}
		}
		if ( _isInRtl === undefined ) {
			_isInRtl = _isContextualDirRtl( text );
		}
		if ( _isOutRtl === undefined ) {
			_isOutRtl = _isContextualDirRtl( text );
		}
	};

	// for reference see 3.2 in http://unicode.org/reports/tr9/
	//
	var _getCharType = function( ch ) {
		var charCode = ch.charCodeAt(), range = charCode >> 8,
			rangeIdx = _UNICODE_RANGES_MAP[ range ];

		if ( rangeIdx !== undefined ) {
			return _UNICODE_TYPES[ ( rangeIdx * 256 ) + ( charCode & 0xFF ) ];
		} else if ( range === 0xFC || range === 0xFD ) {
			return "AL";
		} else if ( _LTR_RANGES_REG_EXPR.test( range ) ) { //unlikely case
			return "L";
		}  else if ( range === 8 ) { // even less likely
			return "R";
		}
		return "N"; //undefined type, mark as neutral
	};

	var _isContextualDirRtl = function( text ) {
		for ( var i = 0, charType; i < text.length; i++ ) {
			charType = _getCharType( text.charAt( i ) );
			if ( charType === "L" ) {
				return false;
			} else if ( charType === "R" ) {
				return true;
			}
		}
		return false;
	};

	// for reference see 3.3.4 & 3.3.5 in http://unicode.org/reports/tr9/
	//
	var _resolveCharType = function( chars, types, resolvedTypes, index ) {
		var cType = types[ index ], wType, nType, i, len;
		switch ( cType ) {
			case "L":
			case "R":
				_lastArabic = false;
				break;
			case "N":
			case "AN":
				break;

			case "EN":
				if ( _lastArabic ) {
					cType = "AN";
				}
				break;

			case "AL":
				_lastArabic = true;
				_hasUbatAl = true;
				cType = "R";
				break;

			case "WS":
				cType = "N";
				break;

			case "CS":
				if ( index < 1 || ( index + 1 ) >= types.length ||
					( ( wType = resolvedTypes[ index - 1 ] ) !== "EN" && wType !== "AN" ) ||
					( ( nType = types[ index + 1 ] ) !== "EN" && nType !== "AN" ) ) {
					cType = "N";
				} else if ( _lastArabic ) {
					nType = "AN";
				}
				cType = ( nType === wType ) ? nType : "N";
				break;

			case "ES":
				wType = index > 0 ? resolvedTypes[ index - 1 ] : "B";
				cType = (( wType === "EN" ) && ( index + 1 < types.length ) &&
					( types[ index + 1 ] === "EN" )) ? "EN" : "N";
				break;

			case "ET":
				if ( index > 0 && resolvedTypes[ index - 1 ] === "EN" ) {
					cType = "EN";
					break;
				} else if ( _lastArabic ) {
					cType = "N";
					break;
				}
				i = index + 1;
				len = types.length;
				while ( i < len && types[ i ] === "ET" ) {
					i++;
				}
				if ( i < len && types[ i ] === "EN" ) {
					cType = "EN";
				} else {
					cType = "N";
				}
				break;

			case "NSM":
				if ( _isInVisual && !_isInRtl ) { //V->L
					len = types.length;
					i = index + 1;
					while ( i < len && types[ i ] === "NSM" ) {
						i++;
					}
					if ( i < len ) {
						var c = chars[ index ];
						var rtlCandidate = ( c >= 0x0591 && c <= 0x08FF ) || c === 0xFB1E;
						wType = types[ i ];
						if ( rtlCandidate && ( wType === "R" || wType === "AL" ) ) {
							cType = "R";
							break;
						}
					}
				}
				if ( index < 1 || ( wType = types[ index - 1 ] ) === "B" ) {
					cType = "N";
				} else {
					cType = resolvedTypes[ index - 1 ];
				}
				break;

			case "B":
				_lastArabic = false;
				_hasUbatB = true;
				cType = _dir;
				break;

			case "S":
				_hasUbatS = true;
				cType = "N";
				break;

			case "LRE":
			case "RLE":
			case "LRO":
			case "RLO":
			case "PDF":
				_lastArabic = false;
				break;
			case "BN":
				cType = "N";
				break;
		}
		return cType;
	};

	var _handleUbatS = function( types, levels, length ) {
		for ( var i = 0; i < length; i++ ) {
			if ( types[ i ] === "S" ) {
				levels[ i ] = _dir;
				for ( var j = i - 1; j >= 0; j-- ) {
					if ( types[ j ] === "WS" ) {
						levels[ j ] = _dir;
					} else {
						break;
					}
				}
			}
		}
	};

	var _invertString = function( text, sourceToTargetMap, levels ) {
		var charArray = text.split( "" );
		if ( levels ) {
			_computeLevels( charArray, levels, { hiLevel: _dir } );
		}
		charArray.reverse();
		sourceToTargetMap && sourceToTargetMap.reverse();
		return charArray.join( "" );
	};

	// For reference see 3.3 in http://unicode.org/reports/tr9/
	//
	var _computeLevels = function( chars, levels, params ) {
		var action, condition, i, index, newLevel, prevState,
			condPos = -1,
			len = chars.length,
			newState = 0,
			resolvedTypes = [],
			stateTable = _dir ? _STATE_TABLE_RTL : _STATE_TABLE_LTR,
			types = [];

		_lastArabic = false;
		_hasUbatAl = false;
		_hasUbatB = false;
		_hasUbatS = false;
		for ( i = 0; i < len; i++ ) {
			types[ i ] = _getCharType( chars[ i ] );
		}
		for ( index = 0; index < len; index++ ) {
			prevState = newState;
			resolvedTypes[ index ] = _resolveCharType( chars, types, resolvedTypes, index );
			newState = stateTable[ prevState ][ _TYPE_NAMES_MAP[ resolvedTypes [ index ] ] ];
			action = newState & 0xF0;
			newState &= 0x0F;
			levels[ index ] = newLevel = stateTable[ newState ][ 5 ];
			if ( action > 0 ) {
				if ( action === 0x10 ) {
					for ( i = condPos; i < index; i++ ) {
						levels[ i ] = 1;
					}
					condPos = -1;
				} else {
					condPos = -1;
				}
			}
			condition = stateTable[ newState ][ 6 ];
			if ( condition ) {
				if ( condPos === -1 ) {
					condPos = index;
				}
			} else {
				if ( condPos > -1 ) {
					for ( i = condPos; i < index; i++ ) {
						levels[ i ] = newLevel;
					}
					condPos = -1;
				}
			}
			if ( types[ index ] === "B" ) {
				levels[ index ] = 0;
			}
			params.hiLevel |= newLevel;
		}
		if ( _hasUbatS ) {
			_handleUbatS( types, levels, len );
		}
	};

	// for reference see 3.4 in http://unicode.org/reports/tr9/
	//
	var _invertByLevel = function( level, charArray, sourceToTargetMap, levels, params ) {
		if ( params.hiLevel < level ) {
			return;
		}
		if ( level === 1 && _dir === DIR_RTL && !_hasUbatB ) {
			charArray.reverse();
			sourceToTargetMap && sourceToTargetMap.reverse();
			return;
		}
		var ch, high, end, low,
			len = charArray.length,
			start = 0;

		while ( start < len ) {
			if ( levels[ start ] >= level ) {
				end = start + 1;
				while ( end < len && levels[ end ] >= level ) {
					end++;
				}
				for ( low = start, high = end - 1 ; low < high; low++, high-- ) {
					ch = charArray[ low ];
					charArray[ low ] = charArray[ high ];
					charArray[ high ] = ch;
					if ( sourceToTargetMap ) {
						ch = sourceToTargetMap[ low ];
						sourceToTargetMap[ low ] = sourceToTargetMap[ high ];
						sourceToTargetMap[ high ] = ch;
					}
				}
				start = end;
			}
			start++;
		}
	};

	// for reference see 7 & BD16 in http://unicode.org/reports/tr9/
	//
	var _symmetricSwap = function( charArray, levels, params ) {
		if ( params.hiLevel !== 0 && _isSymmetricSwapping ) {
			for ( var i = 0, index; i < charArray.length; i++ ) {
				if ( levels[ i ] === 1 ) {
					index = _SWAP_TABLE.indexOf( charArray[ i ] );
					if ( index >= 0 ) {
						charArray[ i ] = _SWAP_TABLE[ index + 1 ];
					}
				}
			}
		}
	};

	var _reorder = function( text, sourceToTargetMap, levels ) {
		var charArray = text.split( "" ),
			params = { hiLevel: _dir };

		if ( !levels ) {
			levels = [];
		}
		_computeLevels( charArray, levels, params );
		_symmetricSwap( charArray, levels, params );
		_invertByLevel( DIR_RTL + 1, charArray, sourceToTargetMap, levels, params );
		_invertByLevel( DIR_RTL, charArray, sourceToTargetMap, levels, params );
		return charArray.join( "" );
	};

	// doBidiReorder( text, sourceToTargetMap, levels )
	// Performs Bidi reordering by implementing Unicode Bidi algorithm.
	// Returns reordered string
	// @text [String]:
	// - input string to be reordered, this is input parameter
	// $sourceToTargetMap [Array] (optional)
	// - resultant mapping between input and output strings, this is output parameter
	// $levels [Array] (optional)
	// - array of calculated Bidi levels, , this is output parameter
	this.__bidiEngine__.doBidiReorder = function( text, sourceToTargetMap, levels ) {
		_init( text, sourceToTargetMap );
		if ( !_isInVisual && _isOutVisual && !_isOutRtl ) {

			// LLTR->VLTR, LRTL->VLTR
			_dir = _isInRtl ? DIR_RTL : DIR_LTR;
			text = _reorder( text, sourceToTargetMap, levels );
		} else if ( _isInVisual && _isOutVisual && ( _isInRtl ^ _isOutRtl ) ) {

			// VRTL->VLTR, VLTR->VRTL
			_dir = _isInRtl ? DIR_RTL : DIR_LTR;
			text = _invertString( text, sourceToTargetMap, levels );
		} else if ( !_isInVisual && _isOutVisual && _isOutRtl ) {

			// LLTR->VRTL, LRTL->VRTL
			_dir = _isInRtl ? DIR_RTL : DIR_LTR;
			text = _reorder( text, sourceToTargetMap, levels );
			text = _invertString( text, sourceToTargetMap );
		} else if ( _isInVisual && !_isInRtl && !_isOutVisual && !_isOutRtl ) {

			// VLTR->LLTR
			_dir = DIR_LTR;
			text = _reorder( text, sourceToTargetMap, levels );
		} else if ( _isInVisual && !_isOutVisual && ( _isInRtl ^ _isOutRtl ) ) {

			// VLTR->LRTL, VRTL->LLTR
			text = _invertString( text, sourceToTargetMap );
			if ( _isInRtl ) { //LLTR -> VLTR
				_dir = DIR_LTR;
				text = _reorder( text, sourceToTargetMap, levels );
			} else { //LRTL -> VRTL
				_dir = DIR_RTL;
				text = _reorder( text, sourceToTargetMap, levels );
				text = _invertString( text, sourceToTargetMap );
			}
		} else if ( _isInVisual && _isInRtl && !_isOutVisual && _isOutRtl ) {

			//  VRTL->LRTL
			_dir = DIR_RTL;
			text = _reorder( text, sourceToTargetMap, levels );
			text = _invertString( text, sourceToTargetMap );
		} else if ( !_isInVisual && !_isOutVisual && ( _isInRtl ^ _isOutRtl ) ) {

			// LRTL->LLTR, LLTR->LRTL
			var isSymmetricSwappingOrig = _isSymmetricSwapping;
			if ( _isInRtl ) { //LRTL->LLTR
				_dir = DIR_RTL;
				text = _reorder( text, sourceToTargetMap, levels );
				_dir = DIR_LTR;
				_isSymmetricSwapping = false;
				text = _reorder( text, sourceToTargetMap, levels );
				_isSymmetricSwapping = isSymmetricSwappingOrig;
			} else { //LLTR->LRTL
				_dir = DIR_LTR;
				text = _reorder( text, sourceToTargetMap, levels );
				text = _invertString( text, sourceToTargetMap );
				_dir = DIR_RTL;
				_isSymmetricSwapping = false;
				text = _reorder( text, sourceToTargetMap, levels );
				_isSymmetricSwapping = isSymmetricSwappingOrig;
				text = _invertString( text, sourceToTargetMap );
			}
		}
		return text;
	};

	/**
	* @name setOptions( options )
	* @function 
	* Sets options for Bidi conversion
	* @param {Object}:
	* - isInputVisual {boolean} (defaults to false): allowed values: true(Visual mode), false(Logical mode)
	* - isInputRtl {boolean}: allowed values true(Right-to-left direction), false (Left-to-right directiion), undefined(Contectual direction, i.e.direction defined by first strong character of input string)
	* - isOutputVisual {boolean} (defaults to false): allowed values: true(Visual mode), false(Logical mode)
	* - isOutputRtl {boolean}: allowed values true(Right-to-left direction), false (Left-to-right directiion), undefined(Contectual direction, i.e.direction defined by first strong characterof input string)
	* - isSymmetricSwapping {boolean} (defaults to false): allowed values true(needs symmetric swapping), false (no need in symmetric swapping),
	*/
	this.__bidiEngine__.setOptions = function( options ) {
		if ( options ) {
			_isInVisual = options.isInputVisual;
			_isOutVisual = options.isOutputVisual;
			_isInRtl = options.isInputRtl;
			_isOutRtl = options.isOutputRtl;
			_isSymmetricSwapping = options.isSymmetricSwapping;
		}
	};

	this.__bidiEngine__.setOptions( options );
	return this.__bidiEngine__;
};

	var _bidiUnicodeTypes = bidiUnicodeTypes;

	var bidiEngine = new jsPDF.__bidiEngine__({isInputVisual: true});

	var bidiEngineFunction = function (args) {
		var text = args.text;
		var x = args.x;
		var y = args.y;
		var options = args.options || {};
		var mutex = args.mutex || {};
		var lang = options.lang;
		var tmpText = [];
		
		if (Object.prototype.toString.call(text) === '[object Array]') {
			var i = 0;
			tmpText = [];
			for (i = 0; i < text.length; i += 1) {
				if (Object.prototype.toString.call(text[i]) === '[object Array]') {
					tmpText.push([bidiEngine.doBidiReorder(text[i][0]), text[i][1], text[i][2]]);
				} else {
					tmpText.push([bidiEngine.doBidiReorder(text[i])]);
				}
			}
			args.text = tmpText;
		} else {
			args.text = bidiEngine.doBidiReorder(text);
		}
	};

	jsPDF.API.events.push([ 
		'postProcessText'
		,bidiEngineFunction
	]);

})(jsPDF);