diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..95de9fb8e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+pdf.pdf
+intelisa.pdf
+openweb_tm-PRINT.pdf
diff --git a/canvas_proxy.js b/canvas_proxy.js
new file mode 100644
index 000000000..d6f5a0a25
--- /dev/null
+++ b/canvas_proxy.js
@@ -0,0 +1,250 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+
+"use strict";
+
+var JpegStreamProxyCounter = 0;
+// WebWorker Proxy for JpegStream.
+var JpegStreamProxy = (function() {
+ function constructor(bytes, dict) {
+ this.id = JpegStreamProxyCounter++;
+ this.dict = dict;
+
+ // Tell the main thread to create an image.
+ postMessage({
+ action: "jpeg_stream",
+ data: {
+ id: this.id,
+ raw: bytesToString(bytes)
+ }
+ });
+ }
+
+ constructor.prototype = {
+ getImage: function() {
+ return this;
+ },
+ getChar: function() {
+ error("internal error: getChar is not valid on JpegStream");
+ }
+ };
+
+ return constructor;
+})();
+
+// Really simple GradientProxy. There is currently only one active gradient at
+// the time, meaning you can't create a gradient, create a second one and then
+// use the first one again. As this isn't used in pdf.js right now, it's okay.
+function GradientProxy(cmdQueue, x0, y0, x1, y1) {
+ cmdQueue.push(["$createLinearGradient", [x0, y0, x1, y1]]);
+ this.addColorStop = function(i, rgba) {
+ cmdQueue.push(["$addColorStop", [i, rgba]]);
+ }
+}
+
+// Really simple PatternProxy.
+var patternProxyCounter = 0;
+function PatternProxy(cmdQueue, object, kind) {
+ this.id = patternProxyCounter++;
+
+ if (!(object instanceof CanvasProxy) ) {
+ throw "unkown type to createPattern";
+ }
+
+ // Flush the object here to ensure it's available on the main thread.
+ // TODO: Make some kind of dependency management, such that the object
+ // gets flushed only if needed.
+ object.flush();
+ cmdQueue.push(["$createPatternFromCanvas", [this.id, object.id, kind]]);
+}
+
+var canvasProxyCounter = 0;
+function CanvasProxy(width, height) {
+ this.id = canvasProxyCounter++;
+
+ // The `stack` holds the rendering calls and gets flushed to the main thead.
+ var cmdQueue = this.cmdQueue = [];
+
+ // Dummy context that gets exposed.
+ var ctx = {};
+ this.getContext = function(type) {
+ if (type != "2d") {
+ throw "CanvasProxy can only provide a 2d context.";
+ }
+ return ctx;
+ }
+
+ // Expose only the minimum of the canvas object - there is no dom to do
+ // more here.
+ this.width = width;
+ this.height = height;
+ ctx.canvas = this;
+
+ // Setup function calls to `ctx`.
+ var ctxFunc = [
+ "createRadialGradient",
+ "arcTo",
+ "arc",
+ "fillText",
+ "strokeText",
+ "createImageData",
+ "drawWindow",
+ "save",
+ "restore",
+ "scale",
+ "rotate",
+ "translate",
+ "transform",
+ "setTransform",
+ "clearRect",
+ "fillRect",
+ "strokeRect",
+ "beginPath",
+ "closePath",
+ "moveTo",
+ "lineTo",
+ "quadraticCurveTo",
+ "bezierCurveTo",
+ "rect",
+ "fill",
+ "stroke",
+ "clip",
+ "measureText",
+ "isPointInPath",
+
+ // These functions are necessary to track the rendering currentX state.
+ // The exact values can be computed on the main thread only, as the
+ // worker has no idea about text width.
+ "$setCurrentX",
+ "$addCurrentX",
+ "$saveCurrentX",
+ "$restoreCurrentX",
+ "$showText"
+ ];
+
+ function buildFuncCall(name) {
+ return function() {
+ // console.log("funcCall", name)
+ cmdQueue.push([name, Array.prototype.slice.call(arguments)]);
+ }
+ }
+ var name;
+ for (var i = 0; i < ctxFunc.length; i++) {
+ name = ctxFunc[i];
+ ctx[name] = buildFuncCall(name);
+ }
+
+ // Some function calls that need more work.
+
+ ctx.createPattern = function(object, kind) {
+ return new PatternProxy(cmdQueue, object, kind);
+ }
+
+ ctx.createLinearGradient = function(x0, y0, x1, y1) {
+ return new GradientProxy(cmdQueue, x0, y0, x1, y1);
+ }
+
+ ctx.getImageData = function(x, y, w, h) {
+ return {
+ width: w,
+ height: h,
+ data: Uint8ClampedArray(w * h * 4)
+ };
+ }
+
+ ctx.putImageData = function(data, x, y, width, height) {
+ cmdQueue.push(["$putImageData", [data, x, y, width, height]]);
+ }
+
+ ctx.drawImage = function(image, x, y, width, height, sx, sy, swidth, sheight) {
+ if (image instanceof CanvasProxy) {
+ // Send the image/CanvasProxy to the main thread.
+ image.flush();
+ cmdQueue.push(["$drawCanvas", [image.id, x, y, sx, sy, swidth, sheight]]);
+ } else if(image instanceof JpegStreamProxy) {
+ cmdQueue.push(["$drawImage", [image.id, x, y, sx, sy, swidth, sheight]])
+ } else {
+ throw "unkown type to drawImage";
+ }
+ }
+
+ // Setup property access to `ctx`.
+ var ctxProp = {
+ // "canvas"
+ "globalAlpha": "1",
+ "globalCompositeOperation": "source-over",
+ "strokeStyle": "#000000",
+ "fillStyle": "#000000",
+ "lineWidth": "1",
+ "lineCap": "butt",
+ "lineJoin": "miter",
+ "miterLimit": "10",
+ "shadowOffsetX": "0",
+ "shadowOffsetY": "0",
+ "shadowBlur": "0",
+ "shadowColor": "rgba(0, 0, 0, 0)",
+ "font": "10px sans-serif",
+ "textAlign": "start",
+ "textBaseline": "alphabetic",
+ "mozTextStyle": "10px sans-serif",
+ "mozImageSmoothingEnabled": "true"
+ }
+
+ function buildGetter(name) {
+ return function() {
+ return ctx["$" + name];
+ }
+ }
+
+ function buildSetter(name) {
+ return function(value) {
+ cmdQueue.push(["$", name, value]);
+ return ctx["$" + name] = value;
+ }
+ }
+
+ // Setting the value to `stroke|fillStyle` needs special handling, as it
+ // might gets an gradient/pattern.
+ function buildSetterStyle(name) {
+ return function(value) {
+ if (value instanceof GradientProxy) {
+ cmdQueue.push(["$" + name + "Gradient"]);
+ } else if (value instanceof PatternProxy) {
+ cmdQueue.push(["$" + name + "Pattern", [value.id]]);
+ } else {
+ cmdQueue.push(["$", name, value]);
+ return ctx["$" + name] = value;
+ }
+ }
+ }
+
+ for (var name in ctxProp) {
+ ctx["$" + name] = ctxProp[name];
+ ctx.__defineGetter__(name, buildGetter(name));
+
+ // Special treatment for `fillStyle` and `strokeStyle`: The passed style
+ // might be a gradient. Need to check for that.
+ if (name == "fillStyle" || name == "strokeStyle") {
+ ctx.__defineSetter__(name, buildSetterStyle(name));
+ } else {
+ ctx.__defineSetter__(name, buildSetter(name));
+ }
+ }
+}
+
+/**
+* Sends the current cmdQueue of the CanvasProxy over to the main thread and
+* resets the cmdQueue.
+*/
+CanvasProxy.prototype.flush = function() {
+ postMessage({
+ action: "canvas_proxy_cmd_queue",
+ data: {
+ id: this.id,
+ cmdQueue: this.cmdQueue,
+ width: this.width,
+ height: this.height
+ }
+ });
+ this.cmdQueue.length = 0;
+}
diff --git a/crypto.js b/crypto.js
new file mode 100644
index 000000000..14cc21902
--- /dev/null
+++ b/crypto.js
@@ -0,0 +1,260 @@
+/* -*- Mode: Java; tab-width: s; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=s tabstop=2 autoindent cindent expandtab: */
+
+"use strict";
+
+var ARCFourCipher = (function() {
+ function constructor(key) {
+ this.a = 0;
+ this.b = 0;
+ var s = new Uint8Array(256);
+ var i, j = 0, tmp, keyLength = key.length;
+ for (i = 0; i < 256; ++i)
+ s[i] = i;
+ for (i = 0; i < 256; ++i) {
+ tmp = s[i];
+ j = (j + tmp + key[i % keyLength]) & 0xFF;
+ s[i] = s[j];
+ s[j] = tmp;
+ }
+ this.s = s;
+ }
+
+ constructor.prototype = {
+ encryptBlock: function(data) {
+ var i, n = data.length, tmp, tmp2;
+ var a = this.a, b = this.b, s = this.s;
+ var output = new Uint8Array(n);
+ for (i = 0; i < n; ++i) {
+ var tmp;
+ a = (a + 1) & 0xFF;
+ tmp = s[a];
+ b = (b + tmp) & 0xFF;
+ tmp2 = s[b]
+ s[a] = tmp2;
+ s[b] = tmp;
+ output[i] = data[i] ^ s[(tmp + tmp2) & 0xFF];
+ }
+ this.a = a;
+ this.b = b;
+ return output;
+ }
+ };
+
+ return constructor;
+})();
+
+var md5 = (function() {
+ const r = new Uint8Array([
+ 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
+ 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
+ 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
+ 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]);
+ const k = new Int32Array([
+ -680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426,
+ -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162,
+ 1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632,
+ 643717713, -373897302, -701558691, 38016083, -660478335, -405537848, 568446438,
+ -1019803690, -187363961, 1163531501, -1444681467, -51403784, 1735328473,
+ -1926607734, -378558, -2022574463, 1839030562, -35309556, -1530992060,
+ 1272893353, -155497632, -1094730640, 681279174, -358537222, -722521979,
+ 76029189, -640364487, -421815835, 530742520, -995338651, -198630844, 1126891415,
+ -1416354905, -57434055, 1700485571, -1894986606, -1051523, -2054922799,
+ 1873313359, -30611744, -1560198380, 1309151649, -145523070, -1120210379,
+ 718787259, -343485551]);
+
+ function hash(data, offset, length) {
+ var h0 = 1732584193, h1 = -271733879, h2 = -1732584194, h3 = 271733878;
+ // pre-processing
+ var paddedLength = (length + 72) & ~63; // data + 9 extra bytes
+ var padded = new Uint8Array(paddedLength);
+ var i, j, n;
+ for (i = 0; i < length; ++i)
+ padded[i] = data[offset++];
+ padded[i++] = 0x80;
+ n = paddedLength - 8;
+ for (; i < n; ++i)
+ padded[i] = 0;
+ padded[i++] = (length << 3) & 0xFF;
+ padded[i++] = (length >> 5) & 0xFF;
+ padded[i++] = (length >> 13) & 0xFF;
+ padded[i++] = (length >> 21) & 0xFF;
+ padded[i++] = (length >>> 29) & 0xFF;
+ padded[i++] = 0;
+ padded[i++] = 0;
+ padded[i++] = 0;
+ // chunking
+ // TODO ArrayBuffer ?
+ var w = new Int32Array(16);
+ for (i = 0; i < paddedLength;) {
+ for (j = 0; j < 16; ++j, i += 4)
+ w[j] = padded[i] | (padded[i + 1] << 8) | (padded[i + 2] << 16) | (padded[i + 3] << 24);
+ var a = h0, b = h1, c = h2, d = h3, f, g;
+ for (j = 0; j < 64; ++j) {
+ if (j < 16) {
+ f = (b & c) | ((~b) & d);
+ g = j;
+ } else if (j < 32) {
+ f = (d & b) | ((~d) & c);
+ g = (5 * j + 1) & 15;
+ } else if (j < 48) {
+ f = b ^ c ^ d;
+ g = (3 * j + 5) & 15;
+ } else {
+ f = c ^ (b | (~d));
+ g = (7 * j) & 15;
+ }
+ var tmp = d, rotateArg = (a + f + k[j] + w[g]) | 0, rotate = r[j];
+ d = c;
+ c = b;
+ b = (b + ((rotateArg << rotate) | (rotateArg >>> (32 - rotate)))) | 0;
+ a = tmp;
+ }
+ h0 = (h0 + a) | 0;
+ h1 = (h1 + b) | 0;
+ h2 = (h2 + c) | 0;
+ h3 = (h3 + d) | 0;
+ }
+ return new Uint8Array([
+ h0 & 0xFF, (h0 >> 8) & 0xFF, (h0 >> 16) & 0xFF, (h0 >>> 24) & 0xFF,
+ h1 & 0xFF, (h1 >> 8) & 0xFF, (h1 >> 16) & 0xFF, (h1 >>> 24) & 0xFF,
+ h2 & 0xFF, (h2 >> 8) & 0xFF, (h2 >> 16) & 0xFF, (h2 >>> 24) & 0xFF,
+ h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >>> 24) & 0xFF
+ ]);
+ }
+ return hash;
+})();
+
+var CipherTransform = (function() {
+ function constructor(stringCipherConstructor, streamCipherConstructor) {
+ this.stringCipherConstructor = stringCipherConstructor;
+ this.streamCipherConstructor = streamCipherConstructor;
+ }
+ constructor.prototype = {
+ createStream: function (stream) {
+ var cipher = new this.streamCipherConstructor();
+ return new DecryptStream(stream, function(data) {
+ return cipher.encryptBlock(data);
+ });
+ },
+ decryptString: function(s) {
+ var cipher = new this.stringCipherConstructor();
+ var data = string2bytes(s);
+ data = cipher.encryptBlock(data);
+ return bytes2string(data);
+ }
+ };
+ return constructor;
+})();
+
+var CipherTransformFactory = (function() {
+ function prepareKeyData(fileId, password, ownerPassword, userPassword, flags, revision, keyLength) {
+ const defaultPasswordBytes = new Uint8Array([
+ 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
+ 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]);
+ var hashData = new Uint8Array(88), i = 0, j, n;
+ if (password) {
+ n = Math.min(32, password.length);
+ for (; i < n; ++i)
+ hashData[i] = password[i];
+ }
+ j = 0;
+ while (i < 32) {
+ hashData[i++] = defaultPasswordBytes[j++];
+ }
+ // as now the padded password in the hashData[0..i]
+ for (j = 0, n = ownerPassword.length; j < n; ++j)
+ hashData[i++] = ownerPassword[j];
+ hashData[i++] = flags & 0xFF;
+ hashData[i++] = (flags >> 8) & 0xFF;
+ hashData[i++] = (flags >> 16) & 0xFF;
+ hashData[i++] = (flags >>> 24) & 0xFF;
+ for (j = 0, n = fileId.length; j < n; ++j)
+ hashData[i++] = fileId[j];
+ // TODO rev 4, if metadata is not encrypted pass 0xFFFFFF also
+ var hash = md5(hashData, 0, i);
+ var keyLengthInBytes = keyLength >> 3;
+ if (revision >= 3) {
+ for (j = 0; j < 50; ++j) {
+ hash = md5(hash, 0, keyLengthInBytes);
+ }
+ }
+ var encryptionKey = hash.subarray(0, keyLengthInBytes);
+ var cipher, checkData;
+
+ if (revision >= 3) {
+ // padded password in hashData, we can use this array for user password check
+ i = 32;
+ for(j = 0, n = fileId.length; j < n; ++j)
+ hashData[i++] = fileId[j];
+ cipher = new ARCFourCipher(encryptionKey);
+ var checkData = cipher.encryptBlock(md5(hashData, 0, i));
+ n = encryptionKey.length;
+ var derrivedKey = new Uint8Array(n), k;
+ for (j = 1; j <= 19; ++j) {
+ for (k = 0; k < n; ++k)
+ derrivedKey[k] = encryptionKey[k] ^ j;
+ cipher = new ARCFourCipher(derrivedKey);
+ checkData = cipher.encryptBlock(checkData);
+ }
+ } else {
+ cipher = new ARCFourCipher(encryptionKey);
+ checkData = cipher.encryptBlock(hashData.subarray(0, 32));
+ }
+ for (j = 0, n = checkData.length; j < n; ++j) {
+ if (userPassword[j] != checkData[j])
+ error("incorrect password");
+ }
+ return encryptionKey;
+ }
+
+ function constructor(dict, fileId, password) {
+ var filter = dict.get("Filter");
+ if (!IsName(filter) || filter.name != "Standard")
+ error("unknown encryption method");
+ this.dict = dict;
+ var algorithm = dict.get("V");
+ if (!IsInt(algorithm) ||
+ (algorithm != 1 && algorithm != 2))
+ error("unsupported encryption algorithm");
+ // TODO support algorithm 4
+ var keyLength = dict.get("Length") || 40;
+ if (!IsInt(keyLength) ||
+ keyLength < 40 || (keyLength % 8) != 0)
+ error("invalid key length");
+ // prepare keys
+ var ownerPassword = stringToBytes(dict.get("O"));
+ var userPassword = stringToBytes(dict.get("U"));
+ var flags = dict.get("P");
+ var revision = dict.get("R");
+ var fileIdBytes = stringToBytes(fileId);
+ var passwordBytes;
+ if (password)
+ passwordBytes = stringToBytes(password);
+
+ this.encryptionKey = prepareKeyData(fileIdBytes, passwordBytes,
+ ownerPassword, userPassword, flags, revision, keyLength);
+ }
+
+ constructor.prototype = {
+ createCipherTransform: function(num, gen) {
+ var encryptionKey = this.encryptionKey;
+ var key = new Uint8Array(encryptionKey.length + 5), i, n;
+ for (i = 0, n = encryptionKey.length; i < n; ++i)
+ key[i] = encryptionKey[i];
+ key[i++] = num & 0xFF;
+ key[i++] = (num >> 8) & 0xFF;
+ key[i++] = (num >> 16) & 0xFF;
+ key[i++] = gen & 0xFF;
+ key[i++] = (gen >> 8) & 0xFF;
+ var hash = md5(key, 0, i);
+ key = hash.subarray(0, Math.min(key.length, 16));
+ var cipherConstructor = function() {
+ return new ARCFourCipher(key);
+ };
+ return new CipherTransform(cipherConstructor, cipherConstructor);
+ }
+ };
+
+ return constructor;
+})();
diff --git a/fonts.js b/fonts.js
index ad3d4fd35..728bc5c68 100644
--- a/fonts.js
+++ b/fonts.js
@@ -80,6 +80,36 @@ var Fonts = {
}
};
+var FontLoader = {
+ bind: function(fonts) {
+ var worker = (typeof window == "undefined");
+ var ready = true;
+
+ for (var i = 0; i < fonts.length; i++) {
+ var font = fonts[i];
+ if (Fonts[font.name]) {
+ ready = ready && !Fonts[font.name].loading;
+ continue;
+ }
+
+ ready = false;
+
+ var obj = new Font(font.name, font.file, font.properties);
+
+ var str = "";
+ var data = Fonts[font.name].data;
+ var length = data.length;
+ for (var j = 0; j < length; j++)
+ str += String.fromCharCode(data[j]);
+
+ worker ? obj.bindWorker(str) : obj.bindDOM(str);
+ }
+
+ return ready;
+ }
+};
+
+
/**
* 'Font' is the class the outside world should use, it encapsulate all the font
* decoding logics whatever type it is (assuming the font type is supported).
@@ -103,7 +133,7 @@ var Font = (function () {
// If the font is to be ignored, register it like an already loaded font
// to avoid the cost of waiting for it be be loaded by the platform.
- if (properties.ignore || properties.type == "TrueType" || kDisableFonts) {
+ if (properties.ignore || kDisableFonts) {
Fonts[name] = {
data: file,
loading: false,
@@ -113,13 +143,14 @@ var Font = (function () {
return;
}
+ var data;
switch (properties.type) {
case "Type1":
var cff = new CFF(name, file, properties);
this.mimetype = "font/opentype";
// Wrap the CFF data inside an OTF font file
- this.font = this.convert(name, cff, properties);
+ data = this.convert(name, cff, properties);
break;
case "TrueType":
@@ -127,7 +158,7 @@ var Font = (function () {
// Repair the TrueType file if it is can be damaged in the point of
// view of the sanitizer
- this.font = this.checkAndRepair(name, file, properties);
+ data = this.checkAndRepair(name, file, properties);
break;
default:
@@ -136,14 +167,11 @@ var Font = (function () {
}
Fonts[name] = {
- data: this.font,
+ data: data,
properties: properties,
loading: true,
cache: Object.create(null)
- }
-
- // Attach the font to the document
- this.bind();
+ };
};
function stringToArray(str) {
@@ -242,7 +270,7 @@ var Font = (function () {
return ranges;
};
- function createCMAPTable(glyphs) {
+ function createCMapTable(glyphs) {
var ranges = getRanges(glyphs);
var headerSize = (12 * 2 + (ranges.length * 4 * 2));
@@ -274,7 +302,7 @@ var Font = (function () {
var bias = 0;
for (var i = 0; i < segCount - 1; i++) {
var range = ranges[i];
- var start = range[0];
+ var start = range[0];
var end = range[1];
var delta = (((start - 1) - bias) ^ 0xffff) + 1;
bias += (end - start + 1);
@@ -284,8 +312,8 @@ var Font = (function () {
idDeltas += string16(delta);
idRangeOffsets += string16(0);
- for (var j = start; j <= end; j++)
- glyphsIds += String.fromCharCode(j);
+ for (var j = 0; j < range.length; j++)
+ glyphsIds += String.fromCharCode(range[j]);
}
startCount += "\xFF\xFF";
@@ -297,57 +325,60 @@ var Font = (function () {
idDeltas + idRangeOffsets + glyphsIds);
};
- function createOS2Table() {
- var OS2 = stringToArray(
- "\x00\x03" + // version
- "\x02\x24" + // xAvgCharWidth
- "\x01\xF4" + // usWeightClass
- "\x00\x05" + // usWidthClass
- "\x00\x00" + // fstype
- "\x02\x8A" + // ySubscriptXSize
- "\x02\xBB" + // ySubscriptYSize
- "\x00\x00" + // ySubscriptXOffset
- "\x00\x8C" + // ySubscriptYOffset
- "\x02\x8A" + // ySuperScriptXSize
- "\x02\xBB" + // ySuperScriptYSize
- "\x00\x00" + // ySuperScriptXOffset
- "\x01\xDF" + // ySuperScriptYOffset
- "\x00\x31" + // yStrikeOutSize
- "\x01\x02" + // yStrikeOutPosition
- "\x00\x00" + // sFamilyClass
- "\x02\x00\x06\x03\x00\x00\x00\x00\x00\x00" + // Panose
- "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 0-31)
- "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 32-63)
- "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 64-95)
- "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 96-127)
- "\x2A\x32\x31\x2A" + // achVendID
- "\x00\x20" + // fsSelection
- "\x00\x2D" + // usFirstCharIndex
- "\x00\x7A" + // usLastCharIndex
- "\x00\x03" + // sTypoAscender
- "\x00\x20" + // sTypeDescender
- "\x00\x38" + // sTypoLineGap
- "\x00\x5A" + // usWinAscent
- "\x02\xB4" + // usWinDescent
- "\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31)
- "\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63)
- "\x00\x00" + // sxHeight
- "\x00\x00" + // sCapHeight
- "\x00\x01" + // usDefaultChar
- "\x00\xCD" + // usBreakChar
- "\x00\x02" // usMaxContext
- );
- return OS2;
+ function createOS2Table(properties) {
+ return "\x00\x03" + // version
+ "\x02\x24" + // xAvgCharWidth
+ "\x01\xF4" + // usWeightClass
+ "\x00\x05" + // usWidthClass
+ "\x00\x00" + // fstype
+ "\x02\x8A" + // ySubscriptXSize
+ "\x02\xBB" + // ySubscriptYSize
+ "\x00\x00" + // ySubscriptXOffset
+ "\x00\x8C" + // ySubscriptYOffset
+ "\x02\x8A" + // ySuperScriptXSize
+ "\x02\xBB" + // ySuperScriptYSize
+ "\x00\x00" + // ySuperScriptXOffset
+ "\x01\xDF" + // ySuperScriptYOffset
+ "\x00\x31" + // yStrikeOutSize
+ "\x01\x02" + // yStrikeOutPosition
+ "\x00\x00" + // sFamilyClass
+ "\x02\x00\x06\x03\x00\x00\x00\x00\x00\x00" + // Panose
+ "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 0-31)
+ "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 32-63)
+ "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 64-95)
+ "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 96-127)
+ "\x2A\x32\x31\x2A" + // achVendID
+ "\x00\x20" + // fsSelection
+ "\x00\x2D" + // usFirstCharIndex
+ "\x00\x7A" + // usLastCharIndex
+ "\x00\x03" + // sTypoAscender
+ "\x00\x20" + // sTypeDescender
+ "\x00\x38" + // sTypoLineGap
+ string16(properties.ascent) + // usWinAscent
+ string16(properties.descent) + // usWinDescent
+ "\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31)
+ "\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63)
+ string16(properties.xHeight) + // sxHeight
+ string16(properties.capHeight) + // sCapHeight
+ "\x00\x01" + // usDefaultChar
+ "\x00\xCD" + // usBreakChar
+ "\x00\x02"; // usMaxContext
+ };
+
+ function createPostTable(properties) {
+ TODO("Fill with real values from the font dict");
+
+ return "\x00\x03\x00\x00" + // Version number
+ string32(properties.italicAngle) + // italicAngle
+ "\x00\x00" + // underlinePosition
+ "\x00\x00" + // underlineThickness
+ "\x00\x00\x00\x00" + // isFixedPitch
+ "\x00\x00\x00\x00" + // minMemType42
+ "\x00\x00\x00\x00" + // maxMemType42
+ "\x00\x00\x00\x00" + // minMemType1
+ "\x00\x00\x00\x00"; // maxMemType1
};
- /**
- * A bunch of the OpenType code is duplicate between this class and the
- * TrueType code, this is intentional and will merge in a future version
- * where all the code relative to OpenType will probably have its own
- * class and will take decision without the Fonts consent.
- * But at the moment it allows to develop around the TrueType rewriting
- * on the fly without messing up with the 'regular' Type1 to OTF conversion.
- */
constructor.prototype = {
name: null,
font: null,
@@ -368,11 +399,11 @@ var Font = (function () {
var length = FontsUtils.bytesToInteger(file.getBytes(4));
// Read the table associated data
- var currentPosition = file.pos;
- file.pos = file.start + offset;
-
+ var previousPosition = file.pos;
+ file.pos = file.start ? file.start : 0;
+ file.skip(offset);
var data = file.getBytes(length);
- file.pos = currentPosition;
+ file.pos = previousPosition;
return {
tag: tag,
@@ -393,6 +424,77 @@ var Font = (function () {
}
};
+ function replaceCMapTable(cmap, font, properties) {
+ var version = FontsUtils.bytesToInteger(font.getBytes(2));
+ var numTables = FontsUtils.bytesToInteger(font.getBytes(2));
+
+ for (var i = 0; i < numTables; i++) {
+ var platformID = FontsUtils.bytesToInteger(font.getBytes(2));
+ var encodingID = FontsUtils.bytesToInteger(font.getBytes(2));
+ var offset = FontsUtils.bytesToInteger(font.getBytes(4));
+ var format = FontsUtils.bytesToInteger(font.getBytes(2));
+ var length = FontsUtils.bytesToInteger(font.getBytes(2));
+ var language = FontsUtils.bytesToInteger(font.getBytes(2));
+
+ if ((format == 0 && numTables == 1) ||
+ (format == 6 && numTables == 1 && !properties.encoding.empty)) {
+ // Format 0 alone is not allowed by the sanitizer so let's rewrite
+ // that to a 3-1-4 Unicode BMP table
+ TODO("Use an other source of informations than charset here, it is not reliable");
+ var charset = properties.charset;
+ var glyphs = [];
+ for (var j = 0; j < charset.length; j++) {
+ glyphs.push({
+ unicode: GlyphsUnicode[charset[j]] || 0
+ });
+ }
+
+ cmap.data = createCMapTable(glyphs);
+ } else if (format == 6 && numTables == 1) {
+ // Format 6 is a 2-bytes dense mapping, which means the font data
+ // lives glue together even if they are pretty far in the unicode
+ // table. (This looks weird, so I can have missed something), this
+ // works on Linux but seems to fails on Mac so let's rewrite the
+ // cmap table to a 3-1-4 style
+ var firstCode = FontsUtils.bytesToInteger(font.getBytes(2));
+ var entryCount = FontsUtils.bytesToInteger(font.getBytes(2));
+
+ var glyphs = [];
+ var min = 0xffff, max = 0;
+ for (var j = 0; j < entryCount; j++) {
+ var charcode = FontsUtils.bytesToInteger(font.getBytes(2));
+ glyphs.push(charcode);
+
+ if (charcode < min)
+ min = charcode;
+ if (charcode > max)
+ max = charcode;
+ }
+
+ // Since Format 6 is a dense array, check for gaps
+ for (var j = min; j < max; j++) {
+ if (glyphs.indexOf(j) == -1)
+ glyphs.push(j);
+ }
+
+ for (var j = 0; j < glyphs.length; j++)
+ glyphs[j] = { unicode: glyphs[j] + firstCode };
+
+ var ranges= getRanges(glyphs);
+ assert(ranges.length == 1, "Got " + ranges.length + " ranges in a dense array");
+
+ var encoding = properties.encoding;
+ var denseRange = ranges[0];
+ var start = denseRange[0];
+ var end = denseRange[1];
+ var index = firstCode;
+ for (var j = start; j <= end; j++)
+ encoding[index++] = glyphs[j - firstCode - 1].unicode;
+ cmap.data = createCMapTable(glyphs);
+ }
+ }
+ };
+
// Check that required tables are present
var requiredTables = [ "OS/2", "cmap", "head", "hhea",
"hmtx", "maxp", "name", "post" ];
@@ -425,7 +527,7 @@ var Font = (function () {
if (requiredTables.length && requiredTables[0] == "OS/2") {
// Create a new file to hold the new version of our truetype with a new
// header and new offsets
- var ttf = Uint8Array(kMaxFontFileSize);
+ var ttf = new Uint8Array(kMaxFontFileSize);
// The offsets object holds at the same time a representation of where
// to write the table entry information about a table and another offset
@@ -442,41 +544,19 @@ var Font = (function () {
createOpenTypeHeader("\x00\x01\x00\x00", ttf, offsets, numTables);
// Insert the missing table
- var OS2 = createOS2Table();
tables.push({
tag: "OS/2",
- data: OS2
+ data: stringToArray(createOS2Table(properties))
});
- // If the font is missing a OS/2 table it's could be an old mac font
- // without a 3-1-4 Unicode BMP table, so let's rewrite it.
- var charset = properties.charset;
- var glyphs = [];
- for (var i = 0; i < charset.length; i++) {
- glyphs.push({
- unicode: GlyphsUnicode[charset[i]]
- });
- }
-
// Replace the old CMAP table with a shiny new one
- cmap.data = createCMAPTable(glyphs);
+ replaceCMapTable(cmap, font, properties);
// Rewrite the 'post' table if needed
if (!post) {
- post =
- "\x00\x03\x00\x00" + // Version number
- "\x00\x00\x01\x00" + // italicAngle
- "\x00\x00" + // underlinePosition
- "\x00\x00" + // underlineThickness
- "\x00\x00\x00\x00" + // isFixedPitch
- "\x00\x00\x00\x00" + // minMemType42
- "\x00\x00\x00\x00" + // maxMemType42
- "\x00\x00\x00\x00" + // minMemType1
- "\x00\x00\x00\x00"; // maxMemType1
-
- tables.unshift({
+ tables.push({
tag: "post",
- data: stringToArray(post)
+ data: stringToArray(createPostTable(properties))
});
}
@@ -520,16 +600,16 @@ var Font = (function () {
return font.getBytes();
},
- convert: function font_convert(name, font, properties) {
- var otf = Uint8Array(kMaxFontFileSize);
+ convert: function font_convert(fontName, font, properties) {
+ var otf = new Uint8Array(kMaxFontFileSize);
function createNameTable(name) {
var names = [
"See original licence", // Copyright
- name, // Font family
+ fontName, // Font family
"undefined", // Font subfamily (font weight)
"uniqueID", // Unique ID
- name, // Full font name
+ fontName, // Full font name
"0.1", // Version
"undefined", // Postscript name
"undefined", // Trademark
@@ -537,7 +617,7 @@ var Font = (function () {
"undefined" // Designer
];
- var name =
+ var nameTable =
"\x00\x00" + // format
"\x00\x0A" + // Number of names Record
"\x00\x7E"; // Storage
@@ -554,21 +634,21 @@ var Font = (function () {
"\x00\x00" + // name ID
string16(str.length) +
string16(strOffset);
- name += nameRecord;
+ nameTable += nameRecord;
strOffset += str.length;
}
- name += names.join("");
- return name;
+ nameTable += names.join("");
+ return nameTable;
}
// Required Tables
var CFF =
- font.data, // PostScript Font Program
+ font.data, // PostScript Font Program
OS2, // OS/2 and Windows Specific metrics
cmap, // Character to glyphs mapping
- head, // Font eader
+ head, // Font header
hhea, // Horizontal header
hmtx, // Horizontal metrics
maxp, // Maximum profile
@@ -592,14 +672,12 @@ var Font = (function () {
createTableEntry(otf, offsets, "CFF ", CFF);
/** OS/2 */
- OS2 = createOS2Table();
+ OS2 = stringToArray(createOS2Table(properties));
createTableEntry(otf, offsets, "OS/2", OS2);
- //XXX Getting charstrings here seems wrong since this is another CFF glue
- var charstrings = font.getOrderedCharStrings(properties.glyphs);
-
/** CMAP */
- cmap = createCMAPTable(charstrings);
+ var charstrings = font.charstrings;
+ cmap = createCMapTable(charstrings);
createTableEntry(otf, offsets, "cmap", cmap);
/** HEAD */
@@ -647,11 +725,15 @@ var Font = (function () {
createTableEntry(otf, offsets, "hhea", hhea);
/** HMTX */
- hmtx = "\x01\xF4\x00\x00";
+ /* For some reasons, probably related to how the backend handle fonts,
+ * Linux seems to ignore this file and prefer the data from the CFF itself
+ * while Windows use this data. So be careful if you hack on Linux and
+ * have to touch the 'hmtx' table
+ */
+ hmtx = "\x01\xF4\x00\x00"; // Fake .notdef
+ var width = 0, lsb = 0;
for (var i = 0; i < charstrings.length; i++) {
- var charstring = charstrings[i].charstring;
- var width = charstring[1];
- var lsb = charstring[0];
+ width = charstrings[i].charstring[1];
hmtx += string16(width) + string16(lsb);
}
hmtx = stringToArray(hmtx);
@@ -668,17 +750,7 @@ var Font = (function () {
createTableEntry(otf, offsets, "name", name);
/** POST */
- // TODO: get those informations from the FontInfo structure
- post = "\x00\x03\x00\x00" + // Version number
- "\x00\x00\x01\x00" + // italicAngle
- "\x00\x00" + // underlinePosition
- "\x00\x00" + // underlineThickness
- "\x00\x00\x00\x00" + // isFixedPitch
- "\x00\x00\x00\x00" + // minMemType42
- "\x00\x00\x00\x00" + // maxMemType42
- "\x00\x00\x00\x00" + // minMemType1
- "\x00\x00\x00\x00"; // maxMemType1
- post = stringToArray(post);
+ post = stringToArray(createPostTable(properties));
createTableEntry(otf, offsets, "post", post);
// Once all the table entries header are written, dump the data!
@@ -695,54 +767,28 @@ var Font = (function () {
return fontData;
},
- bind: function font_bind() {
- var data = this.font;
+ bindWorker: function font_bindWorker(data) {
+ postMessage({
+ action: "font",
+ data: {
+ raw: data,
+ fontName: this.name,
+ mimetype: this.mimetype
+ }
+ });
+ },
+
+ bindDOM: function font_bindDom(data) {
var fontName = this.name;
/** Hack begin */
-
// Actually there is not event when a font has finished downloading so
// the following code are a dirty hack to 'guess' when a font is ready
+ // This code could go away when bug 471915 has landed
var canvas = document.createElement("canvas");
- var style = "border: 1px solid black; position:absolute; top: " +
- (debug ? (100 * fontCount) : "-200") + "px; left: 2px; width: 340px; height: 100px";
- canvas.setAttribute("style", style);
- canvas.setAttribute("width", 340);
- canvas.setAttribute("heigth", 100);
- document.body.appendChild(canvas);
-
- // Get the font size canvas think it will be for 'spaces'
var ctx = canvas.getContext("2d");
ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
- var testString = " ";
-
- // When debugging use the characters provided by the charsets to visually
- // see what's happening instead of 'spaces'
- var debug = false;
- if (debug) {
- var name = document.createElement("font");
- name.setAttribute("style", "position: absolute; left: 20px; top: " +
- (100 * fontCount + 60) + "px");
- name.innerHTML = fontName;
- document.body.appendChild(name);
-
- // Retrieve font charset
- var charset = Fonts[fontName].properties.charset || [];
-
- // if the charset is too small make it repeat a few times
- var count = 30;
- while (count-- && charset.length <= 30)
- charset = charset.concat(charset.slice());
-
- for (var i = 0; i < charset.length; i++) {
- var unicode = GlyphsUnicode[charset[i]];
- if (!unicode)
- continue;
- testString += String.fromCharCode(unicode);
- }
-
- ctx.fillText(testString, 20, 20);
- }
+ var testString = " ";
// Periodicaly check for the width of the testString, it will be
// different once the real font has loaded
@@ -757,30 +803,19 @@ var Font = (function () {
if ((Date.now() - this.start) >= kMaxWaitForFontFace) {
window.clearInterval(interval);
Fonts[fontName].loading = false;
- warn("Is " + fontName + " for charset: " + charset + " loaded?");
+ warn("Is " + fontName + " loaded?");
this.start = 0;
} else if (textWidth != ctx.measureText(testString).width) {
window.clearInterval(interval);
Fonts[fontName].loading = false;
this.start = 0;
}
-
- if (debug)
- ctx.fillText(testString, 20, 50);
}, 30, this);
/** Hack end */
- // Get the base64 encoding of the binary font data
- var str = "";
- var length = data.length;
- for (var i = 0; i < length; ++i)
- str += String.fromCharCode(data[i]);
-
- var base64 = window.btoa(str);
-
// Add the @font-face rule to the document
- var url = "url(data:" + this.mimetype + ";base64," + base64 + ");";
+ var url = "url(data:" + this.mimetype + ";base64," + window.btoa(data) + ");";
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}";
var styleSheet = document.styleSheets[0];
styleSheet.insertRule(rule, styleSheet.length);
@@ -810,6 +845,9 @@ var FontsUtils = {
bytes.set([value >> 24, value >> 16, value >> 8, value]);
return [bytes[0], bytes[1], bytes[2], bytes[3]];
}
+
+ error("This number of bytes " + bytesCount + " is not supported");
+ return null;
},
bytesToInteger: function fu_bytesToInteger(bytesArray) {
@@ -938,6 +976,7 @@ var Type1Parser = function() {
"6": -1, // seac
"7": -1, //sbw
+ "11": "sub",
"12": "div",
// callothersubr is a mechanism to make calls on the postscript
@@ -1088,7 +1127,7 @@ var Type1Parser = function() {
* The CFF class takes a Type1 file and wrap it into a 'Compact Font Format',
* which itself embed Type2 charstrings.
*/
-const CFFStrings = [
+var CFFStrings = [
".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand",
"quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period",
"slash","zero","one","two","three","four","five","six","seven","eight","nine",
@@ -1146,6 +1185,8 @@ const CFFStrings = [
"001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"
];
+var type1Parser = new Type1Parser();
+
var CFF = function(name, file, properties) {
// Get the data block containing glyphs and subrs informations
var length1 = file.dict.get("Length1");
@@ -1153,17 +1194,15 @@ var CFF = function(name, file, properties) {
file.skip(length1);
var eexecBlock = file.getBytes(length2);
- // Decrypt the data blocks and retrieve the informations from it
- var parser = new Type1Parser();
- var fontInfo = parser.extractFontProgram(eexecBlock);
+ // Decrypt the data blocks and retrieve it's content
+ var data = type1Parser.extractFontProgram(eexecBlock);
- properties.subrs = fontInfo.subrs;
- properties.glyphs = fontInfo.charstrings;
- this.data = this.wrap(name, properties);
+ this.charstrings = this.getOrderedCharStrings(data.charstrings);
+ this.data = this.wrap(name, this.charstrings, data.subrs, properties);
};
CFF.prototype = {
- createCFFIndexHeader: function(objects, isByte) {
+ createCFFIndexHeader: function cff_createCFFIndexHeader(objects, isByte) {
// First 2 bytes contains the number of objects contained into this index
var count = objects.length;
@@ -1200,18 +1239,18 @@ CFF.prototype = {
return data;
},
- encodeNumber: function(value) {
+ encodeNumber: function cff_encodeNumber(value) {
var x = 0;
if (value >= -32768 && value <= 32767) {
return [ 28, value >> 8, value & 0xFF ];
} else if (value >= (-2147483647-1) && value <= 2147483647) {
return [ 0xFF, value >> 24, Value >> 16, value >> 8, value & 0xFF ];
- } else {
- error("Value: " + value + " is not allowed");
}
+ error("Value: " + value + " is not allowed");
+ return null;
},
- getOrderedCharStrings: function(glyphs) {
+ getOrderedCharStrings: function cff_getOrderedCharStrings(glyphs) {
var charstrings = [];
for (var i = 0; i < glyphs.length; i++) {
@@ -1224,7 +1263,7 @@ CFF.prototype = {
charstrings.push({
glyph: glyph,
unicode: unicode,
- charstring: glyphs[i].data.slice()
+ charstring: glyphs[i].data
});
}
};
@@ -1250,6 +1289,11 @@ CFF.prototype = {
"hlineto": 6,
"vlineto": 7,
"rrcurveto": 8,
+ "callsubr": 10,
+ "return": 11,
+ "sub": [12, 11],
+ "div": [12, 12],
+ "pop": [1, 12, 18],
"endchar": 14,
"rmoveto": 21,
"hmoveto": 22,
@@ -1257,7 +1301,7 @@ CFF.prototype = {
"hvcurveto": 31,
},
- flattenCharstring: function flattenCharstring(glyph, charstring, subrs) {
+ flattenCharstring: function flattenCharstring(charstring) {
var i = 0;
while (true) {
var obj = charstring[i];
@@ -1266,62 +1310,39 @@ CFF.prototype = {
if (obj.charAt) {
switch (obj) {
- case "callsubr":
- var subr = subrs[charstring[i - 1]].slice();
- if (subr.length > 1) {
- subr = this.flattenCharstring(glyph, subr, subrs);
- subr.pop();
- charstring.splice(i - 1, 2, subr);
- } else {
- charstring.splice(i - 1, 2);
- }
- i -= 1;
- break;
-
case "callothersubr":
var index = charstring[i - 1];
var count = charstring[i - 2];
var data = charstring[i - 3];
- // XXX The callothersubr needs to support at least the 3 defaults
- // otherSubrs of the spec
- if (index != 3)
- error("callothersubr for index: " + index + " (" + charstring + ")");
+ // If the flex mechanishm is not used in a font program, Adobe
+ // state that that entries 0, 1 and 2 can simply be replace by
+ // {}, which means that we can simply ignore them.
+ if (index < 3) {
+ i -= 3;
+ continue;
+ }
- if (!data) {
- charstring.splice(i - 2, 4, "pop", 3);
- i -= 3;
- } else {
- // 5 to remove the arguments, the callothersubr call and the pop command
- charstring.splice(i - 3, 5, 3);
- i -= 3;
+ // This is the same things about hint replacment, if it is not used
+ // entry 3 can be replaced by {}
+ if (index == 3) {
+ if (!data) {
+ charstring.splice(i - 2, 4, 3);
+ i -= 3;
+ } else {
+ // 5 to remove the arguments, the callothersubr call and the pop command
+ charstring.splice(i - 3, 5, 3);
+ i -= 3;
+ }
}
break;
- case "div":
- var num2 = charstring[i - 1];
- var num1 = charstring[i - 2];
- charstring.splice(i - 2, 3, num1 / num2);
- i -= 2;
- break;
-
- case "pop":
- if (i)
- charstring.splice(i - 2, 2);
- else
- charstring.splice(i - 1, 1);
- i -= 1;
- break;
-
-
case "hsbw":
- var charWidthVector = charstring[i - 1];
- var leftSidebearing = charstring[i - 2];
+ var charWidthVector = charstring[1];
+ var leftSidebearing = charstring[0];
- if (leftSidebearing)
- charstring.splice(i - 2, 3, charWidthVector, leftSidebearing, "hmoveto");
- else
- charstring.splice(i - 2, 3, charWidthVector);
+ charstring.splice(i, 1, leftSidebearing, "hmoveto");
+ charstring.splice(0, 1);
break;
case "endchar":
@@ -1333,21 +1354,16 @@ CFF.prototype = {
charstring.splice(j, 1, 28, command >> 8, command);
j+= 2;
} else if (command.charAt) {
- var command = this.commandsMap[command];
- if (IsArray(command)) {
- charstring.splice(j - 1, 1, command[0], command[1]);
+ var cmd = this.commandsMap[command];
+ if (!cmd)
+ error(command);
+
+ if (IsArray(cmd)) {
+ charstring.splice(j, 1, cmd[0], cmd[1]);
j += 1;
} else {
- charstring[j] = command;
+ charstring[j] = cmd;
}
- } else {
- charstring.splice(j, 1);
-
- // command has already been translated, just add them to the
- // charstring directly
- for (var k = 0; k < command.length; k++)
- charstring.splice(j + k, 0, command[k]);
- j+= command.length - 1;
}
}
return charstring;
@@ -1359,23 +1375,16 @@ CFF.prototype = {
i++;
}
error("failing with i = " + i + " in charstring:" + charstring + "(" + charstring.length + ")");
+ return [];
},
- wrap: function wrap(name, properties) {
- var charstrings = this.getOrderedCharStrings(properties.glyphs);
-
+ wrap: function wrap(name, charstrings, subrs, properties) {
// Starts the conversion of the Type1 charstrings to Type2
- var charstringsCount = 0;
- var charstringsDataLength = 0;
var glyphs = [];
- for (var i = 0; i < charstrings.length; i++) {
- var charstring = charstrings[i].charstring.slice();
- var glyph = charstrings[i].glyph;
-
- var flattened = this.flattenCharstring(glyph, charstring, properties.subrs);
- glyphs.push(flattened);
- charstringsCount++;
- charstringsDataLength += flattened.length;
+ var glyphsCount = charstrings.length;
+ for (var i = 0; i < glyphsCount; i++) {
+ var charstring = charstrings[i].charstring;
+ glyphs.push(this.flattenCharstring(charstring.slice()));
}
// Create a CFF font data
@@ -1410,17 +1419,16 @@ CFF.prototype = {
// Fill the charset header (first byte is the encoding)
var charset = [0x00];
- for (var i = 0; i < glyphs.length; i++) {
+ for (var i = 0; i < glyphsCount; i++) {
var index = CFFStrings.indexOf(charstrings[i].glyph);
if (index == -1)
- index = CFFStrings.length + strings.indexOf(glyph);
+ index = CFFStrings.length + strings.indexOf(charstrings[i].glyph);
var bytes = FontsUtils.integerToBytes(index, 2);
charset.push(bytes[0]);
charset.push(bytes[1]);
}
var charstringsIndex = this.createCFFIndexHeader([[0x40, 0x0E]].concat(glyphs), true);
- charstringsIndex = charstringsIndex.join(" ").split(" "); // XXX why?
//Top Dict Index
var topDictIndex = [
@@ -1446,7 +1454,7 @@ CFF.prototype = {
topDictIndex = topDictIndex.concat([28, 0, 0, 16]) // Encoding
- var charstringsOffset = charsetOffset + (charstringsCount * 2) + 1;
+ var charstringsOffset = charsetOffset + (glyphsCount * 2) + 1;
topDictIndex = topDictIndex.concat(this.encodeNumber(charstringsOffset));
topDictIndex.push(17); // charstrings
@@ -1454,7 +1462,6 @@ CFF.prototype = {
var privateOffset = charstringsOffset + charstringsIndex.length;
topDictIndex = topDictIndex.concat(this.encodeNumber(privateOffset));
topDictIndex.push(18); // Private
- topDictIndex = topDictIndex.join(" ").split(" ");
var indexes = [
topDictIndex, stringsIndex,
@@ -1482,23 +1489,35 @@ CFF.prototype = {
247, 32, 11,
247, 10, 161, 147, 154, 150, 143, 12, 13,
139, 12, 14,
- 28, 0, 55, 19
+ 28, 0, 55, 19 // Subrs offset
]);
- privateData = privateData.join(" ").split(" ");
cff.set(privateData, currentOffset);
currentOffset += privateData.length;
- // Dump shit at the end of the file
- var shit = [
- 0x00, 0x01, 0x01, 0x01,
- 0x13, 0x5D, 0x65, 0x64,
- 0x5E, 0x5B, 0xAF, 0x66,
- 0xBA, 0xBB, 0xB1, 0xB0,
- 0xB9, 0xBA, 0x65, 0xB2,
- 0x5C, 0x1F, 0x0B
- ];
- cff.set(shit, currentOffset);
- currentOffset += shit.length;
+ // Local Subrs
+ var flattenedSubrs = [];
+
+ var bias = 0;
+ var subrsCount = subrs.length;
+ if (subrsCount < 1240)
+ bias = 107;
+ else if (subrsCount < 33900)
+ bias = 1131;
+ else
+ bias = 32768;
+
+ // Add a bunch of empty subrs to deal with the Type2 bias
+ for (var i = 0; i < bias; i++)
+ flattenedSubrs.push([0x0B]);
+
+ for (var i = 0; i < subrsCount; i++) {
+ var subr = subrs[i];
+ flattenedSubrs.push(this.flattenCharstring(subr));
+ }
+
+ var subrsData = this.createCFFIndexHeader(flattenedSubrs, true);
+ cff.set(subrsData, currentOffset);
+ currentOffset += subrsData.length;
var fontData = [];
for (var i = 0; i < currentOffset; i++)
diff --git a/multi-page-viewer.css b/multi-page-viewer.css
deleted file mode 100644
index 7f4701022..000000000
--- a/multi-page-viewer.css
+++ /dev/null
@@ -1,197 +0,0 @@
-/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
-/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
-
-body {
- background-color: #929292;
- font-family: 'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;
- margin: 0px;
- padding: 0px;
-}
-
-canvas {
- box-shadow: 0px 4px 10px #000;
- -moz-box-shadow: 0px 4px 10px #000;
- -webkit-box-shadow: 0px 4px 10px #000;
-}
-
-span {
- font-size: 0.8em;
-}
-
-.control {
- display: inline-block;
- float: left;
- margin: 0px 20px 0px 0px;
- padding: 0px 4px 0px 0px;
-}
-
-.control > input {
- float: left;
- border: 1px solid #4d4d4d;
- height: 20px;
- padding: 0px;
- margin: 0px 2px 0px 0px;
- border-radius: 4px;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
- -moz-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
- -webkit-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
-}
-
-.control > select {
- float: left;
- border: 1px solid #4d4d4d;
- height: 22px;
- padding: 2px 0px 0px;
- margin: 0px 0px 1px;
- border-radius: 4px;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
- -moz-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
- -webkit-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
-}
-
-.control > span {
- cursor: default;
- float: left;
- height: 18px;
- margin: 5px 2px 0px;
- padding: 0px;
- user-select: none;
- -moz-user-select: none;
- -webkit-user-select: none;
-}
-
-.control .label {
- clear: both;
- float: left;
- font-size: 0.65em;
- margin: 2px 0px 0px;
- position: relative;
- text-align: center;
- width: 100%;
-}
-
-.page {
- width: 816px;
- height: 1056px;
- margin: 10px auto;
-}
-
-#controls {
- background-color: #eee;
- border-bottom: 1px solid #666;
- padding: 4px 0px 0px 8px;
- position: fixed;
- left: 0px;
- top: 0px;
- height: 40px;
- width: 100%;
- box-shadow: 0px 2px 8px #000;
- -moz-box-shadow: 0px 2px 8px #000;
- -webkit-box-shadow: 0px 2px 8px #000;
-}
-
-#controls input {
- user-select: text;
- -moz-user-select: text;
- -webkit-user-select: text;
-}
-
-#previousPageButton {
- background: url('images/buttons.png') no-repeat 0px -23px;
- cursor: default;
- display: inline-block;
- float: left;
- margin: 0px;
- width: 28px;
- height: 23px;
-}
-
-#previousPageButton.down {
- background: url('images/buttons.png') no-repeat 0px -46px;
-}
-
-#previousPageButton.disabled {
- background: url('images/buttons.png') no-repeat 0px 0px;
-}
-
-#nextPageButton {
- background: url('images/buttons.png') no-repeat -28px -23px;
- cursor: default;
- display: inline-block;
- float: left;
- margin: 0px;
- width: 28px;
- height: 23px;
-}
-
-#nextPageButton.down {
- background: url('images/buttons.png') no-repeat -28px -46px;
-}
-
-#nextPageButton.disabled {
- background: url('images/buttons.png') no-repeat -28px 0px;
-}
-
-#openFileButton {
- background: url('images/buttons.png') no-repeat -56px -23px;
- cursor: default;
- display: inline-block;
- float: left;
- margin: 0px 0px 0px 3px;
- width: 29px;
- height: 23px;
-}
-
-#openFileButton.down {
- background: url('images/buttons.png') no-repeat -56px -46px;
-}
-
-#openFileButton.disabled {
- background: url('images/buttons.png') no-repeat -56px 0px;
-}
-
-#fileInput {
- display: none;
-}
-
-#pageNumber {
- text-align: right;
-}
-
-#sidebar {
- background-color: rgba(0, 0, 0, 0.8);
- position: fixed;
- width: 150px;
- top: 62px;
- bottom: 18px;
- border-top-right-radius: 8px;
- border-bottom-right-radius: 8px;
- -moz-border-radius-topright: 8px;
- -moz-border-radius-bottomright: 8px;
- -webkit-border-top-right-radius: 8px;
- -webkit-border-bottom-right-radius: 8px;
-}
-
-#sidebarScrollView {
- position: absolute;
- overflow: hidden;
- overflow-y: auto;
- top: 40px;
- right: 10px;
- bottom: 10px;
- left: 10px;
-}
-
-#sidebarContentView {
- height: auto;
- width: 100px;
-}
-
-#viewer {
- margin: 44px 0px 0px;
- padding: 8px 0px;
-}
diff --git a/multi-page-viewer.html b/multi-page-viewer.html
deleted file mode 100644
index ffbdfe707..000000000
--- a/multi-page-viewer.html
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-pdf.js Multi-Page Viewer
-
-
-
-
-
-
-
-
-
-
-
-
- Previous/Next
-
-
-
- /
- --
- Page Number
-
-
-
- Zoom
-
-
-
-
- Open File
-
-
-
-
-
-
diff --git a/multi-page-viewer.js b/multi-page-viewer.js
deleted file mode 100644
index baad7809e..000000000
--- a/multi-page-viewer.js
+++ /dev/null
@@ -1,466 +0,0 @@
-/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
-/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
-
-"use strict";
-
-var PDFViewer = {
- queryParams: {},
-
- element: null,
-
- previousPageButton: null,
- nextPageButton: null,
- pageNumberInput: null,
- scaleSelect: null,
- fileInput: null,
-
- willJumpToPage: false,
-
- pdf: null,
-
- url: 'compressed.tracemonkey-pldi-09.pdf',
- pageNumber: 1,
- numberOfPages: 1,
-
- scale: 1.0,
-
- pageWidth: function() {
- return 816 * PDFViewer.scale;
- },
-
- pageHeight: function() {
- return 1056 * PDFViewer.scale;
- },
-
- lastPagesDrawn: [],
-
- visiblePages: function() {
- var pageHeight = PDFViewer.pageHeight() + 20; // Add 20 for the margins.
- var windowTop = window.pageYOffset;
- var windowBottom = window.pageYOffset + window.innerHeight;
- var pageStartIndex = Math.floor(windowTop / pageHeight);
- var pageStopIndex = Math.ceil(windowBottom / pageHeight);
-
- var pages = [];
-
- for (var i = pageStartIndex; i <= pageStopIndex; i++) {
- pages.push(i + 1);
- }
-
- return pages;
- },
-
- createPage: function(num) {
- var anchor = document.createElement('a');
- anchor.name = '' + num;
-
- var div = document.createElement('div');
- div.id = 'pageContainer' + num;
- div.className = 'page';
- div.style.width = PDFViewer.pageWidth() + 'px';
- div.style.height = PDFViewer.pageHeight() + 'px';
-
- PDFViewer.element.appendChild(anchor);
- PDFViewer.element.appendChild(div);
- },
-
- removePage: function(num) {
- var div = document.getElementById('pageContainer' + num);
-
- if (div) {
- while (div.hasChildNodes()) {
- div.removeChild(div.firstChild);
- }
- }
- },
-
- drawPage: function(num) {
- if (!PDFViewer.pdf) {
- return;
- }
-
- var div = document.getElementById('pageContainer' + num);
- var canvas = document.createElement('canvas');
-
- if (div && !div.hasChildNodes()) {
- div.appendChild(canvas);
-
- var page = PDFViewer.pdf.getPage(num);
-
- canvas.id = 'page' + num;
- canvas.mozOpaque = true;
-
- // Canvas dimensions must be specified in CSS pixels. CSS pixels
- // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
- canvas.width = PDFViewer.pageWidth();
- canvas.height = PDFViewer.pageHeight();
-
- var ctx = canvas.getContext('2d');
- ctx.save();
- ctx.fillStyle = 'rgb(255, 255, 255)';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- ctx.restore();
-
- var gfx = new CanvasGraphics(ctx);
- var fonts = [];
-
- // page.compile will collect all fonts for us, once we have loaded them
- // we can trigger the actual page rendering with page.display
- page.compile(gfx, fonts);
-
- var areFontsReady = true;
-
- // Inspect fonts and translate the missing one
- var fontCount = fonts.length;
-
- for (var i = 0; i < fontCount; i++) {
- var font = fonts[i];
-
- if (Fonts[font.name]) {
- areFontsReady = areFontsReady && !Fonts[font.name].loading;
- continue;
- }
-
- new Font(font.name, font.file, font.properties);
-
- areFontsReady = false;
- }
-
- var pageInterval;
-
- var delayLoadFont = function() {
- for (var i = 0; i < fontCount; i++) {
- if (Fonts[font.name].loading) {
- return;
- }
- }
-
- clearInterval(pageInterval);
-
- while (div.hasChildNodes()) {
- div.removeChild(div.firstChild);
- }
-
- PDFViewer.drawPage(num);
- }
-
- if (!areFontsReady) {
- pageInterval = setInterval(delayLoadFont, 10);
- return;
- }
-
- page.display(gfx);
- }
- },
-
- changeScale: function(num) {
- while (PDFViewer.element.hasChildNodes()) {
- PDFViewer.element.removeChild(PDFViewer.element.firstChild);
- }
-
- PDFViewer.scale = num / 100;
-
- var i;
-
- if (PDFViewer.pdf) {
- for (i = 1; i <= PDFViewer.numberOfPages; i++) {
- PDFViewer.createPage(i);
- }
-
- if (PDFViewer.numberOfPages > 0) {
- PDFViewer.drawPage(1);
- }
- }
-
- for (i = 0; i < PDFViewer.scaleSelect.childNodes; i++) {
- var option = PDFViewer.scaleSelect.childNodes[i];
-
- if (option.value == num) {
- if (!option.selected) {
- option.selected = 'selected';
- }
- } else {
- if (option.selected) {
- option.removeAttribute('selected');
- }
- }
- }
-
- PDFViewer.scaleSelect.value = Math.floor(PDFViewer.scale * 100) + '%';
- },
-
- goToPage: function(num) {
- if (1 <= num && num <= PDFViewer.numberOfPages) {
- PDFViewer.pageNumber = num;
- PDFViewer.pageNumberInput.value = PDFViewer.pageNumber;
- PDFViewer.willJumpToPage = true;
-
- document.location.hash = PDFViewer.pageNumber;
-
- PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ?
- 'disabled' : '';
- PDFViewer.nextPageButton.className = (PDFViewer.pageNumber === PDFViewer.numberOfPages) ?
- 'disabled' : '';
- }
- },
-
- goToPreviousPage: function() {
- if (PDFViewer.pageNumber > 1) {
- PDFViewer.goToPage(--PDFViewer.pageNumber);
- }
- },
-
- goToNextPage: function() {
- if (PDFViewer.pageNumber < PDFViewer.numberOfPages) {
- PDFViewer.goToPage(++PDFViewer.pageNumber);
- }
- },
-
- openURL: function(url) {
- PDFViewer.url = url;
- document.title = url;
-
- var req = new XMLHttpRequest();
- req.open('GET', url);
- req.mozResponseType = req.responseType = 'arraybuffer';
- req.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200;
-
- req.onreadystatechange = function() {
- if (req.readyState === 4 && req.status === req.expected) {
- var data = req.mozResponseArrayBuffer ||
- req.mozResponse ||
- req.responseArrayBuffer ||
- req.response;
-
- PDFViewer.readPDF(data);
- }
- };
-
- req.send(null);
- },
-
- readPDF: function(data) {
- while (PDFViewer.element.hasChildNodes()) {
- PDFViewer.element.removeChild(PDFViewer.element.firstChild);
- }
-
- PDFViewer.pdf = new PDFDoc(new Stream(data));
- PDFViewer.numberOfPages = PDFViewer.pdf.numPages;
- document.getElementById('numPages').innerHTML = PDFViewer.numberOfPages.toString();
-
- for (var i = 1; i <= PDFViewer.numberOfPages; i++) {
- PDFViewer.createPage(i);
- }
-
- if (PDFViewer.numberOfPages > 0) {
- PDFViewer.drawPage(1);
- document.location.hash = 1;
- }
-
- PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ?
- 'disabled' : '';
- PDFViewer.nextPageButton.className = (PDFViewer.pageNumber === PDFViewer.numberOfPages) ?
- 'disabled' : '';
- }
-};
-
-window.onload = function() {
-
- // Parse the URL query parameters into a cached object.
- PDFViewer.queryParams = function() {
- var qs = window.location.search.substring(1);
- var kvs = qs.split('&');
- var params = {};
- for (var i = 0; i < kvs.length; ++i) {
- var kv = kvs[i].split('=');
- params[unescape(kv[0])] = unescape(kv[1]);
- }
-
- return params;
- }();
-
- PDFViewer.element = document.getElementById('viewer');
-
- PDFViewer.pageNumberInput = document.getElementById('pageNumber');
- PDFViewer.pageNumberInput.onkeydown = function(evt) {
- var charCode = evt.charCode || evt.keyCode;
-
- // Up arrow key.
- if (charCode === 38) {
- PDFViewer.goToNextPage();
- this.select();
- }
-
- // Down arrow key.
- else if (charCode === 40) {
- PDFViewer.goToPreviousPage();
- this.select();
- }
-
- // All other non-numeric keys (excluding Left arrow, Right arrow,
- // Backspace, and Delete keys).
- else if ((charCode < 48 || charCode > 57) &&
- charCode !== 8 && // Backspace
- charCode !== 46 && // Delete
- charCode !== 37 && // Left arrow
- charCode !== 39 // Right arrow
- ) {
- return false;
- }
-
- return true;
- };
- PDFViewer.pageNumberInput.onkeyup = function(evt) {
- var charCode = evt.charCode || evt.keyCode;
-
- // All numeric keys, Backspace, and Delete.
- if ((charCode >= 48 && charCode <= 57) ||
- charCode === 8 || // Backspace
- charCode === 46 // Delete
- ) {
- PDFViewer.goToPage(this.value);
- }
-
- this.focus();
- };
-
- PDFViewer.previousPageButton = document.getElementById('previousPageButton');
- PDFViewer.previousPageButton.onclick = function(evt) {
- if (this.className.indexOf('disabled') === -1) {
- PDFViewer.goToPreviousPage();
- }
- };
- PDFViewer.previousPageButton.onmousedown = function(evt) {
- if (this.className.indexOf('disabled') === -1) {
- this.className = 'down';
- }
- };
- PDFViewer.previousPageButton.onmouseup = function(evt) {
- this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
- };
- PDFViewer.previousPageButton.onmouseout = function(evt) {
- this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
- };
-
- PDFViewer.nextPageButton = document.getElementById('nextPageButton');
- PDFViewer.nextPageButton.onclick = function(evt) {
- if (this.className.indexOf('disabled') === -1) {
- PDFViewer.goToNextPage();
- }
- };
- PDFViewer.nextPageButton.onmousedown = function(evt) {
- if (this.className.indexOf('disabled') === -1) {
- this.className = 'down';
- }
- };
- PDFViewer.nextPageButton.onmouseup = function(evt) {
- this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
- };
- PDFViewer.nextPageButton.onmouseout = function(evt) {
- this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
- };
-
- PDFViewer.scaleSelect = document.getElementById('scaleSelect');
- PDFViewer.scaleSelect.onchange = function(evt) {
- PDFViewer.changeScale(parseInt(this.value));
- };
-
- if (window.File && window.FileReader && window.FileList && window.Blob) {
- var openFileButton = document.getElementById('openFileButton');
- openFileButton.onclick = function(evt) {
- if (this.className.indexOf('disabled') === -1) {
- PDFViewer.fileInput.click();
- }
- };
- openFileButton.onmousedown = function(evt) {
- if (this.className.indexOf('disabled') === -1) {
- this.className = 'down';
- }
- };
- openFileButton.onmouseup = function(evt) {
- this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
- };
- openFileButton.onmouseout = function(evt) {
- this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
- };
-
- PDFViewer.fileInput = document.getElementById('fileInput');
- PDFViewer.fileInput.onchange = function(evt) {
- var files = evt.target.files;
-
- if (files.length > 0) {
- var file = files[0];
- var fileReader = new FileReader();
-
- document.title = file.name;
-
- // Read the local file into a Uint8Array.
- fileReader.onload = function(evt) {
- var data = evt.target.result;
- var buffer = new ArrayBuffer(data.length);
- var uint8Array = new Uint8Array(buffer);
-
- for (var i = 0; i < data.length; i++) {
- uint8Array[i] = data.charCodeAt(i);
- }
-
- PDFViewer.readPDF(uint8Array);
- };
-
- // Read as a binary string since "readAsArrayBuffer" is not yet
- // implemented in Firefox.
- fileReader.readAsBinaryString(file);
- }
- };
- PDFViewer.fileInput.value = null;
- } else {
- document.getElementById('fileWrapper').style.display = 'none';
- }
-
- PDFViewer.pageNumber = parseInt(PDFViewer.queryParams.page) || PDFViewer.pageNumber;
- PDFViewer.scale = parseInt(PDFViewer.scaleSelect.value) / 100 || 1.0;
-
- PDFViewer.openURL(PDFViewer.queryParams.file || PDFViewer.url);
-
- window.onscroll = function(evt) {
- var lastPagesDrawn = PDFViewer.lastPagesDrawn;
- var visiblePages = PDFViewer.visiblePages();
-
- var pagesToDraw = [];
- var pagesToKeep = [];
- var pagesToRemove = [];
-
- var i;
-
- // Determine which visible pages were not previously drawn.
- for (i = 0; i < visiblePages.length; i++) {
- if (lastPagesDrawn.indexOf(visiblePages[i]) === -1) {
- pagesToDraw.push(visiblePages[i]);
- PDFViewer.drawPage(visiblePages[i]);
- } else {
- pagesToKeep.push(visiblePages[i]);
- }
- }
-
- // Determine which previously drawn pages are no longer visible.
- for (i = 0; i < lastPagesDrawn.length; i++) {
- if (visiblePages.indexOf(lastPagesDrawn[i]) === -1) {
- pagesToRemove.push(lastPagesDrawn[i]);
- PDFViewer.removePage(lastPagesDrawn[i]);
- }
- }
-
- PDFViewer.lastPagesDrawn = pagesToDraw.concat(pagesToKeep);
-
- // Update the page number input with the current page number.
- if (!PDFViewer.willJumpToPage && visiblePages.length > 0) {
- PDFViewer.pageNumber = PDFViewer.pageNumberInput.value = visiblePages[0];
- PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ?
- 'disabled' : '';
- PDFViewer.nextPageButton.className = (PDFViewer.pageNumber === PDFViewer.numberOfPages) ?
- 'disabled' : '';
- } else {
- PDFViewer.willJumpToPage = false;
- }
- };
-};
diff --git a/multi_page_viewer.css b/multi_page_viewer.css
new file mode 100644
index 000000000..2eaca4870
--- /dev/null
+++ b/multi_page_viewer.css
@@ -0,0 +1,230 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+
+body {
+ background-color: #929292;
+ font-family: 'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;
+ margin: 0px;
+ padding: 0px;
+}
+
+canvas {
+ box-shadow: 0px 4px 10px #000;
+ -moz-box-shadow: 0px 4px 10px #000;
+ -webkit-box-shadow: 0px 4px 10px #000;
+}
+
+span {
+ font-size: 0.8em;
+}
+
+.control {
+ display: inline-block;
+ float: left;
+ margin: 0px 20px 0px 0px;
+ padding: 0px 4px 0px 0px;
+}
+
+.control > input {
+ float: left;
+ border: 1px solid #4d4d4d;
+ height: 20px;
+ padding: 0px;
+ margin: 0px 2px 0px 0px;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
+ -moz-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
+ -webkit-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
+}
+
+.control > select {
+ float: left;
+ border: 1px solid #4d4d4d;
+ height: 22px;
+ padding: 2px 0px 0px;
+ margin: 0px 0px 1px;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
+ -moz-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
+ -webkit-box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.25);
+}
+
+.control > span {
+ cursor: default;
+ float: left;
+ height: 18px;
+ margin: 5px 2px 0px;
+ padding: 0px;
+ user-select: none;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+}
+
+.control .label {
+ clear: both;
+ float: left;
+ font-size: 0.65em;
+ margin: 2px 0px 0px;
+ position: relative;
+ text-align: center;
+ width: 100%;
+}
+
+.thumbnailPageNumber {
+ color: #fff;
+ font-size: 0.55em;
+ text-align: right;
+ margin: -6px 2px 6px 0px;
+ width: 102px;
+}
+
+.thumbnail {
+ width: 104px;
+ height: 134px;
+ margin: 0px auto 10px;
+}
+
+.page {
+ width: 816px;
+ height: 1056px;
+ margin: 10px auto;
+}
+
+#controls {
+ background-color: #eee;
+ border-bottom: 1px solid #666;
+ padding: 4px 0px 0px 8px;
+ position: fixed;
+ left: 0px;
+ top: 0px;
+ height: 40px;
+ width: 100%;
+ box-shadow: 0px 2px 8px #000;
+ -moz-box-shadow: 0px 2px 8px #000;
+ -webkit-box-shadow: 0px 2px 8px #000;
+}
+
+#controls input {
+ user-select: text;
+ -moz-user-select: text;
+ -webkit-user-select: text;
+}
+
+#previousPageButton {
+ background: url('images/buttons.png') no-repeat 0px -23px;
+ cursor: default;
+ display: inline-block;
+ float: left;
+ margin: 0px;
+ width: 28px;
+ height: 23px;
+}
+
+#previousPageButton.down {
+ background: url('images/buttons.png') no-repeat 0px -46px;
+}
+
+#previousPageButton.disabled {
+ background: url('images/buttons.png') no-repeat 0px 0px;
+}
+
+#nextPageButton {
+ background: url('images/buttons.png') no-repeat -28px -23px;
+ cursor: default;
+ display: inline-block;
+ float: left;
+ margin: 0px;
+ width: 28px;
+ height: 23px;
+}
+
+#nextPageButton.down {
+ background: url('images/buttons.png') no-repeat -28px -46px;
+}
+
+#nextPageButton.disabled {
+ background: url('images/buttons.png') no-repeat -28px 0px;
+}
+
+#openFileButton {
+ background: url('images/buttons.png') no-repeat -56px -23px;
+ cursor: default;
+ display: inline-block;
+ float: left;
+ margin: 0px 0px 0px 3px;
+ width: 29px;
+ height: 23px;
+}
+
+#openFileButton.down {
+ background: url('images/buttons.png') no-repeat -56px -46px;
+}
+
+#openFileButton.disabled {
+ background: url('images/buttons.png') no-repeat -56px 0px;
+}
+
+#fileInput {
+ display: none;
+}
+
+#pageNumber {
+ text-align: right;
+}
+
+#sidebar {
+ position: fixed;
+ width: 200px;
+ top: 62px;
+ bottom: 18px;
+ left: -170px;
+ transition: left 0.25s ease-in-out 1s;
+ -moz-transition: left 0.25s ease-in-out 1s;
+ -webkit-transition: left 0.25s ease-in-out 1s;
+}
+
+#sidebar:hover {
+ left: 0px;
+ transition: left 0.25s ease-in-out 0s;
+ -moz-transition: left 0.25s ease-in-out 0s;
+ -webkit-transition: left 0.25s ease-in-out 0s;
+}
+
+#sidebarBox {
+ background-color: rgba(0, 0, 0, 0.7);
+ width: 150px;
+ height: 100%;
+ border-top-right-radius: 8px;
+ border-bottom-right-radius: 8px;
+ -moz-border-radius-topright: 8px;
+ -moz-border-radius-bottomright: 8px;
+ -webkit-border-top-right-radius: 8px;
+ -webkit-border-bottom-right-radius: 8px;
+ box-shadow: 0px 2px 8px #000;
+ -moz-box-shadow: 0px 2px 8px #000;
+ -webkit-box-shadow: 0px 2px 8px #000;
+}
+
+#sidebarScrollView {
+ position: absolute;
+ overflow: hidden;
+ overflow-y: auto;
+ top: 10px;
+ bottom: 10px;
+ left: 10px;
+ width: 130px;
+}
+
+#sidebarContentView {
+ height: auto;
+ width: 100px;
+}
+
+#viewer {
+ margin: 44px 0px 0px;
+ padding: 8px 0px;
+}
diff --git a/multi_page_viewer.html b/multi_page_viewer.html
new file mode 100644
index 000000000..e90606a23
--- /dev/null
+++ b/multi_page_viewer.html
@@ -0,0 +1,55 @@
+
+
+
+pdf.js Multi-Page Viewer
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Previous/Next
+
+
+
+ /
+ --
+ Page Number
+
+
+
+ Zoom
+
+
+
+
+ Open File
+
+
+
+
+
+
+
+
+
diff --git a/multi_page_viewer.js b/multi_page_viewer.js
new file mode 100644
index 000000000..b2c0dc3ed
--- /dev/null
+++ b/multi_page_viewer.js
@@ -0,0 +1,525 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+
+"use strict";
+
+var pageTimeout;
+
+var PDFViewer = {
+ queryParams: {},
+
+ element: null,
+
+ sidebarContentView: null,
+
+ previousPageButton: null,
+ nextPageButton: null,
+ pageNumberInput: null,
+ scaleSelect: null,
+ fileInput: null,
+
+ willJumpToPage: false,
+
+ pdf: null,
+
+ url: 'compressed.tracemonkey-pldi-09.pdf',
+ pageNumber: 1,
+ numberOfPages: 1,
+
+ scale: 1.0,
+
+ pageWidth: function(page) {
+ return page.mediaBox[2] * PDFViewer.scale;
+ },
+
+ pageHeight: function(page) {
+ return page.mediaBox[3] * PDFViewer.scale;
+ },
+
+ lastPagesDrawn: [],
+
+ visiblePages: function() {
+ const pageBottomMargin = 20;
+ var windowTop = window.pageYOffset;
+ var windowBottom = window.pageYOffset + window.innerHeight;
+
+ var pageHeight, page;
+ var i, n = PDFViewer.numberOfPages, currentHeight = 0;
+ for (i = 1; i <= n; i++) {
+ var page = PDFViewer.pdf.getPage(i);
+ pageHeight = PDFViewer.pageHeight(page) + pageBottomMargin;
+ if (currentHeight + pageHeight > windowTop)
+ break;
+ currentHeight += pageHeight;
+ }
+
+ var pages = [];
+ for (; i <= n && currentHeight < windowBottom; i++) {
+ var page = PDFViewer.pdf.getPage(i);
+ pageHeight = PDFViewer.pageHeight(page) + pageBottomMargin;
+ currentHeight += pageHeight;
+ pages.push(i);
+ }
+
+ return pages;
+ },
+
+ createThumbnail: function(num) {
+ if (PDFViewer.sidebarContentView) {
+ var anchor = document.createElement('a');
+ anchor.href = '#' + num;
+
+ var containerDiv = document.createElement('div');
+ containerDiv.id = 'thumbnailContainer' + num;
+ containerDiv.className = 'thumbnail';
+
+ var pageNumberDiv = document.createElement('div');
+ pageNumberDiv.className = 'thumbnailPageNumber';
+ pageNumberDiv.innerHTML = '' + num;
+
+ anchor.appendChild(containerDiv);
+ PDFViewer.sidebarContentView.appendChild(anchor);
+ PDFViewer.sidebarContentView.appendChild(pageNumberDiv);
+ }
+ },
+
+ removeThumbnail: function(num) {
+ var div = document.getElementById('thumbnailContainer' + num);
+
+ if (div) {
+ while (div.hasChildNodes()) {
+ div.removeChild(div.firstChild);
+ }
+ }
+ },
+
+ drawThumbnail: function(num) {
+ if (!PDFViewer.pdf)
+ return;
+
+ var div = document.getElementById('thumbnailContainer' + num);
+
+ if (div && !div.hasChildNodes()) {
+ var page = PDFViewer.pdf.getPage(num);
+ var canvas = document.createElement('canvas');
+
+ canvas.id = 'thumbnail' + num;
+ canvas.mozOpaque = true;
+
+ // Canvas dimensions must be specified in CSS pixels. CSS pixels
+ // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
+ canvas.width = 104;
+ canvas.height = 134;
+ div.appendChild(canvas);
+
+ var ctx = canvas.getContext('2d');
+ ctx.save();
+ ctx.fillStyle = 'rgb(255, 255, 255)';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.restore();
+
+ var gfx = new CanvasGraphics(ctx);
+
+ // page.compile will collect all fonts for us, once we have loaded them
+ // we can trigger the actual page rendering with page.display
+ var fonts = [];
+ page.compile(gfx, fonts);
+
+ var loadFont = function() {
+ if (!FontLoader.bind(fonts)) {
+ pageTimeout = window.setTimeout(loadFont, 10);
+ return;
+ }
+ page.display(gfx);
+ }
+ loadFont();
+ }
+ },
+
+ createPage: function(num) {
+ var page = PDFViewer.pdf.getPage(num);
+
+ var anchor = document.createElement('a');
+ anchor.name = '' + num;
+
+ var div = document.createElement('div');
+ div.id = 'pageContainer' + num;
+ div.className = 'page';
+ div.style.width = PDFViewer.pageWidth(page) + 'px';
+ div.style.height = PDFViewer.pageHeight(page) + 'px';
+
+ PDFViewer.element.appendChild(anchor);
+ PDFViewer.element.appendChild(div);
+ },
+
+ removePage: function(num) {
+ var div = document.getElementById('pageContainer' + num);
+
+ if (div) {
+ while (div.hasChildNodes()) {
+ div.removeChild(div.firstChild);
+ }
+ }
+ },
+
+ drawPage: function(num) {
+ if (!PDFViewer.pdf)
+ return;
+
+ var div = document.getElementById('pageContainer' + num);
+
+ if (div && !div.hasChildNodes()) {
+ var page = PDFViewer.pdf.getPage(num);
+ var canvas = document.createElement('canvas');
+
+ canvas.id = 'page' + num;
+ canvas.mozOpaque = true;
+
+ // Canvas dimensions must be specified in CSS pixels. CSS pixels
+ // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
+ canvas.width = PDFViewer.pageWidth(page);
+ canvas.height = PDFViewer.pageHeight(page);
+ div.appendChild(canvas);
+
+ var ctx = canvas.getContext('2d');
+ ctx.save();
+ ctx.fillStyle = 'rgb(255, 255, 255)';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.restore();
+
+ var gfx = new CanvasGraphics(ctx);
+
+ // page.compile will collect all fonts for us, once we have loaded them
+ // we can trigger the actual page rendering with page.display
+ var fonts = [];
+ page.compile(gfx, fonts);
+
+ var loadFont = function() {
+ if (!FontLoader.bind(fonts)) {
+ pageTimeout = window.setTimeout(loadFont, 10);
+ return;
+ }
+ page.display(gfx);
+ }
+ loadFont();
+ }
+ },
+
+ changeScale: function(num) {
+ while (PDFViewer.element.hasChildNodes()) {
+ PDFViewer.element.removeChild(PDFViewer.element.firstChild);
+ }
+
+ PDFViewer.scale = num / 100;
+
+ var i;
+
+ if (PDFViewer.pdf) {
+ for (i = 1; i <= PDFViewer.numberOfPages; i++) {
+ PDFViewer.createThumbnail(i);
+ PDFViewer.createPage(i);
+ }
+ }
+
+ for (i = 0; i < PDFViewer.scaleSelect.childNodes; i++) {
+ var option = PDFViewer.scaleSelect.childNodes[i];
+
+ if (option.value == num) {
+ if (!option.selected) {
+ option.selected = 'selected';
+ }
+ } else {
+ if (option.selected) {
+ option.removeAttribute('selected');
+ }
+ }
+ }
+
+ PDFViewer.scaleSelect.value = Math.floor(PDFViewer.scale * 100) + '%';
+
+ // Clear the array of the last pages drawn to force a redraw.
+ PDFViewer.lastPagesDrawn = [];
+
+ // Jump the scroll position to the correct page.
+ PDFViewer.goToPage(PDFViewer.pageNumber);
+ },
+
+ goToPage: function(num) {
+ if (1 <= num && num <= PDFViewer.numberOfPages) {
+ PDFViewer.pageNumber = num;
+ PDFViewer.pageNumberInput.value = PDFViewer.pageNumber;
+ PDFViewer.willJumpToPage = true;
+
+ document.location.hash = PDFViewer.pageNumber;
+
+ PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? 'disabled' : '';
+ PDFViewer.nextPageButton.className = (PDFViewer.pageNumber === PDFViewer.numberOfPages) ? 'disabled' : '';
+ }
+ },
+
+ goToPreviousPage: function() {
+ if (PDFViewer.pageNumber > 1) {
+ PDFViewer.goToPage(--PDFViewer.pageNumber);
+ }
+ },
+
+ goToNextPage: function() {
+ if (PDFViewer.pageNumber < PDFViewer.numberOfPages) {
+ PDFViewer.goToPage(++PDFViewer.pageNumber);
+ }
+ },
+
+ openURL: function(url) {
+ PDFViewer.url = url;
+ document.title = url;
+
+ var req = new XMLHttpRequest();
+ req.open('GET', url);
+ req.mozResponseType = req.responseType = 'arraybuffer';
+ req.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200;
+
+ req.onreadystatechange = function() {
+ if (req.readyState === 4 && req.status === req.expected) {
+ var data = req.mozResponseArrayBuffer || req.mozResponse || req.responseArrayBuffer || req.response;
+
+ PDFViewer.readPDF(data);
+ }
+ };
+
+ req.send(null);
+ },
+
+ readPDF: function(data) {
+ while (PDFViewer.element.hasChildNodes()) {
+ PDFViewer.element.removeChild(PDFViewer.element.firstChild);
+ }
+
+ while (PDFViewer.sidebarContentView.hasChildNodes()) {
+ PDFViewer.sidebarContentView.removeChild(PDFViewer.sidebarContentView.firstChild);
+ }
+
+ PDFViewer.pdf = new PDFDoc(new Stream(data));
+ PDFViewer.numberOfPages = PDFViewer.pdf.numPages;
+ document.getElementById('numPages').innerHTML = PDFViewer.numberOfPages.toString();
+
+ for (var i = 1; i <= PDFViewer.numberOfPages; i++) {
+ PDFViewer.createPage(i);
+ }
+
+ if (PDFViewer.numberOfPages > 0) {
+ PDFViewer.drawPage(1);
+ document.location.hash = 1;
+
+ setTimeout(function() {
+ for (var i = 1; i <= PDFViewer.numberOfPages; i++) {
+ PDFViewer.createThumbnail(i);
+ PDFViewer.drawThumbnail(i);
+ }
+ }, 500);
+ }
+
+ PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? 'disabled' : '';
+ PDFViewer.nextPageButton.className = (PDFViewer.pageNumber === PDFViewer.numberOfPages) ? 'disabled' : '';
+ }
+};
+
+window.onload = function() {
+ // Parse the URL query parameters into a cached object.
+ PDFViewer.queryParams = function() {
+ var qs = window.location.search.substring(1);
+ var kvs = qs.split('&');
+ var params = {};
+
+ for (var i = 0; i < kvs.length; ++i) {
+ var kv = kvs[i].split('=');
+ params[unescape(kv[0])] = unescape(kv[1]);
+ }
+
+ return params;
+ }();
+
+ PDFViewer.element = document.getElementById('viewer');
+
+ PDFViewer.sidebarContentView = document.getElementById('sidebarContentView');
+
+ PDFViewer.pageNumberInput = document.getElementById('pageNumber');
+ PDFViewer.pageNumberInput.onkeydown = function(evt) {
+ var charCode = evt.charCode || evt.keyCode;
+
+ // Up arrow key.
+ if (charCode === 38) {
+ PDFViewer.goToNextPage();
+ this.select();
+ }
+
+ // Down arrow key.
+ else if (charCode === 40) {
+ PDFViewer.goToPreviousPage();
+ this.select();
+ }
+
+ // All other non-numeric keys (excluding Left arrow, Right arrow,
+ // Backspace, and Delete keys).
+ else if ((charCode < 48 || charCode > 57) &&
+ charCode !== 8 && // Backspace
+ charCode !== 46 && // Delete
+ charCode !== 37 && // Left arrow
+ charCode !== 39 // Right arrow
+ ) {
+ return false;
+ }
+
+ return true;
+ };
+ PDFViewer.pageNumberInput.onkeyup = function(evt) {
+ var charCode = evt.charCode || evt.keyCode;
+
+ // All numeric keys, Backspace, and Delete.
+ if ((charCode >= 48 && charCode <= 57) ||
+ charCode === 8 || // Backspace
+ charCode === 46 // Delete
+ ) {
+ PDFViewer.goToPage(this.value);
+ }
+
+ this.focus();
+ };
+
+ PDFViewer.previousPageButton = document.getElementById('previousPageButton');
+ PDFViewer.previousPageButton.onclick = function(evt) {
+ if (this.className.indexOf('disabled') === -1) {
+ PDFViewer.goToPreviousPage();
+ }
+ };
+ PDFViewer.previousPageButton.onmousedown = function(evt) {
+ if (this.className.indexOf('disabled') === -1) {
+ this.className = 'down';
+ }
+ };
+ PDFViewer.previousPageButton.onmouseup = function(evt) {
+ this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
+ };
+ PDFViewer.previousPageButton.onmouseout = function(evt) {
+ this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
+ };
+
+ PDFViewer.nextPageButton = document.getElementById('nextPageButton');
+ PDFViewer.nextPageButton.onclick = function(evt) {
+ if (this.className.indexOf('disabled') === -1) {
+ PDFViewer.goToNextPage();
+ }
+ };
+ PDFViewer.nextPageButton.onmousedown = function(evt) {
+ if (this.className.indexOf('disabled') === -1) {
+ this.className = 'down';
+ }
+ };
+ PDFViewer.nextPageButton.onmouseup = function(evt) {
+ this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
+ };
+ PDFViewer.nextPageButton.onmouseout = function(evt) {
+ this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
+ };
+
+ PDFViewer.scaleSelect = document.getElementById('scaleSelect');
+ PDFViewer.scaleSelect.onchange = function(evt) {
+ PDFViewer.changeScale(parseInt(this.value));
+ };
+
+ if (window.File && window.FileReader && window.FileList && window.Blob) {
+ var openFileButton = document.getElementById('openFileButton');
+ openFileButton.onclick = function(evt) {
+ if (this.className.indexOf('disabled') === -1) {
+ PDFViewer.fileInput.click();
+ }
+ };
+ openFileButton.onmousedown = function(evt) {
+ if (this.className.indexOf('disabled') === -1) {
+ this.className = 'down';
+ }
+ };
+ openFileButton.onmouseup = function(evt) {
+ this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
+ };
+ openFileButton.onmouseout = function(evt) {
+ this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
+ };
+
+ PDFViewer.fileInput = document.getElementById('fileInput');
+ PDFViewer.fileInput.onchange = function(evt) {
+ var files = evt.target.files;
+
+ if (files.length > 0) {
+ var file = files[0];
+ var fileReader = new FileReader();
+
+ document.title = file.name;
+
+ // Read the local file into a Uint8Array.
+ fileReader.onload = function(evt) {
+ var data = evt.target.result;
+ var buffer = new ArrayBuffer(data.length);
+ var uint8Array = new Uint8Array(buffer);
+
+ for (var i = 0; i < data.length; i++) {
+ uint8Array[i] = data.charCodeAt(i);
+ }
+
+ PDFViewer.readPDF(uint8Array);
+ };
+
+ // Read as a binary string since "readAsArrayBuffer" is not yet
+ // implemented in Firefox.
+ fileReader.readAsBinaryString(file);
+ }
+ };
+ PDFViewer.fileInput.value = null;
+ } else {
+ document.getElementById('fileWrapper').style.display = 'none';
+ }
+
+ PDFViewer.pageNumber = parseInt(PDFViewer.queryParams.page) || PDFViewer.pageNumber;
+ PDFViewer.scale = parseInt(PDFViewer.scaleSelect.value) / 100 || 1.0;
+
+ PDFViewer.openURL(PDFViewer.queryParams.file || PDFViewer.url);
+
+ window.onscroll = function(evt) {
+ var lastPagesDrawn = PDFViewer.lastPagesDrawn;
+ var visiblePages = PDFViewer.visiblePages();
+
+ var pagesToDraw = [];
+ var pagesToKeep = [];
+ var pagesToRemove = [];
+
+ var i;
+
+ // Determine which visible pages were not previously drawn.
+ for (i = 0; i < visiblePages.length; i++) {
+ if (lastPagesDrawn.indexOf(visiblePages[i]) === -1) {
+ pagesToDraw.push(visiblePages[i]);
+ PDFViewer.drawPage(visiblePages[i]);
+ } else {
+ pagesToKeep.push(visiblePages[i]);
+ }
+ }
+
+ // Determine which previously drawn pages are no longer visible.
+ for (i = 0; i < lastPagesDrawn.length; i++) {
+ if (visiblePages.indexOf(lastPagesDrawn[i]) === -1) {
+ pagesToRemove.push(lastPagesDrawn[i]);
+ PDFViewer.removePage(lastPagesDrawn[i]);
+ }
+ }
+
+ PDFViewer.lastPagesDrawn = pagesToDraw.concat(pagesToKeep);
+
+ // Update the page number input with the current page number.
+ if (!PDFViewer.willJumpToPage && visiblePages.length > 0) {
+ PDFViewer.pageNumber = PDFViewer.pageNumberInput.value = visiblePages[0];
+ PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? 'disabled' : '';
+ PDFViewer.nextPageButton.className = (PDFViewer.pageNumber === PDFViewer.numberOfPages) ? 'disabled' : '';
+ } else {
+ PDFViewer.willJumpToPage = false;
+ }
+ };
+};
diff --git a/pdf.js b/pdf.js
index 8b9fefb2c..bdb88a4a4 100644
--- a/pdf.js
+++ b/pdf.js
@@ -56,9 +56,17 @@ function bytesToString(bytes) {
return str;
}
+function stringToBytes(str) {
+ var length = str.length;
+ var bytes = new Uint8Array(length);
+ for (var n = 0; n < length; ++n)
+ bytes[n] = str.charCodeAt(n) & 0xFF;
+ return bytes;
+}
+
var Stream = (function() {
function constructor(arrayBuffer, start, length, dict) {
- this.bytes = Uint8Array(arrayBuffer);
+ this.bytes = new Uint8Array(arrayBuffer);
this.start = start || 0;
this.pos = this.start;
this.end = (start + length) || this.bytes.length;
@@ -71,14 +79,14 @@ var Stream = (function() {
get length() {
return this.end - this.start;
},
- getByte: function() {
+ getByte: function stream_getByte() {
if (this.pos >= this.end)
- return;
+ return null;
return this.bytes[this.pos++];
},
// returns subarray of original buffer
// should only be read
- getBytes: function(length) {
+ getBytes: function stream_getBytes(length) {
var bytes = this.bytes;
var pos = this.pos;
var strEnd = this.end;
@@ -93,28 +101,28 @@ var Stream = (function() {
this.pos = end;
return bytes.subarray(pos, end);
},
- lookChar: function() {
+ lookChar: function stream_lookChar() {
if (this.pos >= this.end)
- return;
+ return null;
return String.fromCharCode(this.bytes[this.pos]);
},
- getChar: function() {
+ getChar: function stream_getChar() {
if (this.pos >= this.end)
- return;
+ return null;
return String.fromCharCode(this.bytes[this.pos++]);
},
- skip: function(n) {
+ skip: function stream_skip(n) {
if (!n)
n = 1;
this.pos += n;
},
- reset: function() {
+ reset: function stream_reset() {
this.pos = this.start;
},
- moveStart: function() {
+ moveStart: function stream_moveStart() {
this.start = this.pos;
},
- makeSubStream: function(start, length, dict) {
+ makeSubStream: function stream_makeSubstream(start, length, dict) {
return new Stream(this.bytes.buffer, start, length, dict);
}
};
@@ -125,7 +133,7 @@ var Stream = (function() {
var StringStream = (function() {
function constructor(str) {
var length = str.length;
- var bytes = Uint8Array(length);
+ var bytes = new Uint8Array(length);
for (var n = 0; n < length; ++n)
bytes[n] = str.charCodeAt(n);
Stream.call(this, bytes);
@@ -146,7 +154,7 @@ var DecodeStream = (function() {
}
constructor.prototype = {
- ensureBuffer: function(requested) {
+ ensureBuffer: function decodestream_ensureBuffer(requested) {
var buffer = this.buffer;
var current = buffer ? buffer.byteLength : 0;
if (requested < current)
@@ -154,21 +162,21 @@ var DecodeStream = (function() {
var size = 512;
while (size < requested)
size <<= 1;
- var buffer2 = Uint8Array(size);
+ var buffer2 = new Uint8Array(size);
for (var i = 0; i < current; ++i)
buffer2[i] = buffer[i];
return this.buffer = buffer2;
},
- getByte: function() {
+ getByte: function decodestream_getByte() {
var pos = this.pos;
while (this.bufferLength <= pos) {
if (this.eof)
- return;
+ return null;
this.readBlock();
}
return this.buffer[this.pos++];
},
- getBytes: function(length) {
+ getBytes: function decodestream_getBytes(length) {
var pos = this.pos;
if (length) {
@@ -191,25 +199,25 @@ var DecodeStream = (function() {
this.pos = end;
return this.buffer.subarray(pos, end)
},
- lookChar: function() {
+ lookChar: function decodestream_lookChar() {
var pos = this.pos;
while (this.bufferLength <= pos) {
if (this.eof)
- return;
+ return null;
this.readBlock();
}
return String.fromCharCode(this.buffer[this.pos]);
},
- getChar: function() {
+ getChar: function decodestream_getChar() {
var pos = this.pos;
while (this.bufferLength <= pos) {
if (this.eof)
- return;
+ return null;
this.readBlock();
}
return String.fromCharCode(this.buffer[this.pos++]);
},
- skip: function(n) {
+ skip: function decodestream_skip(n) {
if (!n)
n = 1;
this.pos += n;
@@ -220,13 +228,50 @@ var DecodeStream = (function() {
})();
+var FakeStream = (function() {
+ function constructor(stream) {
+ this.dict = stream.dict;
+ DecodeStream.call(this);
+ };
+
+ constructor.prototype = Object.create(DecodeStream.prototype);
+ constructor.prototype.readBlock = function() {
+ var bufferLength = this.bufferLength;
+ bufferLength += 1024;
+ var buffer = this.ensureBuffer(bufferLength);
+ this.bufferLength = bufferLength;
+ };
+ constructor.prototype.getBytes = function(length) {
+ var pos = this.pos;
+
+ if (length) {
+ this.ensureBuffer(pos + length);
+ var end = pos + length;
+
+ while (!this.eof && this.bufferLength < end)
+ this.readBlock();
+
+ var bufEnd = this.bufferLength;
+ if (end > bufEnd)
+ end = bufEnd;
+ } else {
+ this.eof = true;
+ var end = this.bufferLength;
+ }
+
+ this.pos = end;
+ return this.buffer.subarray(pos, end)
+ };
+
+ return constructor;
+})();
var FlateStream = (function() {
- const codeLenCodeMap = Uint32Array([
+ var codeLenCodeMap = new Uint32Array([
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
]);
- const lengthDecode = Uint32Array([
+ var lengthDecode = new Uint32Array([
0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009,
0x0000a, 0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017,
0x2001b, 0x2001f, 0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043,
@@ -234,7 +279,7 @@ var FlateStream = (function() {
0x00102, 0x00102, 0x00102
]);
- const distDecode = Uint32Array([
+ var distDecode = new Uint32Array([
0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009,
0x2000d, 0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061,
0x60081, 0x600c1, 0x70101, 0x70181, 0x80201, 0x80301, 0x90401,
@@ -242,7 +287,7 @@ var FlateStream = (function() {
0xd4001, 0xd6001
]);
- const fixedLitCodeTab = [Uint32Array([
+ var fixedLitCodeTab = [new Uint32Array([
0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030,
0x900c0, 0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080,
0x80040, 0x900e0, 0x70104, 0x80058, 0x80018, 0x90090, 0x70114,
@@ -319,7 +364,7 @@ var FlateStream = (function() {
0x900ff
]), 9];
- const fixedDistCodeTab = [Uint32Array([
+ var fixedDistCodeTab = [new Uint32Array([
0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c,
0x5001c, 0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016,
0x5000e, 0x00000, 0x50001, 0x50011, 0x50009, 0x50019, 0x50005,
@@ -410,7 +455,7 @@ var FlateStream = (function() {
// build the table
var size = 1 << maxLen;
- var codes = Uint32Array(size);
+ var codes = new Uint32Array(size);
for (var len = 1, code = 0, skip = 2;
len <= maxLen;
++len, code <<= 1, skip <<= 1) {
@@ -442,17 +487,17 @@ var FlateStream = (function() {
array[i++] = what;
}
- var bytes = this.bytes;
- var bytesPos = this.bytesPos;
-
// read block header
var hdr = this.getBits(3);
if (hdr & 1)
this.eof = true;
hdr >>= 1;
- var b;
if (hdr == 0) { // uncompressed block
+ var bytes = this.bytes;
+ var bytesPos = this.bytesPos;
+ var b;
+
if (typeof (b = bytes[bytesPos++]) == "undefined")
error("Bad block header in flate stream");
var blockLen = b;
@@ -465,18 +510,24 @@ var FlateStream = (function() {
if (typeof (b = bytes[bytesPos++]) == "undefined")
error("Bad block header in flate stream");
check |= (b << 8);
- if (check != (~this.blockLen & 0xffff))
+ if (check != (~blockLen & 0xffff))
error("Bad uncompressed block length in flate stream");
+
+ this.codeBuf = 0;
+ this.codeSize = 0;
+
var bufferLength = this.bufferLength;
var buffer = this.ensureBuffer(bufferLength + blockLen);
- this.bufferLength = bufferLength + blockLen;
- for (var n = bufferLength; n < blockLen; ++n) {
+ var end = bufferLength + blockLen;
+ this.bufferLength = end;
+ for (var n = bufferLength; n < end; ++n) {
if (typeof (b = bytes[bytesPos++]) == "undefined") {
this.eof = true;
break;
}
buffer[n] = b;
}
+ this.bytesPos = bytesPos;
return;
}
@@ -592,14 +643,12 @@ var PredictorStream = (function() {
var rowBytes = this.rowBytes = (columns * colors * bits + 7) >> 3;
DecodeStream.call(this);
+ return this;
}
constructor.prototype = Object.create(DecodeStream.prototype);
constructor.prototype.readBlockTiff = function() {
- var buffer = this.buffer;
- var pos = this.pos;
-
var rowBytes = this.rowBytes;
var pixBytes = this.pixBytes;
@@ -660,9 +709,6 @@ var PredictorStream = (function() {
this.bufferLength += rowBytes;
};
constructor.prototype.readBlockPng = function() {
- var buffer = this.buffer;
- var pos = this.pos;
-
var rowBytes = this.rowBytes;
var pixBytes = this.pixBytes;
@@ -670,7 +716,7 @@ var PredictorStream = (function() {
var rawBytes = this.stream.getBytes(rowBytes);
var bufferLength = this.bufferLength;
- var buffer = this.ensureBuffer(bufferLength + pixBytes);
+ var buffer = this.ensureBuffer(bufferLength + rowBytes);
var currentRow = buffer.subarray(bufferLength, bufferLength + rowBytes);
var prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength);
@@ -762,11 +808,34 @@ var JpegStream = (function() {
return constructor;
})();
var DecryptStream = (function() {
- function constructor(str, fileKey, encAlgorithm, keyLength) {
- TODO("decrypt stream is not implemented");
+ function constructor(str, decrypt) {
+ this.str = str;
+ this.dict = str.dict;
+ this.decrypt = decrypt;
+
+ DecodeStream.call(this);
}
- constructor.prototype = Stream.prototype;
+ const chunkSize = 512;
+
+ constructor.prototype = Object.create(DecodeStream.prototype);
+ constructor.prototype.readBlock = function() {
+ var chunk = this.str.getBytes(chunkSize);
+ if (!chunk || chunk.length == 0) {
+ this.eof = true;
+ return;
+ }
+ var decrypt = this.decrypt;
+ chunk = decrypt(chunk);
+
+ var bufferLength = this.bufferLength;
+ var i, n = chunk.length;
+ var buffer = this.ensureBuffer(bufferLength + n);
+ for (i = 0; i < n; i++)
+ buffer[bufferLength++] = chunk[i];
+ this.bufferLength = bufferLength;
+ this.eof = n < chunkSize;
+ };
return constructor;
})();
@@ -782,8 +851,8 @@ var Ascii85Stream = (function() {
constructor.prototype = Object.create(DecodeStream.prototype);
constructor.prototype.readBlock = function() {
- const tildaCode = "~".charCodeAt(0);
- const zCode = "z".charCodeAt(0);
+ var tildaCode = "~".charCodeAt(0);
+ var zCode = "z".charCodeAt(0);
var str = this.str;
var c = str.getByte();
@@ -839,6 +908,984 @@ var Ascii85Stream = (function() {
return constructor;
})();
+var CCITTFaxStream = (function() {
+
+ const ccittEOL = -2;
+ const twoDimPass = 0;
+ const twoDimHoriz = 1;
+ const twoDimVert0 = 2;
+ const twoDimVertR1 = 3;
+ const twoDimVertL1 = 4;
+ const twoDimVertR2 = 5;
+ const twoDimVertL2 = 6;
+ const twoDimVertR3 = 7;
+ const twoDimVertL3 = 8;
+
+ const twoDimTable = [
+ [-1, -1], [-1, -1], // 000000x
+ [7, twoDimVertL3], // 0000010
+ [7, twoDimVertR3], // 0000011
+ [6, twoDimVertL2], [6, twoDimVertL2], // 000010x
+ [6, twoDimVertR2], [6, twoDimVertR2], // 000011x
+ [4, twoDimPass], [4, twoDimPass], // 0001xxx
+ [4, twoDimPass], [4, twoDimPass],
+ [4, twoDimPass], [4, twoDimPass],
+ [4, twoDimPass], [4, twoDimPass],
+ [3, twoDimHoriz], [3, twoDimHoriz], // 001xxxx
+ [3, twoDimHoriz], [3, twoDimHoriz],
+ [3, twoDimHoriz], [3, twoDimHoriz],
+ [3, twoDimHoriz], [3, twoDimHoriz],
+ [3, twoDimHoriz], [3, twoDimHoriz],
+ [3, twoDimHoriz], [3, twoDimHoriz],
+ [3, twoDimHoriz], [3, twoDimHoriz],
+ [3, twoDimHoriz], [3, twoDimHoriz],
+ [3, twoDimVertL1], [3, twoDimVertL1], // 010xxxx
+ [3, twoDimVertL1], [3, twoDimVertL1],
+ [3, twoDimVertL1], [3, twoDimVertL1],
+ [3, twoDimVertL1], [3, twoDimVertL1],
+ [3, twoDimVertL1], [3, twoDimVertL1],
+ [3, twoDimVertL1], [3, twoDimVertL1],
+ [3, twoDimVertL1], [3, twoDimVertL1],
+ [3, twoDimVertL1], [3, twoDimVertL1],
+ [3, twoDimVertR1], [3, twoDimVertR1], // 011xxxx
+ [3, twoDimVertR1], [3, twoDimVertR1],
+ [3, twoDimVertR1], [3, twoDimVertR1],
+ [3, twoDimVertR1], [3, twoDimVertR1],
+ [3, twoDimVertR1], [3, twoDimVertR1],
+ [3, twoDimVertR1], [3, twoDimVertR1],
+ [3, twoDimVertR1], [3, twoDimVertR1],
+ [3, twoDimVertR1], [3, twoDimVertR1],
+ [1, twoDimVert0], [1, twoDimVert0], // 1xxxxxx
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0],
+ [1, twoDimVert0], [1, twoDimVert0]
+ ];
+
+ const whiteTable1 = [
+ [-1, -1], // 00000
+ [12, ccittEOL], // 00001
+ [-1, -1], [-1, -1], // 0001x
+ [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 001xx
+ [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 010xx
+ [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 011xx
+ [11, 1792], [11, 1792], // 1000x
+ [12, 1984], // 10010
+ [12, 2048], // 10011
+ [12, 2112], // 10100
+ [12, 2176], // 10101
+ [12, 2240], // 10110
+ [12, 2304], // 10111
+ [11, 1856], [11, 1856], // 1100x
+ [11, 1920], [11, 1920], // 1101x
+ [12, 2368], // 11100
+ [12, 2432], // 11101
+ [12, 2496], // 11110
+ [12, 2560] // 11111
+ ];
+
+ const whiteTable2 = [
+ [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000000xx
+ [8, 29], [8, 29], // 00000010x
+ [8, 30], [8, 30], // 00000011x
+ [8, 45], [8, 45], // 00000100x
+ [8, 46], [8, 46], // 00000101x
+ [7, 22], [7, 22], [7, 22], [7, 22], // 0000011xx
+ [7, 23], [7, 23], [7, 23], [7, 23], // 0000100xx
+ [8, 47], [8, 47], // 00001010x
+ [8, 48], [8, 48], // 00001011x
+ [6, 13], [6, 13], [6, 13], [6, 13], // 000011xxx
+ [6, 13], [6, 13], [6, 13], [6, 13],
+ [7, 20], [7, 20], [7, 20], [7, 20], // 0001000xx
+ [8, 33], [8, 33], // 00010010x
+ [8, 34], [8, 34], // 00010011x
+ [8, 35], [8, 35], // 00010100x
+ [8, 36], [8, 36], // 00010101x
+ [8, 37], [8, 37], // 00010110x
+ [8, 38], [8, 38], // 00010111x
+ [7, 19], [7, 19], [7, 19], [7, 19], // 0001100xx
+ [8, 31], [8, 31], // 00011010x
+ [8, 32], [8, 32], // 00011011x
+ [6, 1], [6, 1], [6, 1], [6, 1], // 000111xxx
+ [6, 1], [6, 1], [6, 1], [6, 1],
+ [6, 12], [6, 12], [6, 12], [6, 12], // 001000xxx
+ [6, 12], [6, 12], [6, 12], [6, 12],
+ [8, 53], [8, 53], // 00100100x
+ [8, 54], [8, 54], // 00100101x
+ [7, 26], [7, 26], [7, 26], [7, 26], // 0010011xx
+ [8, 39], [8, 39], // 00101000x
+ [8, 40], [8, 40], // 00101001x
+ [8, 41], [8, 41], // 00101010x
+ [8, 42], [8, 42], // 00101011x
+ [8, 43], [8, 43], // 00101100x
+ [8, 44], [8, 44], // 00101101x
+ [7, 21], [7, 21], [7, 21], [7, 21], // 0010111xx
+ [7, 28], [7, 28], [7, 28], [7, 28], // 0011000xx
+ [8, 61], [8, 61], // 00110010x
+ [8, 62], [8, 62], // 00110011x
+ [8, 63], [8, 63], // 00110100x
+ [8, 0], [8, 0], // 00110101x
+ [8, 320], [8, 320], // 00110110x
+ [8, 384], [8, 384], // 00110111x
+ [5, 10], [5, 10], [5, 10], [5, 10], // 00111xxxx
+ [5, 10], [5, 10], [5, 10], [5, 10],
+ [5, 10], [5, 10], [5, 10], [5, 10],
+ [5, 10], [5, 10], [5, 10], [5, 10],
+ [5, 11], [5, 11], [5, 11], [5, 11], // 01000xxxx
+ [5, 11], [5, 11], [5, 11], [5, 11],
+ [5, 11], [5, 11], [5, 11], [5, 11],
+ [5, 11], [5, 11], [5, 11], [5, 11],
+ [7, 27], [7, 27], [7, 27], [7, 27], // 0100100xx
+ [8, 59], [8, 59], // 01001010x
+ [8, 60], [8, 60], // 01001011x
+ [9, 1472], // 010011000
+ [9, 1536], // 010011001
+ [9, 1600], // 010011010
+ [9, 1728], // 010011011
+ [7, 18], [7, 18], [7, 18], [7, 18], // 0100111xx
+ [7, 24], [7, 24], [7, 24], [7, 24], // 0101000xx
+ [8, 49], [8, 49], // 01010010x
+ [8, 50], [8, 50], // 01010011x
+ [8, 51], [8, 51], // 01010100x
+ [8, 52], [8, 52], // 01010101x
+ [7, 25], [7, 25], [7, 25], [7, 25], // 0101011xx
+ [8, 55], [8, 55], // 01011000x
+ [8, 56], [8, 56], // 01011001x
+ [8, 57], [8, 57], // 01011010x
+ [8, 58], [8, 58], // 01011011x
+ [6, 192], [6, 192], [6, 192], [6, 192], // 010111xxx
+ [6, 192], [6, 192], [6, 192], [6, 192],
+ [6, 1664], [6, 1664], [6, 1664], [6, 1664], // 011000xxx
+ [6, 1664], [6, 1664], [6, 1664], [6, 1664],
+ [8, 448], [8, 448], // 01100100x
+ [8, 512], [8, 512], // 01100101x
+ [9, 704], // 011001100
+ [9, 768], // 011001101
+ [8, 640], [8, 640], // 01100111x
+ [8, 576], [8, 576], // 01101000x
+ [9, 832], // 011010010
+ [9, 896], // 011010011
+ [9, 960], // 011010100
+ [9, 1024], // 011010101
+ [9, 1088], // 011010110
+ [9, 1152], // 011010111
+ [9, 1216], // 011011000
+ [9, 1280], // 011011001
+ [9, 1344], // 011011010
+ [9, 1408], // 011011011
+ [7, 256], [7, 256], [7, 256], [7, 256], // 0110111xx
+ [4, 2], [4, 2], [4, 2], [4, 2], // 0111xxxxx
+ [4, 2], [4, 2], [4, 2], [4, 2],
+ [4, 2], [4, 2], [4, 2], [4, 2],
+ [4, 2], [4, 2], [4, 2], [4, 2],
+ [4, 2], [4, 2], [4, 2], [4, 2],
+ [4, 2], [4, 2], [4, 2], [4, 2],
+ [4, 2], [4, 2], [4, 2], [4, 2],
+ [4, 2], [4, 2], [4, 2], [4, 2],
+ [4, 3], [4, 3], [4, 3], [4, 3], // 1000xxxxx
+ [4, 3], [4, 3], [4, 3], [4, 3],
+ [4, 3], [4, 3], [4, 3], [4, 3],
+ [4, 3], [4, 3], [4, 3], [4, 3],
+ [4, 3], [4, 3], [4, 3], [4, 3],
+ [4, 3], [4, 3], [4, 3], [4, 3],
+ [4, 3], [4, 3], [4, 3], [4, 3],
+ [4, 3], [4, 3], [4, 3], [4, 3],
+ [5, 128], [5, 128], [5, 128], [5, 128], // 10010xxxx
+ [5, 128], [5, 128], [5, 128], [5, 128],
+ [5, 128], [5, 128], [5, 128], [5, 128],
+ [5, 128], [5, 128], [5, 128], [5, 128],
+ [5, 8], [5, 8], [5, 8], [5, 8], // 10011xxxx
+ [5, 8], [5, 8], [5, 8], [5, 8],
+ [5, 8], [5, 8], [5, 8], [5, 8],
+ [5, 8], [5, 8], [5, 8], [5, 8],
+ [5, 9], [5, 9], [5, 9], [5, 9], // 10100xxxx
+ [5, 9], [5, 9], [5, 9], [5, 9],
+ [5, 9], [5, 9], [5, 9], [5, 9],
+ [5, 9], [5, 9], [5, 9], [5, 9],
+ [6, 16], [6, 16], [6, 16], [6, 16], // 101010xxx
+ [6, 16], [6, 16], [6, 16], [6, 16],
+ [6, 17], [6, 17], [6, 17], [6, 17], // 101011xxx
+ [6, 17], [6, 17], [6, 17], [6, 17],
+ [4, 4], [4, 4], [4, 4], [4, 4], // 1011xxxxx
+ [4, 4], [4, 4], [4, 4], [4, 4],
+ [4, 4], [4, 4], [4, 4], [4, 4],
+ [4, 4], [4, 4], [4, 4], [4, 4],
+ [4, 4], [4, 4], [4, 4], [4, 4],
+ [4, 4], [4, 4], [4, 4], [4, 4],
+ [4, 4], [4, 4], [4, 4], [4, 4],
+ [4, 4], [4, 4], [4, 4], [4, 4],
+ [4, 5], [4, 5], [4, 5], [4, 5], // 1100xxxxx
+ [4, 5], [4, 5], [4, 5], [4, 5],
+ [4, 5], [4, 5], [4, 5], [4, 5],
+ [4, 5], [4, 5], [4, 5], [4, 5],
+ [4, 5], [4, 5], [4, 5], [4, 5],
+ [4, 5], [4, 5], [4, 5], [4, 5],
+ [4, 5], [4, 5], [4, 5], [4, 5],
+ [4, 5], [4, 5], [4, 5], [4, 5],
+ [6, 14], [6, 14], [6, 14], [6, 14], // 110100xxx
+ [6, 14], [6, 14], [6, 14], [6, 14],
+ [6, 15], [6, 15], [6, 15], [6, 15], // 110101xxx
+ [6, 15], [6, 15], [6, 15], [6, 15],
+ [5, 64], [5, 64], [5, 64], [5, 64], // 11011xxxx
+ [5, 64], [5, 64], [5, 64], [5, 64],
+ [5, 64], [5, 64], [5, 64], [5, 64],
+ [5, 64], [5, 64], [5, 64], [5, 64],
+ [4, 6], [4, 6], [4, 6], [4, 6], // 1110xxxxx
+ [4, 6], [4, 6], [4, 6], [4, 6],
+ [4, 6], [4, 6], [4, 6], [4, 6],
+ [4, 6], [4, 6], [4, 6], [4, 6],
+ [4, 6], [4, 6], [4, 6], [4, 6],
+ [4, 6], [4, 6], [4, 6], [4, 6],
+ [4, 6], [4, 6], [4, 6], [4, 6],
+ [4, 6], [4, 6], [4, 6], [4, 6],
+ [4, 7], [4, 7], [4, 7], [4, 7], // 1111xxxxx
+ [4, 7], [4, 7], [4, 7], [4, 7],
+ [4, 7], [4, 7], [4, 7], [4, 7],
+ [4, 7], [4, 7], [4, 7], [4, 7],
+ [4, 7], [4, 7], [4, 7], [4, 7],
+ [4, 7], [4, 7], [4, 7], [4, 7],
+ [4, 7], [4, 7], [4, 7], [4, 7],
+ [4, 7], [4, 7], [4, 7], [4, 7]
+ ];
+
+ const blackTable1 = [
+ [-1, -1], [-1, -1], // 000000000000x
+ [12, ccittEOL], [12, ccittEOL], // 000000000001x
+ [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000001xx
+ [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000010xx
+ [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000011xx
+ [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000100xx
+ [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000101xx
+ [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000110xx
+ [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000111xx
+ [11, 1792], [11, 1792], [11, 1792], [11, 1792], // 00000001000xx
+ [12, 1984], [12, 1984], // 000000010010x
+ [12, 2048], [12, 2048], // 000000010011x
+ [12, 2112], [12, 2112], // 000000010100x
+ [12, 2176], [12, 2176], // 000000010101x
+ [12, 2240], [12, 2240], // 000000010110x
+ [12, 2304], [12, 2304], // 000000010111x
+ [11, 1856], [11, 1856], [11, 1856], [11, 1856], // 00000001100xx
+ [11, 1920], [11, 1920], [11, 1920], [11, 1920], // 00000001101xx
+ [12, 2368], [12, 2368], // 000000011100x
+ [12, 2432], [12, 2432], // 000000011101x
+ [12, 2496], [12, 2496], // 000000011110x
+ [12, 2560], [12, 2560], // 000000011111x
+ [10, 18], [10, 18], [10, 18], [10, 18], // 0000001000xxx
+ [10, 18], [10, 18], [10, 18], [10, 18],
+ [12, 52], [12, 52], // 000000100100x
+ [13, 640], // 0000001001010
+ [13, 704], // 0000001001011
+ [13, 768], // 0000001001100
+ [13, 832], // 0000001001101
+ [12, 55], [12, 55], // 000000100111x
+ [12, 56], [12, 56], // 000000101000x
+ [13, 1280], // 0000001010010
+ [13, 1344], // 0000001010011
+ [13, 1408], // 0000001010100
+ [13, 1472], // 0000001010101
+ [12, 59], [12, 59], // 000000101011x
+ [12, 60], [12, 60], // 000000101100x
+ [13, 1536], // 0000001011010
+ [13, 1600], // 0000001011011
+ [11, 24], [11, 24], [11, 24], [11, 24], // 00000010111xx
+ [11, 25], [11, 25], [11, 25], [11, 25], // 00000011000xx
+ [13, 1664], // 0000001100100
+ [13, 1728], // 0000001100101
+ [12, 320], [12, 320], // 000000110011x
+ [12, 384], [12, 384], // 000000110100x
+ [12, 448], [12, 448], // 000000110101x
+ [13, 512], // 0000001101100
+ [13, 576], // 0000001101101
+ [12, 53], [12, 53], // 000000110111x
+ [12, 54], [12, 54], // 000000111000x
+ [13, 896], // 0000001110010
+ [13, 960], // 0000001110011
+ [13, 1024], // 0000001110100
+ [13, 1088], // 0000001110101
+ [13, 1152], // 0000001110110
+ [13, 1216], // 0000001110111
+ [10, 64], [10, 64], [10, 64], [10, 64], // 0000001111xxx
+ [10, 64], [10, 64], [10, 64], [10, 64]
+ ];
+
+ const blackTable2 = [
+ [8, 13], [8, 13], [8, 13], [8, 13], // 00000100xxxx
+ [8, 13], [8, 13], [8, 13], [8, 13],
+ [8, 13], [8, 13], [8, 13], [8, 13],
+ [8, 13], [8, 13], [8, 13], [8, 13],
+ [11, 23], [11, 23], // 00000101000x
+ [12, 50], // 000001010010
+ [12, 51], // 000001010011
+ [12, 44], // 000001010100
+ [12, 45], // 000001010101
+ [12, 46], // 000001010110
+ [12, 47], // 000001010111
+ [12, 57], // 000001011000
+ [12, 58], // 000001011001
+ [12, 61], // 000001011010
+ [12, 256], // 000001011011
+ [10, 16], [10, 16], [10, 16], [10, 16], // 0000010111xx
+ [10, 17], [10, 17], [10, 17], [10, 17], // 0000011000xx
+ [12, 48], // 000001100100
+ [12, 49], // 000001100101
+ [12, 62], // 000001100110
+ [12, 63], // 000001100111
+ [12, 30], // 000001101000
+ [12, 31], // 000001101001
+ [12, 32], // 000001101010
+ [12, 33], // 000001101011
+ [12, 40], // 000001101100
+ [12, 41], // 000001101101
+ [11, 22], [11, 22], // 00000110111x
+ [8, 14], [8, 14], [8, 14], [8, 14], // 00000111xxxx
+ [8, 14], [8, 14], [8, 14], [8, 14],
+ [8, 14], [8, 14], [8, 14], [8, 14],
+ [8, 14], [8, 14], [8, 14], [8, 14],
+ [7, 10], [7, 10], [7, 10], [7, 10], // 0000100xxxxx
+ [7, 10], [7, 10], [7, 10], [7, 10],
+ [7, 10], [7, 10], [7, 10], [7, 10],
+ [7, 10], [7, 10], [7, 10], [7, 10],
+ [7, 10], [7, 10], [7, 10], [7, 10],
+ [7, 10], [7, 10], [7, 10], [7, 10],
+ [7, 10], [7, 10], [7, 10], [7, 10],
+ [7, 10], [7, 10], [7, 10], [7, 10],
+ [7, 11], [7, 11], [7, 11], [7, 11], // 0000101xxxxx
+ [7, 11], [7, 11], [7, 11], [7, 11],
+ [7, 11], [7, 11], [7, 11], [7, 11],
+ [7, 11], [7, 11], [7, 11], [7, 11],
+ [7, 11], [7, 11], [7, 11], [7, 11],
+ [7, 11], [7, 11], [7, 11], [7, 11],
+ [7, 11], [7, 11], [7, 11], [7, 11],
+ [7, 11], [7, 11], [7, 11], [7, 11],
+ [9, 15], [9, 15], [9, 15], [9, 15], // 000011000xxx
+ [9, 15], [9, 15], [9, 15], [9, 15],
+ [12, 128], // 000011001000
+ [12, 192], // 000011001001
+ [12, 26], // 000011001010
+ [12, 27], // 000011001011
+ [12, 28], // 000011001100
+ [12, 29], // 000011001101
+ [11, 19], [11, 19], // 00001100111x
+ [11, 20], [11, 20], // 00001101000x
+ [12, 34], // 000011010010
+ [12, 35], // 000011010011
+ [12, 36], // 000011010100
+ [12, 37], // 000011010101
+ [12, 38], // 000011010110
+ [12, 39], // 000011010111
+ [11, 21], [11, 21], // 00001101100x
+ [12, 42], // 000011011010
+ [12, 43], // 000011011011
+ [10, 0], [10, 0], [10, 0], [10, 0], // 0000110111xx
+ [7, 12], [7, 12], [7, 12], [7, 12], // 0000111xxxxx
+ [7, 12], [7, 12], [7, 12], [7, 12],
+ [7, 12], [7, 12], [7, 12], [7, 12],
+ [7, 12], [7, 12], [7, 12], [7, 12],
+ [7, 12], [7, 12], [7, 12], [7, 12],
+ [7, 12], [7, 12], [7, 12], [7, 12],
+ [7, 12], [7, 12], [7, 12], [7, 12],
+ [7, 12], [7, 12], [7, 12], [7, 12]
+ ];
+
+ const blackTable3 = [
+ [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000xx
+ [6, 9], // 000100
+ [6, 8], // 000101
+ [5, 7], [5, 7], // 00011x
+ [4, 6], [4, 6], [4, 6], [4, 6], // 0010xx
+ [4, 5], [4, 5], [4, 5], [4, 5], // 0011xx
+ [3, 1], [3, 1], [3, 1], [3, 1], // 010xxx
+ [3, 1], [3, 1], [3, 1], [3, 1],
+ [3, 4], [3, 4], [3, 4], [3, 4], // 011xxx
+ [3, 4], [3, 4], [3, 4], [3, 4],
+ [2, 3], [2, 3], [2, 3], [2, 3], // 10xxxx
+ [2, 3], [2, 3], [2, 3], [2, 3],
+ [2, 3], [2, 3], [2, 3], [2, 3],
+ [2, 3], [2, 3], [2, 3], [2, 3],
+ [2, 2], [2, 2], [2, 2], [2, 2], // 11xxxx
+ [2, 2], [2, 2], [2, 2], [2, 2],
+ [2, 2], [2, 2], [2, 2], [2, 2],
+ [2, 2], [2, 2], [2, 2], [2, 2]
+ ];
+
+ function constructor(str, params) {
+ this.str = str;
+ this.dict = str.dict;
+
+ params = params || new Dict();
+
+ this.encoding = params.get("K") || 0;
+ this.eoline = params.get("EndOfLine") || false;
+ this.byteAlign = params.get("EncodedByteAlign") || false;
+ this.columns = params.get("Columns") || 1728;
+ this.rows = params.get("Rows") || 0;
+ var eoblock = params.get("EndOfBlock");
+ if (eoblock == null)
+ eoblock = true;
+ this.eoblock = eoblock;
+ this.black = params.get("BlackIs1") || false;
+
+ this.codingLine = new Uint32Array(this.columns + 1);
+ this.refLine = new Uint32Array(this.columns + 2);
+
+ this.codingLine[0] = this.columns;
+ this.codingPos = 0;
+
+ this.row = 0;
+ this.nextLine2D = this.encoding < 0;
+ this.inputBits = 0;
+ this.inputBuf;
+ this.outputBits = 0;
+ this.buf = EOF;
+
+ var code1;
+ while ((code1 = this.lookBits(12)) == 0) {
+ this.eatBits(1);
+ }
+ if (code1 == 1) {
+ this.eatBits(12);
+ }
+ if (this.encoding > 0) {
+ this.nextLine2D = !this.lookBits(1);
+ this.eatBits(1);
+ }
+
+ DecodeStream.call(this);
+ }
+
+ constructor.prototype = Object.create(DecodeStream.prototype);
+ constructor.prototype.readBlock = function() {
+ while (!this.eof) {
+ var c = this.lookChar();
+ this.buf = EOF;
+ this.ensureBuffer(this.bufferLength + 1);
+ this.buffer[this.bufferLength++] = c;
+ }
+ };
+ constructor.prototype.addPixels = function(a1, blackPixels) {
+ var codingLine = this.codingLine;
+ var codingPos = this.codingPos;
+
+ if (a1 > codingLine[codingPos]) {
+ if (a1 > this.columns) {
+ warn("row is wrong length");
+ this.err = true;
+ a1 = this.columns;
+ }
+ if ((codingPos & 1) ^ blackPixels) {
+ ++codingPos;
+ }
+
+ codingLine[codingPos] = a1;
+ }
+ this.codingPos = codingPos;
+ };
+ constructor.prototype.addPixelsNeg = function(a1, blackPixels) {
+ var codingLine = this.codingLine;
+ var codingPos = this.codingPos;
+
+ if (a1 > codingLine[codingPos]) {
+ if (a1 > this.columns) {
+ warn("row is wrong length");
+ this.err = true;
+ a1 = this.columns;
+ }
+ if ((codingPos & 1) ^ blackPixels)
+ ++codingPos;
+
+ codingLine[codingPos] = a1;
+ } else if (a1 < codingLine[codingPos]) {
+ if (a1 < 0) {
+ warn("invalid code");
+ this.err = true;
+ a1 = 0;
+ }
+ while (codingPos > 0 && a1 < codingLine[codingPos - 1])
+ --codingPos;
+ codingLine[codingPos] = a1;
+ }
+
+ this.codingPos = codingPos;
+ };
+ constructor.prototype.lookChar = function() {
+ var refLine = this.refLine;
+ var codingLine = this.codingLine;
+ var columns = this.columns;
+
+ var refPos, blackPixels, bits;
+
+ if (this.buf != EOF)
+ return buf;
+
+ if (this.outputBits == 0) {
+ if (this.eof)
+ return;
+
+ this.err = false;
+
+ if (this.nextLine2D) {
+ for (var i = 0; codingLine[i] < columns; ++i)
+ refLine[i] = codingLine[i];
+
+ refLine[i++] = columns;
+ refLine[i] = columns;
+ codingLine[0] = 0;
+ this.codingPos = 0;
+ refPos = 0;
+ blackPixels = 0;
+
+ while (codingLine[this.codingPos] < columns) {
+ var code1 = this.getTwoDimCode();
+ switch (code1) {
+ case twoDimPass:
+ this.addPixels(refLine[refPos + 1], blackPixels);
+ if (refLine[refPos + 1] < columns)
+ refPos += 2;
+ break;
+ case twoDimHoriz:
+ var code1 = 0, code2 = 0;
+ if (blackPixels) {
+ var code3;
+ do {
+ code1 += (code3 = this.getBlackCode());
+ } while (code3 >= 64);
+ do {
+ code2 += (code3 = this.getWhiteCode());
+ } while (code3 >= 64);
+ } else {
+ var code3;
+ do {
+ code1 += (code3 = this.getWhiteCode());
+ } while (code3 >= 64);
+ do {
+ code2 += (code3 = this.getBlackCode());
+ } while (code3 >= 64);
+ }
+ this.addPixels(codingLine[this.codingPos] + code1, blackPixels);
+ if (codingLine[this.codingPos] < columns) {
+ this.addPixels(codingLine[this.codingPos] + code2,
+ blackPixels ^ 1);
+ }
+ while (refLine[refPos] <= codingLine[this.codingPos]
+ && refLine[refPos] < columns) {
+ refPos += 2;
+ }
+ break;
+ case twoDimVertR3:
+ this.addPixels(refLine[refPos] + 3, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[this.codingPos] < columns) {
+ ++refPos;
+ while (refLine[refPos] <= codingLine[this.codingPos] &&
+ refLine[refPos] < columns)
+ refPos += 2;
+ }
+ break;
+ case twoDimVertR2:
+ this.addPixels(refLine[refPos] + 2, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[this.codingPos] < columns) {
+ ++refPos;
+ while (refLine[refPos] <= codingLine[this.codingPos] &&
+ refLine[refPos] < columns) {
+ refPos += 2;
+ }
+ }
+ break;
+ case twoDimVertR1:
+ this.addPixels(refLine[refPos] + 1, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[this.codingPos] < columns) {
+ ++refPos;
+ while (refLine[refPos] <= codingLine[this.codingPos] &&
+ refLine[refPos] < columns)
+ refPos += 2;
+ }
+ break;
+ case twoDimVert0:
+ this.addPixels(refLine[refPos], blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[this.codingPos] < columns) {
+ ++refPos;
+ while (refLine[refPos] <= codingLine[this.codingPos] &&
+ refLine[refPos] < columns)
+ refPos += 2;
+ }
+ break;
+ case twoDimVertL3:
+ this.addPixelsNeg(refLine[refPos] - 3, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[this.codingPos] < columns) {
+ if (refPos > 0)
+ --refPos;
+ else
+ ++refPos;
+ while (refLine[refPos] <= codingLine[this.codingPos] &&
+ refLine[refPos] < columns)
+ refPos += 2;
+ }
+ break;
+ case twoDimVertL2:
+ this.addPixelsNeg(refLine[refPos] - 2, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[this.codingPos] < columns) {
+ if (refPos > 0)
+ --refPos;
+ else
+ ++refPos;
+ while (refLine[refPos] <= codingLine[this.codingPos] &&
+ refLine[refPos] < columns)
+ refPos += 2;
+ }
+ break;
+ case twoDimVertL1:
+ this.addPixelsNeg(refLine[refPos] - 1, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[this.codingPos] < columns) {
+ if (refPos > 0)
+ --refPos;
+ else
+ ++refPos;
+
+ while (refLine[refPos] <= codingLine[this.codingPos] &&
+ refLine[refPos] < columns)
+ refPos += 2;
+ }
+ break;
+ case EOF:
+ this.addPixels(columns, 0);
+ this.eof = true;
+ break;
+ default:
+ warn("bad 2d code");
+ this.addPixels(columns, 0);
+ this.err = true;
+ break;
+ }
+ }
+ } else {
+ codingLine[0] = 0;
+ this.codingPos = 0;
+ blackPixels = 0;
+ while(codingLine[this.codingPos] < columns) {
+ code1 = 0;
+ if (blackPixels) {
+ do {
+ code1 += (code3 = this.getBlackCode());
+ } while (code3 >= 64);
+ } else {
+ do {
+ code1 += (code3 = this.getWhiteCode());
+ } while (code3 >= 64);
+ }
+ this.addPixels(codingLine[this.codingPos] + code1, blackPixels);
+ blackPixels ^= 1;
+ }
+ }
+
+ if (this.byteAlign)
+ inputBits &= ~7;
+
+ var gotEOL = false;
+
+ if (!this.eoblock && this.row == this.rows - 1) {
+ this.eof = true;
+ } else {
+ code1 = this.lookBits(12);
+ while (code1 == 0) {
+ this.eatBits(1);
+ code1 = this.lookBits(12);
+ }
+ if (code1 == 1) {
+ this.eatBits(12);
+ gotEOL = true;
+ } else if (code1 == EOF) {
+ this.eof = true;
+ }
+ }
+
+ if (!this.eof && this.encoding > 0) {
+ this.nextLine2D = !this.lookBits(1);
+ this.eatBits(1);
+ }
+
+ if (this.eoblock && gotEOL) {
+ code1 = this.lookBits(12);
+ if (code1 == 1) {
+ this.eatBits(12);
+ if (this.encoding > 0) {
+ this.lookBits(1);
+ this.eatBits(1);
+ }
+ if (this.encoding >= 0) {
+ for (var i = 0; i < 4; ++i) {
+ code1 = this.lookBits(12);
+ if (code1 != 1)
+ warning("bad rtc code");
+ this.eatBits(12);
+ if (this.encoding > 0) {
+ this.lookBits(1);
+ this.eatBits(1);
+ }
+ }
+ }
+ this.eof = true;
+ }
+ } else if (this.err && this.eoline) {
+ var code1;
+ while (true) {
+ code1 = this.lookBits(13);
+ if (code1 == EOF) {
+ this.eof = true;
+ return;
+ }
+ if ((code1 >> 1) == 1) {
+ break;
+ }
+ this.eatBits(1);
+ }
+ this.eatBits(12);
+ if (this.encoding > 0) {
+ this.eatBits(1);
+ this.nextLine2D = !(code1 & 1);
+ }
+ }
+
+ if (codingLine[0] > 0)
+ this.outputBits = codingLine[this.codingPos = 0];
+ else
+ this.outputBits = codingLine[this.codingPos = 1];
+ this.row++;
+ }
+
+ if (this.outputBits >= 8) {
+ this.buf = (this.codingPos & 1) ? 0 : 0xFF;
+ this.outputBits -= 8;
+ if (this.outputBits == 0 && codingLine[this.codingPos] < columns) {
+ this.codingPos++;
+ this.outputBits = codingLine[this.codingPos] - codingLine[this.codingPos - 1];
+ }
+ } else {
+ var bits = 8;
+ this.buf = 0;
+ do {
+ if (this.outputBits > bits) {
+ this.buf <<= bits;
+ if (!(this.codingPos & 1)) {
+ this.buf |= 0xFF >> (8 - bits);
+ }
+ this.outputBits -= bits;
+ bits = 0;
+ } else {
+ this.buf <<= this.outputBits;
+ if (!(this.codingPos & 1)) {
+ this.buf |= 0xFF >> (8 - this.outputBits);
+ }
+ bits -= this.outputBits;
+ this.outputBits = 0;
+ if (codingLine[this.codingPos] < columns) {
+ this.codingPos++;
+ this.outputBits = codingLine[this.codingPos] - codingLine[this.codingPos - 1];
+ } else if (bits > 0) {
+ this.buf <<= bits;
+ bits = 0;
+ }
+ }
+ } while (bits);
+ }
+ if (this.black) {
+ this.buf ^= 0xFF;
+ }
+ return this.buf;
+ };
+ constructor.prototype.getTwoDimCode = function() {
+ var code = 0;
+ var p;
+ if (this.eoblock) {
+ code = this.lookBits(7);
+ p = twoDimTable[code];
+ if (p[0] > 0) {
+ this.eatBits(p[0]);
+ return p[1];
+ }
+ } else {
+ for (var n = 1; n <= 7; ++n) {
+ code = this.lookBits(n);
+ if (n < 7) {
+ code <<= 7 - n;
+ }
+ p = twoDimTable[code];
+ if (p[0] == n) {
+ this.eatBits(n);
+ return p[1];
+ }
+ }
+ }
+ warn("Bad two dim code");
+ return EOF;
+ };
+
+ constructor.prototype.getWhiteCode = function() {
+ var code = 0;
+ var p;
+ var n;
+ if (this.eoblock) {
+ code = this.lookBits(12);
+ if (code == EOF)
+ return 1;
+
+ if ((code >> 5) == 0)
+ p = whiteTable1[code];
+ else
+ p = whiteTable2[code >> 3];
+
+ if (p[0] > 0) {
+ this.eatBits(p[0]);
+ return p[1];
+ }
+ } else {
+ for (var n = 1; n <= 9; ++n) {
+ code = this.lookBits(n);
+ if (code == EOF)
+ return 1;
+
+ if (n < 9)
+ code <<= 9 - n;
+ p = whiteTable2[code];
+ if (p[0] == n) {
+ this.eatBits(n);
+ return p[0];
+ }
+ }
+ for (var n = 11; n <= 12; ++n) {
+ code == this.lookBits(n);
+ if (code == EOF)
+ return 1;
+ if (n < 12)
+ code <<= 12 - n;
+ p = whiteTable1[code];
+ if (p[0] == n) {
+ this.eatBits(n);
+ return p[1];
+ }
+ }
+ }
+ warn("bad white code");
+ this.eatBits(1);
+ return 1;
+ };
+ constructor.prototype.getBlackCode = function() {
+ var code, p, n;
+ if (this.eoblock) {
+ code = this.lookBits(13);
+ if (code == EOF)
+ return 1;
+ if ((code >> 7) == 0)
+ p = blackTable1[code];
+ else if ((code >> 9) == 0 && (code >> 7) != 0)
+ p = blackTable2[(code >> 1) - 64];
+ else
+ p = blackTable3[code >> 7];
+
+ if (p[0] > 0) {
+ this.eatBits(p[0]);
+ return p[1];
+ }
+ } else {
+ for (var n = 2; n <= 6; ++n) {
+ code = this.lookBits(n);
+ if (code == EOF)
+ return 1;
+ if (n < 6)
+ code <<= 6 - n;
+
+ p = blackTable3[code];
+ if (p[0] == n) {
+ this.eatBits(n);
+ return p[1];
+ }
+ }
+ for (var n = 7; n <= 12; ++n) {
+ code = this.lookBits(n);
+ if (code == EOF)
+ return 1;
+ if (n < 12)
+ code <<= 12 - n;
+ if (code >= 64) {
+ p = blackTable2[code - 64];
+ if (p[0] == n) {
+ this.eatBits(n);
+ return p[1];
+ }
+ }
+ }
+ for (n = 10; n <= 13; ++n) {
+ code = this.lookBits(n);
+ if (code == EOF)
+ return 1;
+ if (n < 13)
+ code << 13 - n;
+ p = blackTable1[code];
+ if (p[0] == n) {
+ this.eatBits(n);
+ return p[1];
+ }
+ }
+ }
+ warn("bad black code");
+ this.eatBits(1);
+ return 1;
+ };
+ constructor.prototype.lookBits = function(n) {
+ var c;
+ while (this.inputBits < n) {
+ if ((c = this.str.getByte()) == null) {
+ if (this.inputBits == 0)
+ return EOF;
+ return (this.inputBuf << (n - this.inputBits))
+ & (0xFFFF >> (16 - n));
+ }
+ this.inputBuf = (this.inputBuf << 8) + c;
+ this.inputBits += 8;
+ }
+ return (this.inputBuf >> (this.inputBits - n)) & (0xFFFF >> (16 - n));
+ };
+ constructor.prototype.eatBits = function(n) {
+ if ((this.inputBits -= n) < 0)
+ this.inputBits = 0;
+ }
+
+ return constructor;
+})();
+
var Name = (function() {
function constructor(name) {
this.name = name;
@@ -868,7 +1915,9 @@ var Dict = (function() {
constructor.prototype = {
get: function(key) {
- return this.map[key];
+ if (key in this.map)
+ return this.map[key];
+ return null;
},
get2: function(key1, key2) {
return this.get(key1) || this.get(key2);
@@ -939,7 +1988,7 @@ function IsArray(v) {
}
function IsStream(v) {
- return typeof v == "object" && "getChar" in v;
+ return typeof v == "object" && v != null && ("getChar" in v);
}
function IsRef(v) {
@@ -1001,17 +2050,17 @@ var Lexer = (function() {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx
];
- const MIN_INT = (1<<31) | 0;
- const MAX_INT = (MIN_INT - 1) | 0;
- const MIN_UINT = 0;
- const MAX_UINT = ((1<<30) * 4) - 1;
+ var MIN_INT = (1<<31) | 0;
+ var MAX_INT = (MIN_INT - 1) | 0;
+ var MIN_UINT = 0;
+ var MAX_UINT = ((1<<30) * 4) - 1;
function ToHexDigit(ch) {
if (ch >= "0" && ch <= "9")
- return ch - "0";
- ch = ch.toLowerCase();
- if (ch >= "a" && ch <= "f")
- return ch - "a";
+ return ch.charCodeAt(0) - 48;
+ ch = ch.toUpperCase();
+ if (ch >= "A" && ch <= "F")
+ return ch.charCodeAt(0) - 55;
return -1;
}
@@ -1305,7 +2354,7 @@ var Parser = (function() {
// don't buffer inline image data
this.buf2 = (this.inlineImg > 0) ? null : this.lexer.getObj();
},
- getObj: function() {
+ getObj: function(cipherTransform) {
// refill buffer after inline image data
if (this.inlineImg == 2)
this.refill();
@@ -1331,7 +2380,7 @@ var Parser = (function() {
this.shift();
if (IsEOF(this.buf1))
break;
- dict.set(key, this.getObj());
+ dict.set(key, this.getObj(cipherTransform));
}
}
if (IsEOF(this.buf1))
@@ -1340,7 +2389,7 @@ var Parser = (function() {
// stream objects are not allowed inside content streams or
// object streams
if (this.allowStreams && IsCmd(this.buf2, "stream")) {
- return this.makeStream(dict);
+ return this.makeStream(dict, cipherTransform);
} else {
this.shift();
}
@@ -1359,17 +2408,8 @@ var Parser = (function() {
} else if (IsString(this.buf1)) { // string
var str = this.buf1;
this.shift();
- if (this.fileKey) {
- var decrypt = new DecryptStream(new StringStream(str),
- this.fileKey,
- this.encAlgorithm,
- this.keyLength);
- var str = "";
- var pos = decrypt.pos;
- var length = decrypt.length;
- while (pos++ > length)
- str += decrypt.getChar();
- }
+ if (cipherTransform)
+ str = cipherTransform.decryptString(str);
return str;
}
@@ -1378,7 +2418,7 @@ var Parser = (function() {
this.shift();
return obj;
},
- makeStream: function(dict) {
+ makeStream: function(dict, cipherTransform) {
var lexer = this.lexer;
var stream = lexer.stream;
@@ -1405,12 +2445,8 @@ var Parser = (function() {
this.shift();
stream = stream.makeSubStream(pos, length, dict);
- if (this.fileKey) {
- stream = new DecryptStream(stream,
- this.fileKey,
- this.encAlgorithm,
- this.keyLength);
- }
+ if (cipherTransform)
+ stream = cipherTransform.createStream(stream);
stream = this.filter(stream, dict, length);
stream.parameters = dict;
return stream;
@@ -1448,6 +2484,9 @@ var Parser = (function() {
return new JpegStream(bytes, stream.dict);
} else if (name == "ASCII85Decode") {
return new Ascii85Stream(stream);
+ } else if (name == "CCITTFaxDecode") {
+ TODO("implement fax stream");
+ return new CCITTFaxStream(stream, params);
} else {
error("filter '" + name + "' not supported yet");
}
@@ -1541,16 +2580,22 @@ var XRef = (function() {
this.xrefstms = {};
var trailerDict = this.readXRef(startXRef);
+ // prepare the XRef cache
+ this.cache = [];
+
+ var encrypt = trailerDict.get("Encrypt");
+ if (encrypt) {
+ var fileId = trailerDict.get("ID");
+ this.encrypt = new CipherTransformFactory(this.fetch(encrypt), fileId[0] /*, password */);
+ }
+
// get the root dictionary (catalog) object
if (!IsRef(this.root = trailerDict.get("Root")))
error("Invalid root reference");
-
- // prepare the XRef cache
- this.cache = [];
}
constructor.prototype = {
- readXRefTable: function(parser) {
+ readXRefTable: function readXRefTable(parser) {
var obj;
while (true) {
if (IsCmd(obj = parser.getObj(), "trailer"))
@@ -1621,7 +2666,7 @@ var XRef = (function() {
return dict;
},
- readXRefStream: function(stream) {
+ readXRefStream: function readXRefStream(stream) {
var streamParameters = stream.parameters;
var length = streamParameters.get("Length");
var byteWidths = streamParameters.get("W");
@@ -1673,7 +2718,7 @@ var XRef = (function() {
this.readXRef(prev);
return streamParameters;
},
- readXRef: function(startXRef) {
+ readXRef: function readXref(startXRef) {
var stream = this.stream;
stream.pos = startXRef;
var parser = new Parser(new Lexer(stream), true);
@@ -1691,6 +2736,7 @@ var XRef = (function() {
return this.readXRefStream(obj);
}
error("Invalid XRef");
+ return null;
},
getEntry: function(i) {
var e = this.entries[i];
@@ -1734,7 +2780,11 @@ var XRef = (function() {
}
error("bad XRef entry");
}
- e = parser.getObj();
+ if (this.encrypt) {
+ e = parser.getObj(this.encrypt.createCipherTransform(num, gen));
+ } else {
+ e = parser.getObj();
+ }
// Don't cache streams since they are mutable.
if (!IsStream(e))
this.cache[num] = e;
@@ -2031,32 +3081,7 @@ var PDFDoc = (function() {
return constructor;
})();
-const IDENTITY_MATRIX = [ 1, 0, 0, 1, 0, 0 ];
-
-//