diff --git a/javascript/src/CryptoData.js b/javascript/src/CryptoData.js index 9261a2d92c9f12334b2388a9224f90b2e2653b19..dfa79a5778f3ac50f541b5699ec16356d6a66dcd 100644 --- a/javascript/src/CryptoData.js +++ b/javascript/src/CryptoData.js @@ -1,58 +1,58 @@ function CryptoData() {} CryptoData.prototype.set = function(obj) { - for(var member in obj) { - this[member] = JSON.parse(JSON.stringify(obj[member])) + for (let member in obj) { + this[member] = JSON.parse(JSON.stringify(obj[member])); } }; CryptoData.prototype.serialize = function() { - return JSON.stringify(this) + return JSON.stringify(this); }; CryptoData.prototype.deserialize = function(serialized) { - var obj = JSON.parse(serialized); - this.set(obj) + let obj = JSON.parse(serialized); + this.set(obj); }; CryptoData.prototype.setPublicKey = function(publicKey) { - this["publicKey"] = publicKey + this["publicKey"] = publicKey; }; CryptoData.prototype.getPublicKey = function() { - return this["publicKey"] + return this["publicKey"]; }; CryptoData.prototype.setPrivateKey = function(privateKey) { - this["privateKey"] = privateKey + this["privateKey"] = privateKey; }; CryptoData.prototype.getPrivateKey = function() { - return this["privateKey"] + return this["privateKey"]; }; CryptoData.prototype.setx509Certificate = function(x509Certificate) { - this["x509Certificate"] = x509Certificate + this["x509Certificate"] = x509Certificate; }; CryptoData.prototype.getx509Certificate = function() { - return this["x509Certificate"] + return this["x509Certificate"]; }; CryptoData.prototype.setKeyUUID = function(keyUUID) { - this["keyUUID"] = keyUUID + this["keyUUID"] = keyUUID; }; CryptoData.prototype.getKeyUUID = function() { - return this["keyUUID"] + return this["keyUUID"]; }; CryptoData.prototype.setChain = function(chain) { - this["chain"] = chain + this["chain"] = chain; }; CryptoData.prototype.getChain = function() { - return this["chain"] + return this["chain"]; }; -export default CryptoData; \ No newline at end of file +export default CryptoData; diff --git a/javascript/src/constants/authentication.js b/javascript/src/constants/authentication.js index e00c27fa1dd9acd046795061a019a054a7e56af8..aff4861f605a9cdc8c1882f67019d61c8e5a7836 100644 --- a/javascript/src/constants/authentication.js +++ b/javascript/src/constants/authentication.js @@ -1,5 +1,5 @@ export const LOGIN_MODES = { - SMS: 'sms', - PREVIOUSLY_ADDED_DEVICE: 'previousaddeddevice', - NEW_DEVICE: 'newdevice' + SMS: "sms", + PREVIOUSLY_ADDED_DEVICE: "previousaddeddevice", + NEW_DEVICE: "newdevice" }; diff --git a/javascript/src/helpers/mailparser.js b/javascript/src/helpers/mailparser.js index 2e9142e21e517709e4da7d32eb35321374853ab3..9bd873ff9ba1d20c06bd890a4bd525e640c378ec 100644 --- a/javascript/src/helpers/mailparser.js +++ b/javascript/src/helpers/mailparser.js @@ -347,5 +347,5 @@ export function getAttachment(mime, part) { base64 = window.btoa(body); } - return { contentType, base64 } + return { contentType, base64 }; } diff --git a/javascript/src/iframe/collaboraapi-iframe.js b/javascript/src/iframe/collaboraapi-iframe.js index 6dde19dfdb308c4f1291e49b3bd0780d9d906127..af29c89d5a4959592d50e3058bd7974e7f1192b7 100644 --- a/javascript/src/iframe/collaboraapi-iframe.js +++ b/javascript/src/iframe/collaboraapi-iframe.js @@ -1,30 +1,32 @@ -const axios = require('axios'); +const axios = require("axios"); function CollaboraAPI() {} -CollaboraAPI.prototype.discovery = function () { +CollaboraAPI.prototype.discovery = function() { const requestConfig = { url: `${window.COLLABORA_URL}hosting/discovery`, - method: 'GET' + method: "GET" }; return axios(requestConfig).then(response => { - const apps = response.request.responseXML.querySelectorAll("net-zone[name='external-http'] app"); + const apps = response.request.responseXML.querySelectorAll( + "net-zone[name='external-http'] app" + ); const results = []; for (let i = 0; i < apps.length; i++) { const app = apps[i]; - const action = app.querySelector('action'); - const mimeType = app.getAttribute('name'); - const ext = action.getAttribute('ext'); - const urlsrc = action.getAttribute('urlsrc'); + const action = app.querySelector("action"); + const mimeType = app.getAttribute("name"); + const ext = action.getAttribute("ext"); + const urlsrc = action.getAttribute("urlsrc"); results.push({ mimeType, ext, urlsrc }); } - return results + return results; }); }; diff --git a/javascript/src/iframe/viamapi-iframe.js b/javascript/src/iframe/viamapi-iframe.js index 7ad243400d972b5a1e4b18cde88d149b4b694dd7..efd6fe262831e165c52e6723ba09232701b48b6a 100644 --- a/javascript/src/iframe/viamapi-iframe.js +++ b/javascript/src/iframe/viamapi-iframe.js @@ -1,4 +1,4 @@ -import { parseSMIME } from '../utilities/emailUtilities'; +import { parseSMIME } from "../utilities/emailUtilities"; import { stringToUtf8ByteArray, utf8ByteArrayToString, @@ -6,45 +6,45 @@ import { utf8Base64ToString, base64ToByteArray, byteArrayToBase64 -} from '../utilities/stringUtilities'; +} from "../utilities/stringUtilities"; -const QRCode = require('qrcode'); -const Penpal = require('penpal').default; +const QRCode = require("qrcode"); +const Penpal = require("penpal").default; import { createDeviceHash, destroyIdentityFromLocalStorage, encodeResponse, - listIdentitiesFromLocalStorage, makeid -} from '../utilities/appUtility'; -import {LOGIN_MODES} from '../constants/authentication'; + listIdentitiesFromLocalStorage, + makeid +} from "../utilities/appUtility"; +import { LOGIN_MODES } from "../constants/authentication"; import { createOneTimePassportCertificate, createPassportCertificate, decryptMessage, - encryptMessage, signEmail -} from '../utilities/signingUtilities'; -import { - signPdf -} from '../utilities/pdfUtilities'; -import CryptoData from '../CryptoData'; -import Identity from '../Identity'; -import {STATUS_DEVICE_REVOKED} from '../constants/statuses'; + encryptMessage, + signEmail +} from "../utilities/signingUtilities"; +import { signPdf } from "../utilities/pdfUtilities"; +import CryptoData from "../CryptoData"; +import Identity from "../Identity"; +import { STATUS_DEVICE_REVOKED } from "../constants/statuses"; -const penpalMethods = require('../../temp/penpal-methods').default; -const WopiAPI = require('./wopiapi-iframe'); -const CollaboraAPI = require('./collaboraapi-iframe'); -const ViamAPI = require('../../temp/viamapi'); +const penpalMethods = require("../../temp/penpal-methods").default; +const WopiAPI = require("./wopiapi-iframe"); +const CollaboraAPI = require("./collaboraapi-iframe"); +const ViamAPI = require("../../temp/viamapi"); -var identityColors = ["#994392", "#cb0767", "#e51d31", "#ec671b", "#fab610"]; +let identityColors = ["#994392", "#cb0767", "#e51d31", "#ec671b", "#fab610"]; function getNextColor() { - var colorIndex = localStorage.getItem("colorIndex"); + let colorIndex = localStorage.getItem("colorIndex"); if (colorIndex == null || colorIndex === "") { - colorIndex = 0 + colorIndex = 0; } - var color = identityColors[colorIndex]; + let color = identityColors[colorIndex]; colorIndex++; @@ -52,77 +52,92 @@ function getNextColor() { localStorage.setItem("colorIndex", colorIndex); - return color + return color; } function setKeyForUUID(uuid, key) { - var storedIdentityForUuid = localStorage.getItem("keyperuuid/" + uuid); - if(storedIdentityForUuid !== key && storedIdentityForUuid != null && storedIdentityForUuid !== "") { - destroyIdentityFromLocalStorage(storedIdentityForUuid) + let storedIdentityForUuid = localStorage.getItem("keyperuuid/" + uuid); + if ( + storedIdentityForUuid !== key && + storedIdentityForUuid != null && + storedIdentityForUuid !== "" + ) { + destroyIdentityFromLocalStorage(storedIdentityForUuid); } - localStorage.setItem("keyperuuid/" + uuid, key) + localStorage.setItem("keyperuuid/" + uuid, key); } function getColorForIdentity(key) { - var storedColor = localStorage.getItem("colors/" + key); + let storedColor = localStorage.getItem("colors/" + key); - if(storedColor == null || storedColor === "") { + if (storedColor == null || storedColor === "") { storedColor = getNextColor(); - localStorage.setItem("colors/" + key, storedColor) + localStorage.setItem("colors/" + key, storedColor); } - return storedColor + return storedColor; } function setIdentityInLocalStorage(identityToStore, extendKey = true) { - var pinCode = identityToStore.pinCode; + let pinCode = identityToStore.pinCode; const serializedIdentity = JSON.stringify(identityToStore); const key = identityToStore.authentication.publicKey; - if(pinCode == null || pinCode === "") { - pinCode = getPincode(key) + if (pinCode == null || pinCode === "") { + pinCode = getPincode(key); } - if(pinCode == null || pinCode === "") { + if (pinCode == null || pinCode === "") { return null; } - return encryptMessage(serializedIdentity, pinCode, "identity").then((encryptedIdentity) => { - var success = true; - if(extendKey === true) { - success = extendPinCodeTtl(key, pinCode) - } - if (success === true) { - localStorage.setItem(key, encryptedIdentity); - let serializedIdentitiesList = localStorage.getItem("identities"); - let identities = JSON.parse(serializedIdentitiesList); - identities[key] = true; + return encryptMessage(serializedIdentity, pinCode, "identity").then( + encryptedIdentity => { + let success = true; + if (extendKey === true) { + success = extendPinCodeTtl(key, pinCode); + } + if (success === true) { + localStorage.setItem(key, encryptedIdentity); + let serializedIdentitiesList = localStorage.getItem("identities"); + let identities = JSON.parse(serializedIdentitiesList); + identities[key] = true; - localStorage.setItem("identities", JSON.stringify(identities)) - } else { - console.log("Can not extend pincode ttl"); + localStorage.setItem("identities", JSON.stringify(identities)); + } else { + console.log("Can not extend pincode ttl"); + } } - }); + ); } function getProfileData(identity) { return new Penpal.Promise(executeResultUpper => { - executeRestfulFunction("private", viamApi, - viamApi.identityGetIdentityProfileData, null).then(executeResult => { - if(executeResult.code === "200") { - var listItem = {}; + executeRestfulFunction( + "private", + viamApi, + viamApi.identityGetIdentityProfileData, + null + ).then(executeResult => { + if (executeResult.code === "200") { + let listItem = {}; - listItem.identityColor = getColorForIdentity(identity.authentication.publicKey); + listItem.identityColor = getColorForIdentity( + identity.authentication.publicKey + ); listItem.initials = executeResult.data.initials; - if(listItem.initials === null || listItem.initials === "") { + if (listItem.initials === null || listItem.initials === "") { listItem.initials = "JD"; } - localStorage.setItem("profiles/" + identity.authentication.publicKey, JSON.stringify(listItem)); - executeResultUpper(listItem) + localStorage.setItem( + "profiles/" + identity.authentication.publicKey, + JSON.stringify(listItem) + ); + executeResultUpper(listItem); } else { - executeResultUpper({}) + executeResultUpper({}); } }); }); @@ -152,20 +167,20 @@ async function getIdentityFromLocalStorage(key, pinCode, extendTtl = true) { } function extendPinCodeTtl(key, pinCode) { - if(pinCode == null || pinCode === "") { - var now = new Date(); - var nowMillis = now.getTime(); - var ttl = window.sessionStorage.getItem("pincodettls/" + key); + if (pinCode == null || pinCode === "") { + let now = new Date(); + let nowMillis = now.getTime(); + let ttl = window.sessionStorage.getItem("pincodettls/" + key); if (ttl == null || ttl === "" || nowMillis >= parseInt(ttl)) { clearPinCodeTtl(key); - return false + return false; } else { - var ttl = now.getTime() + 4 * 60 * 60 * 1000; + let ttl = now.getTime() + 4 * 60 * 60 * 1000; window.sessionStorage.setItem("pincodettls/" + key, ttl); } } else { - var now = new Date(); - var ttl = now.getTime() + 4 * 60 * 60 * 1000; + let now = new Date(); + let ttl = now.getTime() + 4 * 60 * 60 * 1000; window.sessionStorage.setItem("pincodettls/" + key, ttl); window.sessionStorage.setItem("pincodes/" + key, pinCode); } @@ -177,19 +192,19 @@ window.extendPinCodeTtl = extendPinCodeTtl; function clearPinCodeTtl(key) { window.sessionStorage.removeItem("pincodettls/" + key); - window.sessionStorage.removeItem("pincodes/" + key) + window.sessionStorage.removeItem("pincodes/" + key); } function getPincode(key) { - var now = new Date(); - var nowMillis = now.getTime(); - var ttl = window.sessionStorage.getItem("pincodettls/" + key); + let now = new Date(); + let nowMillis = now.getTime(); + let ttl = window.sessionStorage.getItem("pincodettls/" + key); if (ttl == null || ttl === "") { - return null + return null; } else { - if(nowMillis >= parseInt(ttl)) { + if (nowMillis >= parseInt(ttl)) { clearPinCodeTtl(key); - return null + return null; } else { return window.sessionStorage.getItem("pincodes/" + key); } @@ -198,11 +213,11 @@ function getPincode(key) { function createEvent(actionId, type, payloads) { return { - "actionID": actionId, - "type": type, - "stamp": new Date().getTime(), - "payloads" : payloads - } + actionID: actionId, + type: type, + stamp: new Date().getTime(), + payloads: payloads + }; } const destroyAuthentication = () => { @@ -258,7 +273,11 @@ const handleIdentityLogin = (identity, uuid, token) => { }; async function executeRestfulFunction(type, that, fn, config, ...args) { - const { currentlyAuthenticatedIdentity, viamApi, currentlyLoadedIdentity } = window; + const { + currentlyAuthenticatedIdentity, + viamApi, + currentlyLoadedIdentity + } = window; let response; try { response = await fn.apply(that, [config, ...args]); @@ -269,9 +288,9 @@ async function executeRestfulFunction(type, that, fn, config, ...args) { } else { //Connection error or similar const data = { - "data": "", - "code": "999", - "status": error.message + data: "", + code: "999", + status: error.message }; return data; } @@ -280,7 +299,8 @@ async function executeRestfulFunction(type, that, fn, config, ...args) { const identity = currentlyAuthenticatedIdentity || currentlyLoadedIdentity; const { code, status } = response.data; - const deviceRevoked = type === "private" && code === "401" && status === STATUS_DEVICE_REVOKED; + const deviceRevoked = + type === "private" && code === "401" && status === STATUS_DEVICE_REVOKED; if (deviceRevoked) { destroyIdentity(); @@ -290,10 +310,17 @@ async function executeRestfulFunction(type, that, fn, config, ...args) { return response.data; } - const badSession = type === "private" && identity && code === "400" && status === "Bad session"; + const badSession = + type === "private" && + identity && + code === "400" && + status === "Bad session"; if (!badSession) return response.data; - const loginResponse = await viamApi.identityLogin(null, "previousaddeddevice"); + const loginResponse = await viamApi.identityLogin( + null, + "previousaddeddevice" + ); if (loginResponse.data.code !== "200") return loginResponse.data; const uuid = loginResponse.data.data["Uuid"]; @@ -311,85 +338,95 @@ window.executeRestfulFunction = executeRestfulFunction; function loadIdentityInternal(identityKey, pinCode) { return new Penpal.Promise(result => { - getIdentityFromLocalStorage(identityKey, pinCode).then(async (loadedIdentity) => { - if (loadedIdentity == null) { - result({ - "data": "", - "code": "400", - "status": "Please restore or authorize your account via another device." - }); - } - localStorage.removeItem("attempt"); - - window.loadedIdentities[identityKey] = loadedIdentity; - window.currentlyLoadedIdentity = loadedIdentity; - - if (identityKey === localStorage.getItem("authenticatedIdentity")) { - window.currentlyAuthenticatedIdentity = loadedIdentity; - const uuid = localStorage.getItem("uuid"); - const token = localStorage.getItem("token"); - const deviceHash = await createDeviceHash(identityKey); - window.viamApi.setIdentity(identityKey); - window.viamApi.setDeviceHash(deviceHash); - window.viamApi.setSessionData(uuid, token); - } + getIdentityFromLocalStorage(identityKey, pinCode) + .then(async loadedIdentity => { + if (loadedIdentity == null) { + result({ + data: "", + code: "400", + status: + "Please restore or authorize your account via another device." + }); + } + localStorage.removeItem("attempt"); + + window.loadedIdentities[identityKey] = loadedIdentity; + window.currentlyLoadedIdentity = loadedIdentity; + + if (identityKey === localStorage.getItem("authenticatedIdentity")) { + window.currentlyAuthenticatedIdentity = loadedIdentity; + const uuid = localStorage.getItem("uuid"); + const token = localStorage.getItem("token"); + const deviceHash = await createDeviceHash(identityKey); + window.viamApi.setIdentity(identityKey); + window.viamApi.setDeviceHash(deviceHash); + window.viamApi.setSessionData(uuid, token); + } - window.viamAnonymousApi.setIdentity(window.currentlyLoadedIdentity.authentication.publicKey); + window.viamAnonymousApi.setIdentity( + window.currentlyLoadedIdentity.authentication.publicKey + ); - const { publicKey, x509Certificate } = loadedIdentity.authentication; + const { publicKey, x509Certificate } = loadedIdentity.authentication; - result({ - "data": { - authentication: { - publicKey, - x509Certificate - } - }, - "code": "200", - "status": "Identity loaded" - }); - }).catch((e) => { - result({ - "data": "", - "code": "400", - "status": "" + e + result({ + data: { + authentication: { + publicKey, + x509Certificate + } + }, + code: "200", + status: "Identity loaded" + }); + }) + .catch(e => { + result({ + data: "", + code: "400", + status: "" + e + }); }); - }); }); } function getCertificateForPassport(passportUUID, internal) { - return new Penpal.Promise(certificateResult => { if (window.currentlyAuthenticatedIdentity === null) { - return {"data" : "", - "code" : "400", - "status" : "Identity not authenticated" - } + return { data: "", code: "400", status: "Identity not authenticated" }; } const passportIdentity = window.currentlyAuthenticatedIdentity; - var passport = passportIdentity.getPassport(passportUUID); - if(passport === undefined || passport === null) { - createPassportCertificate(passportUUID).then(function(keys){ - var cryptoData = new CryptoData(); + let passport = passportIdentity.getPassport(passportUUID); + if (passport === undefined || passport === null) { + createPassportCertificate(passportUUID).then(function(keys) { + let cryptoData = new CryptoData(); cryptoData.setPublicKey(keys["publicKeyPEM"]); cryptoData.setPrivateKey(keys["privateKeyPEM"]); - var certificate = keys["certificatePEM"]; + let certificate = keys["certificatePEM"]; //download("passportCertificateBeforeSigning.crt", "text/plain", certificate) //cryptoData.setx509Certificate(keys["certificate"]) - executeRestfulFunction("private", viamApi, viamApi.signSignCertificate, null, btoa(certificate), passportUUID).then(executeResult => { - if(executeResult.code === "200") { - var signedCertificate = atob(executeResult.data["SignedCertificate"]); + executeRestfulFunction( + "private", + viamApi, + viamApi.signSignCertificate, + null, + btoa(certificate), + passportUUID + ).then(executeResult => { + if (executeResult.code === "200") { + let signedCertificate = atob( + executeResult.data["SignedCertificate"] + ); //download("passportCertificateAfterSigning.crt", "text/plain", signedCertificate) - var keyUUID = executeResult.data["CertificateUUID"]; - var encodedChain = executeResult.data["Chain"]; + let keyUUID = executeResult.data["CertificateUUID"]; + let encodedChain = executeResult.data["Chain"]; //download("rootCertificate.crt", "text/plain", atob(encodedChain[0])) - var chain = []; + let chain = []; - for(var i = 0; i < encodedChain.length; i++) { - chain.push(atob(encodedChain[i])) + for (let i = 0; i < encodedChain.length; i++) { + chain.push(atob(encodedChain[i])); } cryptoData.setx509Certificate(signedCertificate); @@ -399,28 +436,32 @@ function getCertificateForPassport(passportUUID, internal) { passportIdentity.setPassport(passportUUID, cryptoData); getProfileData(passportIdentity).then(executeResult1 => { - setIdentityInLocalStorage(passportIdentity).then(() => { - window.currentlyAuthenticatedIdentity = passportIdentity; - window.lastTimeGetProfile = 0; - window.currentlyLoadedIdentity = passportIdentity; - const copyOfCryptoData = JSON.parse(JSON.stringify(cryptoData)); - - if (internal === false) { - copyOfCryptoData["privateKey"] = ""; - } - - certificateResult({ - "data": copyOfCryptoData, - "code": "200", - "status": "Certificate got" - }); - }).catch((e) => { - certificateResult({ - "data": "", - "code": "400", - "status": "Can not store certificate " + e + setIdentityInLocalStorage(passportIdentity) + .then(() => { + window.currentlyAuthenticatedIdentity = passportIdentity; + window.lastTimeGetProfile = 0; + window.currentlyLoadedIdentity = passportIdentity; + const copyOfCryptoData = JSON.parse( + JSON.stringify(cryptoData) + ); + + if (internal === false) { + copyOfCryptoData["privateKey"] = ""; + } + + certificateResult({ + data: copyOfCryptoData, + code: "200", + status: "Certificate got" + }); + }) + .catch(e => { + certificateResult({ + data: "", + code: "400", + status: "Can not store certificate " + e + }); }); - }); }); } else { certificateResult(executeResult); @@ -428,15 +469,16 @@ function getCertificateForPassport(passportUUID, internal) { }); }); } else { - var copyOfCryptoData = JSON.parse(JSON.stringify(passport)); + let copyOfCryptoData = JSON.parse(JSON.stringify(passport)); - if(internal === false) { - copyOfCryptoData["privateKey"] = "" + if (internal === false) { + copyOfCryptoData["privateKey"] = ""; } - certificateResult({"data" : copyOfCryptoData, - "code" : "200", - "status" : "Certificate got" + certificateResult({ + data: copyOfCryptoData, + code: "200", + status: "Certificate got" }); } }); @@ -458,19 +500,26 @@ const connection = Penpal.connectToParent({ if (!collaboraUrl) { collaboraUrl = window.location.origin; - console.warn(`Collabora host URL not specified. Fall back to ${collaboraUrl}`); // eslint-disable-line no-console + console.warn( + `Collabora host URL not specified. Fall back to ${collaboraUrl}` + ); // eslint-disable-line no-console } - window.API_HOST = apiUrl.charAt(apiUrl.length - 1) === "/" ? apiUrl : apiUrl + "/"; - window.WOPI_URL = wopiUrl.charAt(wopiUrl.length - 1) === "/" ? wopiUrl : wopiUrl + "/"; - window.COLLABORA_URL = collaboraUrl.charAt(collaboraUrl.length - 1) === "/" ? collaboraUrl : collaboraUrl + "/"; + window.API_HOST = + apiUrl.charAt(apiUrl.length - 1) === "/" ? apiUrl : apiUrl + "/"; + window.WOPI_URL = + wopiUrl.charAt(wopiUrl.length - 1) === "/" ? wopiUrl : wopiUrl + "/"; + window.COLLABORA_URL = + collaboraUrl.charAt(collaboraUrl.length - 1) === "/" + ? collaboraUrl + : collaboraUrl + "/"; }, ...penpalMethods, createIdentity(pinCode) { return new Penpal.Promise(result => { - createPassportCertificate(makeid()).then(function(keys){ - var newIdentity = new Identity(); - var cryptoData = new CryptoData(); + createPassportCertificate(makeid()).then(function(keys) { + let newIdentity = new Identity(); + let cryptoData = new CryptoData(); cryptoData.setPublicKey(keys["publicKeyPEM"]); cryptoData.setPrivateKey(keys["privateKeyPEM"]); cryptoData.setx509Certificate(keys["certificatePEM"]); @@ -478,29 +527,31 @@ const connection = Penpal.connectToParent({ newIdentity.setPinCode(pinCode); window.currentlyLoadedIdentity = newIdentity; - window.loadedIdentities[newIdentity.authentication.publicKey] = newIdentity; + window.loadedIdentities[ + newIdentity.authentication.publicKey + ] = newIdentity; extendPinCodeTtl(newIdentity.authentication.publicKey, pinCode); - window.viamAnonymousApi.setIdentity(newIdentity.authentication.publicKey); + window.viamAnonymousApi.setIdentity( + newIdentity.authentication.publicKey + ); - result({"data" : newIdentity, - "code" : "200", - "status" : "Identity created" - }) + result({ + data: newIdentity, + code: "200", + status: "Identity created" + }); }); - }) + }); }, listIdentities() { return new Penpal.Promise(result => { - var identities = listIdentitiesFromLocalStorage(); - result({"data" : identities, - "code" : "200", - "status" : "Identities listed" - }) + let identities = listIdentitiesFromLocalStorage(); + result({ data: identities, code: "200", status: "Identities listed" }); }); }, loadIdentity(identityKey, pinCode) { - return loadIdentityInternal(identityKey, pinCode) + return loadIdentityInternal(identityKey, pinCode); }, checkIdentityPinCode: async (key, pinCode) => { try { @@ -517,7 +568,11 @@ const connection = Penpal.connectToParent({ }, changeIdentityPinCode: async (key, oldPinCode, newPinCode) => { try { - const identity = await getIdentityFromLocalStorage(key, oldPinCode, false); + const identity = await getIdentityFromLocalStorage( + key, + oldPinCode, + false + ); if (identity) { identity.pinCode = newPinCode; @@ -533,17 +588,17 @@ const connection = Penpal.connectToParent({ }, getIdentityProfile(identityKey) { return new Penpal.Promise(result => { - const serializedProfile = localStorage.getItem("profiles/" + identityKey); + const serializedProfile = localStorage.getItem( + "profiles/" + identityKey + ); if (serializedProfile === null || serializedProfile === "") { - result({"data" : "", - "code" : "400", - "status" : "Profile is empty" - }); + result({ data: "", code: "400", status: "Profile is empty" }); } else { - result({"data" : JSON.parse(serializedProfile), - "code" : "200", - "status" : "Identities cleared" - }) + result({ + data: JSON.parse(serializedProfile), + code: "200", + status: "Identities cleared" + }); } }); }, @@ -561,7 +616,13 @@ const connection = Penpal.connectToParent({ return new Penpal.Promise(result => { viamApi.setIdentity(identity.authentication.publicKey); - executeRestfulFunction("public", viamApi, viamApi.identityConfirmIdentificator, null, confirmationCodeArg).then(executeResult => { + executeRestfulFunction( + "public", + viamApi, + viamApi.identityConfirmIdentificator, + null, + confirmationCodeArg + ).then(executeResult => { result(executeResult); }); }); @@ -570,7 +631,13 @@ const connection = Penpal.connectToParent({ return new Penpal.Promise(result => { viamApi.setIdentity(identity.authentication.publicKey); - executeRestfulFunction("public", viamApi, viamApi.identityGetIdentificatorByRegisterToken, null, tokenArg).then(executeResult => { + executeRestfulFunction( + "public", + viamApi, + viamApi.identityGetIdentificatorByRegisterToken, + null, + tokenArg + ).then(executeResult => { result(executeResult); }); }); @@ -579,16 +646,38 @@ const connection = Penpal.connectToParent({ return new Penpal.Promise(result => { viamApi.setIdentity(identity.authentication.publicKey); - executeRestfulFunction("public", viamApi, viamApi.identitySubmitIdentificator, null, identificatorArg, registerToken).then(executeResult => { + executeRestfulFunction( + "public", + viamApi, + viamApi.identitySubmitIdentificator, + null, + identificatorArg, + registerToken + ).then(executeResult => { result(executeResult); }); }); }, - submitRegisterClaims(identity, givennameArg,familynameArg,emailArg,phonenumberArg) { + submitRegisterClaims( + identity, + givennameArg, + familynameArg, + emailArg, + phonenumberArg + ) { return new Penpal.Promise(result => { viamApi.setIdentity(identity.authentication.publicKey); - executeRestfulFunction("public", viamApi, viamApi.identitySubmitRegisterClaims, null, givennameArg,familynameArg,emailArg,phonenumberArg).then(executeResult => { + executeRestfulFunction( + "public", + viamApi, + viamApi.identitySubmitRegisterClaims, + null, + givennameArg, + familynameArg, + emailArg, + phonenumberArg + ).then(executeResult => { result(executeResult); }); }); @@ -597,23 +686,29 @@ const connection = Penpal.connectToParent({ return new Penpal.Promise(result => { viamApi.setIdentity(registerIdentity.authentication.publicKey); - executeRestfulFunction("public", viamApi, viamApi.identityAgreeOnRegistration, null).then(executeResult => { + executeRestfulFunction( + "public", + viamApi, + viamApi.identityAgreeOnRegistration, + null + ).then(executeResult => { let sequence = Promise.resolve(); if (executeResult.code === "200") { sequence = sequence.then(() => { - setIdentityInLocalStorage(registerIdentity) - } - ) + setIdentityInLocalStorage(registerIdentity); + }); } - sequence.then(() => { - result(executeResult); - }).catch((e) => { - result({ - "data": "", - "code": "400", - "status": "Can not store identity: " + e + sequence + .then(() => { + result(executeResult); }) - }) + .catch(e => { + result({ + data: "", + code: "400", + status: "Can not store identity: " + e + }); + }); }); }); }, @@ -621,7 +716,13 @@ const connection = Penpal.connectToParent({ return new Penpal.Promise(result => { viamApi.setIdentity(identity.authentication.publicKey); - executeRestfulFunction("public", viamApi, viamApi.identityResendConfirmationCode, null, identificatorArg).then(executeResult => { + executeRestfulFunction( + "public", + viamApi, + viamApi.identityResendConfirmationCode, + null, + identificatorArg + ).then(executeResult => { result(executeResult); }); }); @@ -635,26 +736,31 @@ const connection = Penpal.connectToParent({ }; } - const deviceHash = await createDeviceHash(loginIdentity.authentication.publicKey); + const deviceHash = await createDeviceHash( + loginIdentity.authentication.publicKey + ); window.viamApi.setSessionData("", ""); window.viamApi.setDeviceHash(deviceHash); window.viamApi.setIdentity(loginIdentity.authentication.publicKey); - const identityLoginResponse = - await executeRestfulFunction( - "public", - window.viamApi, - window.viamApi.identityLogin, - null, - mode, requestCode, - requestActionID - ); + const identityLoginResponse = await executeRestfulFunction( + "public", + window.viamApi, + window.viamApi.identityLogin, + null, + mode, + requestCode, + requestActionID + ); const { code, data } = identityLoginResponse; const responseToClient = Object.assign({}, identityLoginResponse); if (code === "200") { - if (mode === LOGIN_MODES.SMS || mode === LOGIN_MODES.PREVIOUSLY_ADDED_DEVICE) { + if ( + mode === LOGIN_MODES.SMS || + mode === LOGIN_MODES.PREVIOUSLY_ADDED_DEVICE + ) { handleIdentityLogin(loginIdentity, data.Uuid, data.Session); await getProfileData(loginIdentity); @@ -662,7 +768,9 @@ const connection = Penpal.connectToParent({ await setIdentityInLocalStorage(loginIdentity); } } else if (mode === LOGIN_MODES.NEW_DEVICE) { - const dataUrl = await QRCode.toDataURL(`${data.ActionID},${data.QrCode}`); + const dataUrl = await QRCode.toDataURL( + `${data.ActionID},${data.QrCode}` + ); Object.assign(responseToClient.data, { image: dataUrl }); } } @@ -671,39 +779,49 @@ const connection = Penpal.connectToParent({ }, identityAddNewDevice() { return new Penpal.Promise(result => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if (authenticationPublicKey === null) { - result({"data" : "", - "code" : "400", - "status" : "Identity not authenticated" - }) + result({ + data: "", + code: "400", + status: "Identity not authenticated" + }); } if (window.loadedIdentities[authenticationPublicKey] === null) { - result({"data" : "", - "code" : "400", - "status" : "Identity not authenticated" - }) + result({ + data: "", + code: "400", + status: "Identity not authenticated" + }); } - var success = extendPinCodeTtl(authenticationPublicKey); + let success = extendPinCodeTtl(authenticationPublicKey); - if(success === false) { - result({"data" : "", - "code" : "400", - "status" : "Identity not authenticated" - }) + if (success === false) { + result({ + data: "", + code: "400", + status: "Identity not authenticated" + }); } - executeRestfulFunction("private", viamApi, viamApi.identityAddNewDevice, null).then(executeResult => { + executeRestfulFunction( + "private", + viamApi, + viamApi.identityAddNewDevice, + null + ).then(executeResult => { if (executeResult.code === "200") { - var actionID = executeResult.data["ActionID"]; - var QrCode = executeResult.data["QrCode"]; - QRCode.toDataURL(actionID + "," + QrCode, function (err, url) { + let actionID = executeResult.data["ActionID"]; + let QrCode = executeResult.data["QrCode"]; + QRCode.toDataURL(actionID + "," + QrCode, function(err, url) { executeResult.data["image"] = url; result(executeResult); - }) + }); } else { result(executeResult); } @@ -712,38 +830,54 @@ const connection = Penpal.connectToParent({ }, identityDestroyKeysForDevice(authenticationPublicKeyArg) { return new Penpal.Promise(result => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if (authenticationPublicKey === null) { - result({"data" : "", - "code" : "400", - "status" : "Identity not authenticated" - }) + result({ + data: "", + code: "400", + status: "Identity not authenticated" + }); } if (window.loadedIdentities[authenticationPublicKey] === null) { - result({"data" : "", - "code" : "400", - "status" : "Identity not authenticated" - }) + result({ + data: "", + code: "400", + status: "Identity not authenticated" + }); } - var success = extendPinCodeTtl(authenticationPublicKey); + let success = extendPinCodeTtl(authenticationPublicKey); - if(success === false) { - result({"data" : "", - "code" : "400", - "status" : "Identity not authenticated" - }) + if (success === false) { + result({ + data: "", + code: "400", + status: "Identity not authenticated" + }); } - executeRestfulFunction("private", viamApi, viamApi.identityDestroyKeysForDevice, null, btoa(authenticationPublicKeyArg)).then(executeResult => { + executeRestfulFunction( + "private", + viamApi, + viamApi.identityDestroyKeysForDevice, + null, + btoa(authenticationPublicKeyArg) + ).then(executeResult => { result(executeResult); }); }); }, logout: async () => { try { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); - if (!authenticationPublicKey || !window.loadedIdentities[authenticationPublicKey]) { + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); + if ( + !authenticationPublicKey || + !window.loadedIdentities[authenticationPublicKey] + ) { return { data: "", code: "400", @@ -753,7 +887,7 @@ const connection = Penpal.connectToParent({ // Clone headers to be able destroy authentication first. // We need it because clients should be able reload page right after logout invocation and not wait until request completed - const headers = {...window.viamApi.getConfig().headers}; + const headers = { ...window.viamApi.getConfig().headers }; destroyAuthentication(); @@ -778,141 +912,149 @@ const connection = Penpal.connectToParent({ viamApi.setSessionData("", ""); viamApi.setIdentity(restoreAccessIdentity.authentication.publicKey); - executeRestfulFunction("public", viamApi, viamApi.identityRestoreAccess, null, identificator).then(executeResult => { - result(executeResult); + executeRestfulFunction( + "public", + viamApi, + viamApi.identityRestoreAccess, + null, + identificator + ).then(executeResult => { + result(executeResult); }); }); }, parseSMIME, getCurrentlyLoggedInUUID() { return new Penpal.Promise(result => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if (authenticationPublicKey === null) { - return {"data" : "", - "code" : "400", - "status" : "Identity not loaded" - } + return { data: "", code: "400", status: "Identity not loaded" }; } if (window.loadedIdentities[authenticationPublicKey] === null) { - return {"data" : "", - "code" : "400", - "status" : "Identity not loaded" - } + return { data: "", code: "400", status: "Identity not loaded" }; } - var success = extendPinCodeTtl(authenticationPublicKey); + let success = extendPinCodeTtl(authenticationPublicKey); - if(success === false) { - result({"data" : "", - "code" : "400", - "status" : "Identity not authenticated" - }) + if (success === false) { + result({ + data: "", + code: "400", + status: "Identity not authenticated" + }); } - if(localStorage.getItem("uuid") === null) { - result({"data" : "", - "code" : "400", - "status" : "Not logged in UUID" - }) + if (localStorage.getItem("uuid") === null) { + result({ data: "", code: "400", status: "Not logged in UUID" }); } - result({"data" : localStorage.getItem("uuid"), - "code" : "200", - "status" : "UUID loaded" - }) + result({ + data: localStorage.getItem("uuid"), + code: "200", + status: "UUID loaded" + }); }); }, getCertificateByPassport(passportUUID) { return new Penpal.Promise(result => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if (authenticationPublicKey === null) { - return {"data" : "", - "code" : "400", - "status" : "Identity not loaded" - } + return { data: "", code: "400", status: "Identity not loaded" }; } if (window.loadedIdentities[authenticationPublicKey] === null) { - return {"data" : "", - "code" : "400", - "status" : "Identity not loaded" - } + return { data: "", code: "400", status: "Identity not loaded" }; } - var success = extendPinCodeTtl(authenticationPublicKey); + let success = extendPinCodeTtl(authenticationPublicKey); - if(success === false) { - result({"data" : "", - "code" : "400", - "status" : "Identity not authenticated" - }) + if (success === false) { + result({ + data: "", + code: "400", + status: "Identity not authenticated" + }); } - getCertificateForPassport(passportUUID, false).then(certificateResult => { - result(certificateResult) - }) + getCertificateForPassport(passportUUID, false).then( + certificateResult => { + result(certificateResult); + } + ); }); }, getOneTimeCertificateByPassport(passportUUID, emailArg) { return new Penpal.Promise(result => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if (authenticationPublicKey === null) { - return {"data" : "", - "code" : "400", - "status" : "Identity not loaded" - } + return { data: "", code: "400", status: "Identity not loaded" }; } if (window.loadedIdentities[authenticationPublicKey] === null) { - return {"data" : "", - "code" : "400", - "status" : "Identity not loaded" - } + return { data: "", code: "400", status: "Identity not loaded" }; } - var success = extendPinCodeTtl(authenticationPublicKey); + let success = extendPinCodeTtl(authenticationPublicKey); - if(success === false) { - result({"data" : "", - "code" : "400", - "status" : "Identity not authenticated" - }) + if (success === false) { + result({ + data: "", + code: "400", + status: "Identity not authenticated" + }); } - getCertificateForPassport(passportUUID, true).then(certificateResult => { - if(certificateResult.code === "200") { - var passportCertificate = certificateResult.data["x509Certificate"]; - var passportPrivateKey = certificateResult.data["privateKey"]; - var passportChain = certificateResult.data["chain"]; - - createOneTimePassportCertificate(makeid() + "-" + passportUUID, emailArg, passportPrivateKey, passportCertificate).then(function(keys){ - var publicKeyOneTime = keys["publicKeyPEM"]; - var privateKeyOneTime = keys["privateKeyPEM"]; - var certificateOneTime = keys["certificatePEM"]; - passportChain.push(passportCertificate); - - var oneTimeCryptoData = new CryptoData(); - oneTimeCryptoData.setx509Certificate(certificateOneTime); - oneTimeCryptoData.setPrivateKey(privateKeyOneTime); - oneTimeCryptoData.setPublicKey(publicKeyOneTime); - oneTimeCryptoData.setChain(passportChain); - - result({"data" : oneTimeCryptoData, - "code" : "200", - "status" : "One time certificate generated" - }) - // Prints PEM formatted signed certificate - // -----BEGIN CERTIFICATE-----MIID....7Hyg==-----END CERTIFICATE----- - - }); - } else { - result({"data" : "", - "code" : "400", - "status" : "Can not generate one time certificate" - }) + getCertificateForPassport(passportUUID, true).then( + certificateResult => { + if (certificateResult.code === "200") { + let passportCertificate = + certificateResult.data["x509Certificate"]; + let passportPrivateKey = certificateResult.data["privateKey"]; + let passportChain = certificateResult.data["chain"]; + + createOneTimePassportCertificate( + makeid() + "-" + passportUUID, + emailArg, + passportPrivateKey, + passportCertificate + ).then(function(keys) { + let publicKeyOneTime = keys["publicKeyPEM"]; + let privateKeyOneTime = keys["privateKeyPEM"]; + let certificateOneTime = keys["certificatePEM"]; + passportChain.push(passportCertificate); + + let oneTimeCryptoData = new CryptoData(); + oneTimeCryptoData.setx509Certificate(certificateOneTime); + oneTimeCryptoData.setPrivateKey(privateKeyOneTime); + oneTimeCryptoData.setPublicKey(publicKeyOneTime); + oneTimeCryptoData.setChain(passportChain); + + result({ + data: oneTimeCryptoData, + code: "200", + status: "One time certificate generated" + }); + // Prints PEM formatted signed certificate + // -----BEGIN CERTIFICATE-----MIID....7Hyg==-----END CERTIFICATE----- + }); + } else { + result({ + data: "", + code: "400", + status: "Can not generate one time certificate" + }); + } } - }) + ); }); }, signEmail: async (passportUUID, emailArg, emailMessage) => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if ( !authenticationPublicKey || @@ -934,25 +1076,48 @@ const connection = Penpal.connectToParent({ chain: passportChain } = response.data; - const keys = - await createOneTimePassportCertificate( - makeid() + "-" + passportUUID, emailArg, passportPrivateKey, passportCertificate); + const keys = await createOneTimePassportCertificate( + makeid() + "-" + passportUUID, + emailArg, + passportPrivateKey, + passportCertificate + ); - const { privateKeyPEM: privateKeyOneTime, certificatePEM: certificateOneTime } = keys; + const { + privateKeyPEM: privateKeyOneTime, + certificatePEM: certificateOneTime + } = keys; passportChain.push(passportCertificate); response = await executeRestfulFunction( - "private", window.viamApi, window.viamApi.passportGetEmailWithHeaderByPassport, null, passportUUID, emailMessage); + "private", + window.viamApi, + window.viamApi.passportGetEmailWithHeaderByPassport, + null, + passportUUID, + emailMessage + ); if (response.code !== "200") { return encodeResponse("400", "", response.status); } - const signedEmail = await signEmail(response.data, certificateOneTime, passportChain, privateKeyOneTime); + const signedEmail = await signEmail( + response.data, + certificateOneTime, + passportChain, + privateKeyOneTime + ); response = await executeRestfulFunction( - "private", window.viamApi, window.viamApi.signResignEmail, null, passportUUID, signedEmail); + "private", + window.viamApi, + window.viamApi.signResignEmail, + null, + passportUUID, + signedEmail + ); if (response.code !== "200") { return encodeResponse("400", "", response.status); @@ -961,8 +1126,9 @@ const connection = Penpal.connectToParent({ return encodeResponse("200", response.data, "Email signed"); }, signDocument: async (passportUUID, documentUUID, documentContentType) => { - - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if ( !authenticationPublicKey || @@ -984,26 +1150,45 @@ const connection = Penpal.connectToParent({ chain: passportChain } = certResponse.data; - const keys = - await createOneTimePassportCertificate( - makeid() + "-" + passportUUID, null, passportPrivateKey, passportCertificate); + const keys = await createOneTimePassportCertificate( + makeid() + "-" + passportUUID, + null, + passportPrivateKey, + passportCertificate + ); - const { privateKeyPEM: privateKeyOneTime, certificatePEM: certificateOneTime } = keys; + const { + privateKeyPEM: privateKeyOneTime, + certificatePEM: certificateOneTime + } = keys; passportChain.push(passportCertificate); - const pdfContentType = 'application/pdf'; + const pdfContentType = "application/pdf"; if (documentContentType !== pdfContentType) { const convResponse = await executeRestfulFunction( - "private", window.viamApi, window.viamApi.documentConvertDocumentByUUID, null, documentUUID, documentContentType, pdfContentType); + "private", + window.viamApi, + window.viamApi.documentConvertDocumentByUUID, + null, + documentUUID, + documentContentType, + pdfContentType + ); if (convResponse.code !== "200") { return encodeResponse("400", "", convResponse.status); } } const downloadResponse = await executeRestfulFunction( - "private", window.viamApi, window.viamApi.documentGetDocumentByUUID, null, documentUUID, pdfContentType); + "private", + window.viamApi, + window.viamApi.documentGetDocumentByUUID, + null, + documentUUID, + pdfContentType + ); if (downloadResponse.code !== "200") { return encodeResponse("400", "", downloadResponse.status); @@ -1013,7 +1198,12 @@ const connection = Penpal.connectToParent({ let signedPdf; try { - signedPdf = await signPdf(pdfRaw, certificateOneTime, passportChain, privateKeyOneTime); + signedPdf = await signPdf( + pdfRaw, + certificateOneTime, + passportChain, + privateKeyOneTime + ); } catch (err) { console.error(err); return encodeResponse("500", "", err.message); @@ -1022,13 +1212,27 @@ const connection = Penpal.connectToParent({ const signedPdfB64 = byteArrayToBase64(signedPdf); const uploadResponse = await executeRestfulFunction( - "private", window.viamApi, window.viamApi.documentPutDocumentByUUID, null, documentUUID, pdfContentType, signedPdfB64); + "private", + window.viamApi, + window.viamApi.documentPutDocumentByUUID, + null, + documentUUID, + pdfContentType, + signedPdfB64 + ); if (uploadResponse.code !== "200") { return encodeResponse("400", "", uploadResponse.status); } const signResponse = await executeRestfulFunction( - "private", window.viamApi, window.viamApi.documentSignDocumentByUUID, null, passportUUID, documentUUID, pdfContentType); + "private", + window.viamApi, + window.viamApi.documentSignDocumentByUUID, + null, + passportUUID, + documentUUID, + pdfContentType + ); if (signResponse.code !== "200") { return encodeResponse("400", "", signResponse.status); } @@ -1036,7 +1240,9 @@ const connection = Penpal.connectToParent({ return encodeResponse("200", "", "Document signed"); }, documentCreateDocument: async (passportUUID, path, contentType, title) => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if ( !authenticationPublicKey || !window.loadedIdentities[authenticationPublicKey] || @@ -1057,8 +1263,12 @@ const connection = Penpal.connectToParent({ title } }; - const response = await executeRestfulFunction("private", window.viamApi, window.viamApi.documentCreateDocument, - config); + const response = await executeRestfulFunction( + "private", + window.viamApi, + window.viamApi.documentCreateDocument, + config + ); if (response.code !== "200") { return encodeResponse("400", "", response.status); @@ -1066,8 +1276,16 @@ const connection = Penpal.connectToParent({ return encodeResponse("200", response.data, "Document created"); }, - documentPutDocument: async (passportUUID, resourceid, contentType, file, upload) => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + documentPutDocument: async ( + passportUUID, + resourceid, + contentType, + file, + upload + ) => { + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if ( !authenticationPublicKey || !window.loadedIdentities[authenticationPublicKey] || @@ -1081,7 +1299,7 @@ const connection = Penpal.connectToParent({ const config = { headers: { - 'Content-Type': 'multipart/form-data', + "Content-Type": "multipart/form-data", passportuuid: passportUUID, resourceid, contentType, @@ -1090,7 +1308,12 @@ const connection = Penpal.connectToParent({ }; const response = await executeRestfulFunction( - "private", window.viamApi, window.viamApi.documentPutDocument, config, file); + "private", + window.viamApi, + window.viamApi.documentPutDocument, + config, + file + ); if (response.code !== "200") { return encodeResponse("400", "", response.status); @@ -1100,30 +1323,40 @@ const connection = Penpal.connectToParent({ }, hasSession() { return new Penpal.Promise(result => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if (authenticationPublicKey === null) { - result({"data" : "", - "code" : "400", - "status" : "Identity not authenticated" + result({ + data: "", + code: "400", + status: "Identity not authenticated" }); } if (window.loadedIdentities[authenticationPublicKey] === null) { - result({"data" : "", - "code" : "400", - "status" : "Identity not authenticated" + result({ + data: "", + code: "400", + status: "Identity not authenticated" }); } - var success = extendPinCodeTtl(authenticationPublicKey); + let success = extendPinCodeTtl(authenticationPublicKey); - if(success === false) { - result({"data" : "", - "code" : "400", - "status" : "Identity not authenticated" - }) + if (success === false) { + result({ + data: "", + code: "400", + status: "Identity not authenticated" + }); } - executeRestfulFunction("private", viamApi, viamApi.identityHasSession, null).then(executeResult => { + executeRestfulFunction( + "private", + viamApi, + viamApi.identityHasSession, + null + ).then(executeResult => { result(executeResult); }); }); @@ -1132,7 +1365,14 @@ const connection = Penpal.connectToParent({ return new Penpal.Promise(result => { viamApi.setIdentity("marketingapppublickey"); - executeRestfulFunction("public", viamApi, viamApi.marketingSignUpIdentificator, null, identificator, reference).then(executeResult => { + executeRestfulFunction( + "public", + viamApi, + viamApi.marketingSignUpIdentificator, + null, + identificator, + reference + ).then(executeResult => { viamApi.setIdentity(""); viamApi.setSessionData("", ""); result(executeResult); @@ -1143,7 +1383,14 @@ const connection = Penpal.connectToParent({ return new Penpal.Promise(result => { viamApi.setIdentity("marketingapppublickey"); - executeRestfulFunction("public", viamApi, viamApi.marketingGetIdentificatorProfile, null, identificator, pincode).then(executeResult => { + executeRestfulFunction( + "public", + viamApi, + viamApi.marketingGetIdentificatorProfile, + null, + identificator, + pincode + ).then(executeResult => { viamApi.setIdentity(""); viamApi.setSessionData("", ""); result(executeResult); @@ -1154,7 +1401,15 @@ const connection = Penpal.connectToParent({ return new Penpal.Promise(result => { viamApi.setIdentity("marketingapppublickey"); - executeRestfulFunction("public", viamApi, viamApi.marketingExecuteEventForIdentificator, null, identificator, pincode, event).then(executeResult => { + executeRestfulFunction( + "public", + viamApi, + viamApi.marketingExecuteEventForIdentificator, + null, + identificator, + pincode, + event + ).then(executeResult => { viamApi.setIdentity(""); viamApi.setSessionData("", ""); result(executeResult); @@ -1162,7 +1417,10 @@ const connection = Penpal.connectToParent({ }); }, getCurrentlyAuthenticatedIdentity() { - const { publicKey, x509Certificate } = window.currentlyAuthenticatedIdentity.authentication; + const { + publicKey, + x509Certificate + } = window.currentlyAuthenticatedIdentity.authentication; return encodeResponse( "200", @@ -1213,7 +1471,9 @@ const connection = Penpal.connectToParent({ // WOPI getPassportsNewProtocol: async (resourceID, contentType) => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if ( !authenticationPublicKey || @@ -1223,12 +1483,17 @@ const connection = Penpal.connectToParent({ return encodeResponse("400", "", "Identity not authenticated"); } - const response = await wopiAPI.getPassportsNewProtocol(resourceID, contentType); + const response = await wopiAPI.getPassportsNewProtocol( + resourceID, + contentType + ); return response.data; }, - getPassports: async fileId => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + getPassports: async fileId => { + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if ( !authenticationPublicKey || @@ -1243,7 +1508,9 @@ const connection = Penpal.connectToParent({ }, wopiCreateDocument: async (passportUUID, path, contentType, title) => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if ( !authenticationPublicKey || !window.loadedIdentities[authenticationPublicKey] || @@ -1260,8 +1527,12 @@ const connection = Penpal.connectToParent({ title } }; - const executeResult = await executeRestfulFunction("private", window.viamApi, window.viamApi.documentCreateDocument, - config); + const executeResult = await executeRestfulFunction( + "private", + window.viamApi, + window.viamApi.documentCreateDocument, + config + ); if (executeResult.code !== "200") return executeResult; const resourceID = executeResult.data; const passports = await wopiAPI.getPassports(resourceID, contentType); @@ -1269,7 +1540,9 @@ const connection = Penpal.connectToParent({ }, wopiPutFile: async (path, accessToken, file) => { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if ( !authenticationPublicKey || @@ -1293,9 +1566,10 @@ connection.promise.then(parent => { return; } - window.addEventListener('storage', event => { + window.addEventListener("storage", event => { if (event.key === "authenticatedIdentity" && event.newValue === null) { - const publicKey = window.currentlyAuthenticatedIdentity.authentication.publicKey; + const publicKey = + window.currentlyAuthenticatedIdentity.authentication.publicKey; window.currentlyLoadedIdentity = null; window.currentlyAuthenticatedIdentity = null; const event = createEvent("LogoutFromAnotherTab", "Logout", [publicKey]); @@ -1322,7 +1596,9 @@ connection.promise.then(parent => { localStorage.removeItem("token"); localStorage.removeItem("authenticatedIdentity"); } else { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); const pinCode = getPincode(authenticationPublicKey); if (pinCode === "" || pinCode === null) { @@ -1366,27 +1642,42 @@ connection.promise.then(parent => { let previousLocalStorageToken; let previousLocalStorageIdentity; - setInterval(async function () { + setInterval(async function() { if (window.currentlyAuthenticatedIdentity) { const { authentication } = window.currentlyAuthenticatedIdentity; const pinCode = getPincode(authentication.publicKey); if (pinCode) { - const identity = await getIdentityFromLocalStorage(authentication.publicKey, pinCode, false); + const identity = await getIdentityFromLocalStorage( + authentication.publicKey, + pinCode, + false + ); window.currentlyLoadedIdentity = identity; if (!identityAuthenticatedEvent && identity) { - const event = createEvent("IdentityAuthenticated", "Authenticated", [identity.authentication.publicKey]); + const event = createEvent("IdentityAuthenticated", "Authenticated", [ + identity.authentication.publicKey + ]); parent.onEvent(event); identityAuthenticatedEvent = true; } } else { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); if (authenticationPublicKey) { - const result = await loadIdentityInternal(authenticationPublicKey, "00000000"); + const result = await loadIdentityInternal( + authenticationPublicKey, + "00000000" + ); if (result.code !== "200") { - const event = createEvent("CanNotGetPincodeForAuthenticatedIdentity", "IdentityNotLoaded", [authenticationPublicKey]); + const event = createEvent( + "CanNotGetPincodeForAuthenticatedIdentity", + "IdentityNotLoaded", + [authenticationPublicKey] + ); parent.onEvent(event); clearPinCodeTtl(authenticationPublicKey); window.currentlyAuthenticatedIdentity = null; @@ -1399,12 +1690,21 @@ connection.promise.then(parent => { } if (window.currentlyLoadedIdentity) { - const pinCode = getPincode(window.currentlyLoadedIdentity.authentication.publicKey); + const pinCode = getPincode( + window.currentlyLoadedIdentity.authentication.publicKey + ); if (!pinCode) { if (!identityLoadedEvent) { - const result = await loadIdentityInternal(window.currentlyLoadedIdentity.authentication.publicKey, "00000000"); + const result = await loadIdentityInternal( + window.currentlyLoadedIdentity.authentication.publicKey, + "00000000" + ); if (window.currentlyLoadedIdentity && result.code !== "200") { - const event = createEvent("CanNotLoadPincodeForLoadedIdentity", "IdentityNotLoaded", [window.currentlyLoadedIdentity.authentication.publicKey]); + const event = createEvent( + "CanNotLoadPincodeForLoadedIdentity", + "IdentityNotLoaded", + [window.currentlyLoadedIdentity.authentication.publicKey] + ); parent.onEvent(event); identityLoadedEvent = true; } @@ -1424,11 +1724,13 @@ connection.promise.then(parent => { const currentLocalStorageUUID = localStorage.getItem("uuid"); const currentLocalStorageToken = localStorage.getItem("token"); - const currentLocalStorageIdentity = localStorage.getItem("authenticatedIdentity"); + const currentLocalStorageIdentity = localStorage.getItem( + "authenticatedIdentity" + ); if ( - !currentLocalStorageUUID && previousLocalStorageUUID || - !currentLocalStorageToken && previousLocalStorageToken || - !currentLocalStorageIdentity && previousLocalStorageIdentity + (!currentLocalStorageUUID && previousLocalStorageUUID) || + (!currentLocalStorageToken && previousLocalStorageToken) || + (!currentLocalStorageIdentity && previousLocalStorageIdentity) ) { previousLocalStorageUUID = null; previousLocalStorageToken = null; @@ -1448,34 +1750,42 @@ connection.promise.then(parent => { const getNewEventsWithoutSession = async () => { anynomousDeviceKeyEventsProcessing = true; try { - const executeResult = await executeRestfulFunction("public", viamAnonymousApi, viamAnonymousApi.eventGetNewEventsWithoutSession, null, "devicekey"); - if(executeResult.code === "200") { + const executeResult = await executeRestfulFunction( + "public", + viamAnonymousApi, + viamAnonymousApi.eventGetNewEventsWithoutSession, + null, + "devicekey" + ); + if (executeResult.code === "200") { const eventsLen = executeResult.data.length; let changedMaxDeviceKeyAnonymousEventTime = false; for (let i = 0; i < eventsLen; i++) { const event = executeResult.data[i]; switch (event.type) { - case "DeviceConfirmed" : { + case "DeviceConfirmed": { await setIdentityInLocalStorage(window.currentlyLoadedIdentity); parent.onEvent(event); break; } - case "QRCodeUpdated" : { + case "QRCodeUpdated": { const actionID = event["actionID"]; const QrCode = event["payloads"][1]; const eventCopy = JSON.parse(JSON.stringify(event)); - QRCode.toDataURL(actionID + "," + QrCode, function (err, url) { + QRCode.toDataURL(actionID + "," + QrCode, function(err, url) { eventCopy["payloads"].push(url); parent.onEvent(eventCopy); }); break; } - case "KeyDeleted" : { - const authenticationPublicKey = localStorage.getItem("authenticatedIdentity"); + case "KeyDeleted": { + const authenticationPublicKey = localStorage.getItem( + "authenticatedIdentity" + ); clearPinCodeTtl(authenticationPublicKey); localStorage.removeItem("uuid"); localStorage.removeItem("token"); @@ -1489,17 +1799,26 @@ connection.promise.then(parent => { break; } - default : { + default: { parent.onEvent(event); } } changedMaxDeviceKeyAnonymousEventTime = true; - maxDeviceKeyAnonymousEventTime = Math.max(maxDeviceKeyAnonymousEventTime, event.stamp); + maxDeviceKeyAnonymousEventTime = Math.max( + maxDeviceKeyAnonymousEventTime, + event.stamp + ); } - if(changedMaxDeviceKeyAnonymousEventTime) { - await executeRestfulFunction("public", viamAnonymousApi, viamAnonymousApi.eventUpdateLastViewedWithoutSession, - null, "devicekey", maxDeviceKeyAnonymousEventTime.toString()); + if (changedMaxDeviceKeyAnonymousEventTime) { + await executeRestfulFunction( + "public", + viamAnonymousApi, + viamAnonymousApi.eventUpdateLastViewedWithoutSession, + null, + "devicekey", + maxDeviceKeyAnonymousEventTime.toString() + ); } } } catch (e) { @@ -1511,7 +1830,13 @@ connection.promise.then(parent => { const getNewDeviceEvents = async () => { eventsDeviceEventsProcessing = true; try { - const executeResult = await executeRestfulFunction("private", viamApi, viamApi.eventGetNewEvents, null, "devicekey"); + const executeResult = await executeRestfulFunction( + "private", + viamApi, + viamApi.eventGetNewEvents, + null, + "devicekey" + ); if (executeResult.code === "200") { const eventsLen = executeResult.data.length; const changedMaxDeviceKeyEventTime = false; @@ -1523,7 +1848,7 @@ connection.promise.then(parent => { const eventCopy = JSON.parse(JSON.stringify(event)); - QRCode.toDataURL(actionID + "," + QrCode, function (err, url) { + QRCode.toDataURL(actionID + "," + QrCode, function(err, url) { eventCopy["payloads"].push(url); parent.onEvent(eventCopy); }); @@ -1532,9 +1857,15 @@ connection.promise.then(parent => { } maxDeviceKeyEventTime = Math.max(maxDeviceKeyEventTime, event.stamp); } - if(changedMaxDeviceKeyEventTime) { - await executeRestfulFunction("private", viamApi, viamApi.eventUpdateLastViewed, null, "devicekey", - maxDeviceKeyEventTime.toString()); + if (changedMaxDeviceKeyEventTime) { + await executeRestfulFunction( + "private", + viamApi, + viamApi.eventUpdateLastViewed, + null, + "devicekey", + maxDeviceKeyEventTime.toString() + ); } } } catch (e) { @@ -1546,7 +1877,13 @@ connection.promise.then(parent => { const getNewEntityEvents = async () => { eventsEntityEventsProcessing = true; try { - const executeResult = await executeRestfulFunction("private", viamApi, viamApi.eventGetNewEvents, null, "entity"); + const executeResult = await executeRestfulFunction( + "private", + viamApi, + viamApi.eventGetNewEvents, + null, + "entity" + ); if (executeResult.code === "200") { const eventsLen = executeResult.data.length; @@ -1559,7 +1896,7 @@ connection.promise.then(parent => { const eventCopy = JSON.parse(JSON.stringify(event)); - QRCode.toDataURL(actionID + "," + QrCode, function (err, url) { + QRCode.toDataURL(actionID + "," + QrCode, function(err, url) { eventCopy["payloads"].push(url); parent.onEvent(eventCopy); }); @@ -1571,19 +1908,29 @@ connection.promise.then(parent => { changedMaxEntityEventTime = true; maxEntityEventTime = Math.max(maxEntityEventTime, event.stamp); } - if(changedMaxEntityEventTime) { - await executeRestfulFunction("private", viamApi, viamApi.eventUpdateLastViewed, null, "entity", - maxEntityEventTime.toString()); + if (changedMaxEntityEventTime) { + await executeRestfulFunction( + "private", + viamApi, + viamApi.eventUpdateLastViewed, + null, + "entity", + maxEntityEventTime.toString() + ); } } } catch (e) { console.warn(e); } eventsEntityEventsProcessing = false; - } + }; setInterval(() => { - if (window.currentlyLoadedIdentity && !anynomousDeviceKeyEventsProcessing && !window.currentlyAuthenticatedIdentity) { + if ( + window.currentlyLoadedIdentity && + !anynomousDeviceKeyEventsProcessing && + !window.currentlyAuthenticatedIdentity + ) { getNewEventsWithoutSession(); } diff --git a/javascript/src/iframe/wopiapi-iframe.js b/javascript/src/iframe/wopiapi-iframe.js index a7106a486dc8a6ec9515197ad52e9d1bf770e9a1..9de61de7ce47792039a5944cd2e76ae4c68aa7f1 100644 --- a/javascript/src/iframe/wopiapi-iframe.js +++ b/javascript/src/iframe/wopiapi-iframe.js @@ -1,12 +1,17 @@ -const axios = require('axios'); +const axios = require("axios"); function WopiAPI() {} -WopiAPI.prototype.getPassportsNewProtocol = function (resourceID, contentType) { - const { publicKey, uuid, token, deviceHash } = window.viamApi.getConfig().headers; +WopiAPI.prototype.getPassportsNewProtocol = function(resourceID, contentType) { + const { + publicKey, + uuid, + token, + deviceHash + } = window.viamApi.getConfig().headers; const requestConfig = { url: `${window.WOPI_URL}getPassportsNewProtocol`, - method: 'POST', + method: "POST", headers: { publicKey, uuid, @@ -20,11 +25,16 @@ WopiAPI.prototype.getPassportsNewProtocol = function (resourceID, contentType) { return axios(requestConfig); }; -WopiAPI.prototype.getPassports = function (fileID) { - const { publicKey, uuid, token, deviceHash } = window.viamApi.getConfig().headers; +WopiAPI.prototype.getPassports = function(fileID) { + const { + publicKey, + uuid, + token, + deviceHash + } = window.viamApi.getConfig().headers; const requestConfig = { url: `${window.WOPI_URL}getPassports`, - method: 'POST', + method: "POST", headers: { publicKey, uuid, @@ -37,13 +47,18 @@ WopiAPI.prototype.getPassports = function (fileID) { return axios(requestConfig); }; -WopiAPI.prototype.putDocument = function (resourceID, accessToken, file) { - const { publicKey, uuid, token, deviceHash } = window.viamApi.getConfig().headers; +WopiAPI.prototype.putDocument = function(resourceID, accessToken, file) { + const { + publicKey, + uuid, + token, + deviceHash + } = window.viamApi.getConfig().headers; resourceID = encodeURI(resourceID); const requestConfig = { url: `${window.WOPI_URL}files/${resourceID}/contents`, - method: 'POST', + method: "POST", headers: { publicKey, uuid, diff --git a/javascript/src/lib/pdfjs.parser.js b/javascript/src/lib/pdfjs.parser.js index 69c7db74c0036427245d5077d9f586a064117c76..c0329bd65c119e0c5d875360fd939189c1d0edf1 100644 --- a/javascript/src/lib/pdfjs.parser.js +++ b/javascript/src/lib/pdfjs.parser.js @@ -1,10256 +1,14639 @@ -/* Copyright 2015 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* globals global */ - -'use strict'; - -(function (root, factory) { - //if (typeof define === 'function' && define.amd) { - // define('pdfjs/shared/global', ['exports'], factory); - // } else if (typeof exports !== 'undefined') { - // factory(exports); - //} else { - factory((root.pdfjsSharedGlobal = {})); - //} -}(window, function (exports) { - - var globalScope = (typeof window !== 'undefined') ? window : - (typeof global !== 'undefined') ? global : - (typeof self !== 'undefined') ? self : this; - - var isWorker = (typeof window === 'undefined'); - - // The global PDFJS object exposes the API - // In production, it will be declared outside a global wrapper - // In development, it will be declared here - if (!globalScope.PDFJS) { - globalScope.PDFJS = {}; - } - - globalScope.PDFJS.pdfBug = false; - - exports.globalScope = globalScope; - exports.isWorker = isWorker; - exports.PDFJS = globalScope.PDFJS; -})); - -/* Copyright 2012 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* globals MozBlobBuilder, URL */ - -'use strict'; - -(function (root, factory) { - //if (typeof define === 'function' && define.amd) { - // define('pdfjs/shared/util', ['exports', 'pdfjs/shared/global'], factory); - // } else if (typeof exports !== 'undefined') { - // factory(exports, require('./global.js')); - //} else { - factory((root.pdfjsSharedUtil = {}), root.pdfjsSharedGlobal); - //} -}(window, function (exports, sharedGlobal) { - -var PDFJS = sharedGlobal.PDFJS; -var globalScope = sharedGlobal.globalScope; - -var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; - -var TextRenderingMode = { - FILL: 0, - STROKE: 1, - FILL_STROKE: 2, - INVISIBLE: 3, - FILL_ADD_TO_PATH: 4, - STROKE_ADD_TO_PATH: 5, - FILL_STROKE_ADD_TO_PATH: 6, - ADD_TO_PATH: 7, - FILL_STROKE_MASK: 3, - ADD_TO_PATH_FLAG: 4 -}; - -var ImageKind = { - GRAYSCALE_1BPP: 1, - RGB_24BPP: 2, - RGBA_32BPP: 3 -}; - -var AnnotationType = { - TEXT: 1, - LINK: 2, - FREETEXT: 3, - LINE: 4, - SQUARE: 5, - CIRCLE: 6, - POLYGON: 7, - POLYLINE: 8, - HIGHLIGHT: 9, - UNDERLINE: 10, - SQUIGGLY: 11, - STRIKEOUT: 12, - STAMP: 13, - CARET: 14, - INK: 15, - POPUP: 16, - FILEATTACHMENT: 17, - SOUND: 18, - MOVIE: 19, - WIDGET: 20, - SCREEN: 21, - PRINTERMARK: 22, - TRAPNET: 23, - WATERMARK: 24, - THREED: 25, - REDACT: 26 -}; - -var AnnotationFlag = { - INVISIBLE: 0x01, - HIDDEN: 0x02, - PRINT: 0x04, - NOZOOM: 0x08, - NOROTATE: 0x10, - NOVIEW: 0x20, - READONLY: 0x40, - LOCKED: 0x80, - TOGGLENOVIEW: 0x100, - LOCKEDCONTENTS: 0x200 -}; - -var AnnotationBorderStyleType = { - SOLID: 1, - DASHED: 2, - BEVELED: 3, - INSET: 4, - UNDERLINE: 5 -}; - -var StreamType = { - UNKNOWN: 0, - FLATE: 1, - LZW: 2, - DCT: 3, - JPX: 4, - JBIG: 5, - A85: 6, - AHX: 7, - CCF: 8, - RL: 9 -}; - -var FontType = { - UNKNOWN: 0, - TYPE1: 1, - TYPE1C: 2, - CIDFONTTYPE0: 3, - CIDFONTTYPE0C: 4, - TRUETYPE: 5, - CIDFONTTYPE2: 6, - TYPE3: 7, - OPENTYPE: 8, - TYPE0: 9, - MMTYPE1: 10 -}; - -PDFJS.VERBOSITY_LEVELS = { - errors: 0, - warnings: 1, - infos: 5 -}; - -// All the possible operations for an operator list. -var OPS = PDFJS.OPS = { - // Intentionally start from 1 so it is easy to spot bad operators that will be - // 0's. - dependency: 1, - setLineWidth: 2, - setLineCap: 3, - setLineJoin: 4, - setMiterLimit: 5, - setDash: 6, - setRenderingIntent: 7, - setFlatness: 8, - setGState: 9, - save: 10, - restore: 11, - transform: 12, - moveTo: 13, - lineTo: 14, - curveTo: 15, - curveTo2: 16, - curveTo3: 17, - closePath: 18, - rectangle: 19, - stroke: 20, - closeStroke: 21, - fill: 22, - eoFill: 23, - fillStroke: 24, - eoFillStroke: 25, - closeFillStroke: 26, - closeEOFillStroke: 27, - endPath: 28, - clip: 29, - eoClip: 30, - beginText: 31, - endText: 32, - setCharSpacing: 33, - setWordSpacing: 34, - setHScale: 35, - setLeading: 36, - setFont: 37, - setTextRenderingMode: 38, - setTextRise: 39, - moveText: 40, - setLeadingMoveText: 41, - setTextMatrix: 42, - nextLine: 43, - showText: 44, - showSpacedText: 45, - nextLineShowText: 46, - nextLineSetSpacingShowText: 47, - setCharWidth: 48, - setCharWidthAndBounds: 49, - setStrokeColorSpace: 50, - setFillColorSpace: 51, - setStrokeColor: 52, - setStrokeColorN: 53, - setFillColor: 54, - setFillColorN: 55, - setStrokeGray: 56, - setFillGray: 57, - setStrokeRGBColor: 58, - setFillRGBColor: 59, - setStrokeCMYKColor: 60, - setFillCMYKColor: 61, - shadingFill: 62, - beginInlineImage: 63, - beginImageData: 64, - endInlineImage: 65, - paintXObject: 66, - markPoint: 67, - markPointProps: 68, - beginMarkedContent: 69, - beginMarkedContentProps: 70, - endMarkedContent: 71, - beginCompat: 72, - endCompat: 73, - paintFormXObjectBegin: 74, - paintFormXObjectEnd: 75, - beginGroup: 76, - endGroup: 77, - beginAnnotations: 78, - endAnnotations: 79, - beginAnnotation: 80, - endAnnotation: 81, - paintJpegXObject: 82, - paintImageMaskXObject: 83, - paintImageMaskXObjectGroup: 84, - paintImageXObject: 85, - paintInlineImageXObject: 86, - paintInlineImageXObjectGroup: 87, - paintImageXObjectRepeat: 88, - paintImageMaskXObjectRepeat: 89, - paintSolidColorImageMask: 90, - constructPath: 91 -}; - -// A notice for devs. These are good for things that are helpful to devs, such -// as warning that Workers were disabled, which is important to devs but not -// end users. -function info(msg) { - if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.infos) { - console.log('Info: ' + msg); - } -} - -// Non-fatal warnings. -function warn(msg) { - if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.warnings) { - console.log('Warning: ' + msg); - } -} - -// Deprecated API function -- treated as warnings. -function deprecated(details) { - warn('Deprecated API usage: ' + details); -} - -// Fatal errors that should trigger the fallback UI and halt execution by -// throwing an exception. -function error(msg) { - if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.errors) { - console.log('Error: ' + msg); - console.log(backtrace()); - } - throw new Error(msg); -} - -function backtrace() { - try { - throw new Error(); - } catch (e) { - return e.stack ? e.stack.split('\n').slice(2).join('\n') : ''; - } -} - -function assert(cond, msg) { - if (!cond) { - error(msg); - } -} - -var UNSUPPORTED_FEATURES = PDFJS.UNSUPPORTED_FEATURES = { - unknown: 'unknown', - forms: 'forms', - javaScript: 'javaScript', - smask: 'smask', - shadingPattern: 'shadingPattern', - font: 'font' -}; - -// Combines two URLs. The baseUrl shall be absolute URL. If the url is an -// absolute URL, it will be returned as is. -function combineUrl(baseUrl, url) { - if (!url) { - return baseUrl; - } - if (/^[a-z][a-z0-9+\-.]*:/i.test(url)) { - return url; - } - var i; - if (url.charAt(0) === '/') { - // absolute path - i = baseUrl.indexOf('://'); - if (url.charAt(1) === '/') { - ++i; - } else { - i = baseUrl.indexOf('/', i + 3); - } - return baseUrl.substring(0, i) + url; - } else { - // relative path - var pathLength = baseUrl.length; - i = baseUrl.lastIndexOf('#'); - pathLength = i >= 0 ? i : pathLength; - i = baseUrl.lastIndexOf('?', pathLength); - pathLength = i >= 0 ? i : pathLength; - var prefixLength = baseUrl.lastIndexOf('/', pathLength); - return baseUrl.substring(0, prefixLength + 1) + url; - } -} - -// Validates if URL is safe and allowed, e.g. to avoid XSS. -function isValidUrl(url, allowRelative) { - if (!url) { - return false; - } - // RFC 3986 (http://tools.ietf.org/html/rfc3986#section-3.1) - // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - var protocol = /^[a-z][a-z0-9+\-.]*(?=:)/i.exec(url); - if (!protocol) { - return allowRelative; - } - protocol = protocol[0].toLowerCase(); - switch (protocol) { - case 'http': - case 'https': - case 'ftp': - case 'mailto': - case 'tel': - return true; - default: - return false; - } -} -PDFJS.isValidUrl = isValidUrl; - -function shadow(obj, prop, value) { - Object.defineProperty(obj, prop, { value: value, - enumerable: true, - configurable: true, - writable: false }); - return value; -} -PDFJS.shadow = shadow; - -var LinkTarget = PDFJS.LinkTarget = { - NONE: 0, // Default value. - SELF: 1, - BLANK: 2, - PARENT: 3, - TOP: 4, -}; -var LinkTargetStringMap = [ - '', - '_self', - '_blank', - '_parent', - '_top' -]; - -function isExternalLinkTargetSet() { -//#if !MOZCENTRAL - if (PDFJS.openExternalLinksInNewWindow) { - deprecated('PDFJS.openExternalLinksInNewWindow, please use ' + - '"PDFJS.externalLinkTarget = PDFJS.LinkTarget.BLANK" instead.'); - if (PDFJS.externalLinkTarget === LinkTarget.NONE) { - PDFJS.externalLinkTarget = LinkTarget.BLANK; - } - // Reset the deprecated parameter, to suppress further warnings. - PDFJS.openExternalLinksInNewWindow = false; - } -//#endif - switch (PDFJS.externalLinkTarget) { - case LinkTarget.NONE: - return false; - case LinkTarget.SELF: - case LinkTarget.BLANK: - case LinkTarget.PARENT: - case LinkTarget.TOP: - return true; - } - warn('PDFJS.externalLinkTarget is invalid: ' + PDFJS.externalLinkTarget); - // Reset the external link target, to suppress further warnings. - PDFJS.externalLinkTarget = LinkTarget.NONE; - return false; -} -PDFJS.isExternalLinkTargetSet = isExternalLinkTargetSet; - -var PasswordResponses = PDFJS.PasswordResponses = { - NEED_PASSWORD: 1, - INCORRECT_PASSWORD: 2 -}; - -var PasswordException = (function PasswordExceptionClosure() { - function PasswordException(msg, code) { - this.name = 'PasswordException'; - this.message = msg; - this.code = code; - } - - PasswordException.prototype = new Error(); - PasswordException.constructor = PasswordException; - - return PasswordException; -})(); -PDFJS.PasswordException = PasswordException; - -var UnknownErrorException = (function UnknownErrorExceptionClosure() { - function UnknownErrorException(msg, details) { - this.name = 'UnknownErrorException'; - this.message = msg; - this.details = details; - } - - UnknownErrorException.prototype = new Error(); - UnknownErrorException.constructor = UnknownErrorException; - - return UnknownErrorException; -})(); -PDFJS.UnknownErrorException = UnknownErrorException; - -var InvalidPDFException = (function InvalidPDFExceptionClosure() { - function InvalidPDFException(msg) { - this.name = 'InvalidPDFException'; - this.message = msg; - } - - InvalidPDFException.prototype = new Error(); - InvalidPDFException.constructor = InvalidPDFException; - - return InvalidPDFException; -})(); -PDFJS.InvalidPDFException = InvalidPDFException; - -var MissingPDFException = (function MissingPDFExceptionClosure() { - function MissingPDFException(msg) { - this.name = 'MissingPDFException'; - this.message = msg; - } - - MissingPDFException.prototype = new Error(); - MissingPDFException.constructor = MissingPDFException; - - return MissingPDFException; -})(); -PDFJS.MissingPDFException = MissingPDFException; - -var UnexpectedResponseException = - (function UnexpectedResponseExceptionClosure() { - function UnexpectedResponseException(msg, status) { - this.name = 'UnexpectedResponseException'; - this.message = msg; - this.status = status; - } - - UnexpectedResponseException.prototype = new Error(); - UnexpectedResponseException.constructor = UnexpectedResponseException; - - return UnexpectedResponseException; -})(); -PDFJS.UnexpectedResponseException = UnexpectedResponseException; - -var NotImplementedException = (function NotImplementedExceptionClosure() { - function NotImplementedException(msg) { - this.message = msg; - } - - NotImplementedException.prototype = new Error(); - NotImplementedException.prototype.name = 'NotImplementedException'; - NotImplementedException.constructor = NotImplementedException; - - return NotImplementedException; -})(); - -var MissingDataException = (function MissingDataExceptionClosure() { - function MissingDataException(begin, end) { - this.begin = begin; - this.end = end; - this.message = 'Missing data [' + begin + ', ' + end + ')'; - } - - MissingDataException.prototype = new Error(); - MissingDataException.prototype.name = 'MissingDataException'; - MissingDataException.constructor = MissingDataException; - - return MissingDataException; -})(); - -var XRefParseException = (function XRefParseExceptionClosure() { - function XRefParseException(msg) { - this.message = msg; - } - - XRefParseException.prototype = new Error(); - XRefParseException.prototype.name = 'XRefParseException'; - XRefParseException.constructor = XRefParseException; - - return XRefParseException; -})(); - - -function bytesToString(bytes) { - assert(bytes !== null && typeof bytes === 'object' && - bytes.length !== undefined, 'Invalid argument for bytesToString'); - var length = bytes.length; - var MAX_ARGUMENT_COUNT = 8192; - if (length < MAX_ARGUMENT_COUNT) { - return String.fromCharCode.apply(null, bytes); - } - var strBuf = []; - for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) { - var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length); - var chunk = bytes.subarray(i, chunkEnd); - strBuf.push(String.fromCharCode.apply(null, chunk)); - } - return strBuf.join(''); -} - -function stringToBytes(str) { - assert(typeof str === 'string', 'Invalid argument for stringToBytes'); - var length = str.length; - var bytes = new Uint8Array(length); - for (var i = 0; i < length; ++i) { - bytes[i] = str.charCodeAt(i) & 0xFF; - } - return bytes; -} - -function string32(value) { - return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff, - (value >> 8) & 0xff, value & 0xff); -} - -function log2(x) { - var n = 1, i = 0; - while (x > n) { - n <<= 1; - i++; - } - return i; -} - -function readInt8(data, start) { - return (data[start] << 24) >> 24; -} - -function readUint16(data, offset) { - return (data[offset] << 8) | data[offset + 1]; -} - -function readUint32(data, offset) { - return ((data[offset] << 24) | (data[offset + 1] << 16) | - (data[offset + 2] << 8) | data[offset + 3]) >>> 0; -} - -// Lazy test the endianness of the platform -// NOTE: This will be 'true' for simulated TypedArrays -function isLittleEndian() { - var buffer8 = new Uint8Array(2); - buffer8[0] = 1; - var buffer16 = new Uint16Array(buffer8.buffer); - return (buffer16[0] === 1); -} - -Object.defineProperty(PDFJS, 'isLittleEndian', { - configurable: true, - get: function PDFJS_isLittleEndian() { - return shadow(PDFJS, 'isLittleEndian', isLittleEndian()); - } -}); - -//#if !(FIREFOX || MOZCENTRAL || CHROME) -//// Lazy test if the userAgent support CanvasTypedArrays -function hasCanvasTypedArrays() { - var canvas = document.createElement('canvas'); - canvas.width = canvas.height = 1; - var ctx = canvas.getContext('2d'); - var imageData = ctx.createImageData(1, 1); - return (typeof imageData.data.buffer !== 'undefined'); -} - -Object.defineProperty(PDFJS, 'hasCanvasTypedArrays', { - configurable: true, - get: function PDFJS_hasCanvasTypedArrays() { - return shadow(PDFJS, 'hasCanvasTypedArrays', hasCanvasTypedArrays()); - } -}); - -var Uint32ArrayView = (function Uint32ArrayViewClosure() { - - function Uint32ArrayView(buffer, length) { - this.buffer = buffer; - this.byteLength = buffer.length; - this.length = length === undefined ? (this.byteLength >> 2) : length; - ensureUint32ArrayViewProps(this.length); - } - Uint32ArrayView.prototype = Object.create(null); - - var uint32ArrayViewSetters = 0; - function createUint32ArrayProp(index) { - return { - get: function () { - var buffer = this.buffer, offset = index << 2; - return (buffer[offset] | (buffer[offset + 1] << 8) | - (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)) >>> 0; - }, - set: function (value) { - var buffer = this.buffer, offset = index << 2; - buffer[offset] = value & 255; - buffer[offset + 1] = (value >> 8) & 255; - buffer[offset + 2] = (value >> 16) & 255; - buffer[offset + 3] = (value >>> 24) & 255; - } - }; - } - - function ensureUint32ArrayViewProps(length) { - while (uint32ArrayViewSetters < length) { - Object.defineProperty(Uint32ArrayView.prototype, - uint32ArrayViewSetters, - createUint32ArrayProp(uint32ArrayViewSetters)); - uint32ArrayViewSetters++; - } - } - - return Uint32ArrayView; -})(); - -exports.Uint32ArrayView = Uint32ArrayView; -//#else -//PDFJS.hasCanvasTypedArrays = true; -//#endif - -var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; - -var Util = PDFJS.Util = (function UtilClosure() { - function Util() {} - - var rgbBuf = ['rgb(', 0, ',', 0, ',', 0, ')']; - - // makeCssRgb() can be called thousands of times. Using |rgbBuf| avoids - // creating many intermediate strings. - Util.makeCssRgb = function Util_makeCssRgb(r, g, b) { - rgbBuf[1] = r; - rgbBuf[3] = g; - rgbBuf[5] = b; - return rgbBuf.join(''); - }; - - // Concatenates two transformation matrices together and returns the result. - Util.transform = function Util_transform(m1, m2) { - return [ - m1[0] * m2[0] + m1[2] * m2[1], - m1[1] * m2[0] + m1[3] * m2[1], - m1[0] * m2[2] + m1[2] * m2[3], - m1[1] * m2[2] + m1[3] * m2[3], - m1[0] * m2[4] + m1[2] * m2[5] + m1[4], - m1[1] * m2[4] + m1[3] * m2[5] + m1[5] - ]; - }; - - // For 2d affine transforms - Util.applyTransform = function Util_applyTransform(p, m) { - var xt = p[0] * m[0] + p[1] * m[2] + m[4]; - var yt = p[0] * m[1] + p[1] * m[3] + m[5]; - return [xt, yt]; - }; - - Util.applyInverseTransform = function Util_applyInverseTransform(p, m) { - var d = m[0] * m[3] - m[1] * m[2]; - var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d; - var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d; - return [xt, yt]; - }; - - // Applies the transform to the rectangle and finds the minimum axially - // aligned bounding box. - Util.getAxialAlignedBoundingBox = - function Util_getAxialAlignedBoundingBox(r, m) { - - var p1 = Util.applyTransform(r, m); - var p2 = Util.applyTransform(r.slice(2, 4), m); - var p3 = Util.applyTransform([r[0], r[3]], m); - var p4 = Util.applyTransform([r[2], r[1]], m); - return [ - Math.min(p1[0], p2[0], p3[0], p4[0]), - Math.min(p1[1], p2[1], p3[1], p4[1]), - Math.max(p1[0], p2[0], p3[0], p4[0]), - Math.max(p1[1], p2[1], p3[1], p4[1]) - ]; - }; - - Util.inverseTransform = function Util_inverseTransform(m) { - var d = m[0] * m[3] - m[1] * m[2]; - return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, - (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d]; - }; - - // Apply a generic 3d matrix M on a 3-vector v: - // | a b c | | X | - // | d e f | x | Y | - // | g h i | | Z | - // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i], - // with v as [X,Y,Z] - Util.apply3dTransform = function Util_apply3dTransform(m, v) { - return [ - m[0] * v[0] + m[1] * v[1] + m[2] * v[2], - m[3] * v[0] + m[4] * v[1] + m[5] * v[2], - m[6] * v[0] + m[7] * v[1] + m[8] * v[2] - ]; - }; - - // This calculation uses Singular Value Decomposition. - // The SVD can be represented with formula A = USV. We are interested in the - // matrix S here because it represents the scale values. - Util.singularValueDecompose2dScale = - function Util_singularValueDecompose2dScale(m) { - - var transpose = [m[0], m[2], m[1], m[3]]; - - // Multiply matrix m with its transpose. - var a = m[0] * transpose[0] + m[1] * transpose[2]; - var b = m[0] * transpose[1] + m[1] * transpose[3]; - var c = m[2] * transpose[0] + m[3] * transpose[2]; - var d = m[2] * transpose[1] + m[3] * transpose[3]; - - // Solve the second degree polynomial to get roots. - var first = (a + d) / 2; - var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2; - var sx = first + second || 1; - var sy = first - second || 1; - - // Scale values are the square roots of the eigenvalues. - return [Math.sqrt(sx), Math.sqrt(sy)]; - }; - - // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2) - // For coordinate systems whose origin lies in the bottom-left, this - // means normalization to (BL,TR) ordering. For systems with origin in the - // top-left, this means (TL,BR) ordering. - Util.normalizeRect = function Util_normalizeRect(rect) { - var r = rect.slice(0); // clone rect - if (rect[0] > rect[2]) { - r[0] = rect[2]; - r[2] = rect[0]; - } - if (rect[1] > rect[3]) { - r[1] = rect[3]; - r[3] = rect[1]; - } - return r; - }; - - // Returns a rectangle [x1, y1, x2, y2] corresponding to the - // intersection of rect1 and rect2. If no intersection, returns 'false' - // The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2] - Util.intersect = function Util_intersect(rect1, rect2) { - function compare(a, b) { - return a - b; - } - - // Order points along the axes - var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare), - orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare), - result = []; - - rect1 = Util.normalizeRect(rect1); - rect2 = Util.normalizeRect(rect2); - - // X: first and second points belong to different rectangles? - if ((orderedX[0] === rect1[0] && orderedX[1] === rect2[0]) || - (orderedX[0] === rect2[0] && orderedX[1] === rect1[0])) { - // Intersection must be between second and third points - result[0] = orderedX[1]; - result[2] = orderedX[2]; - } else { - return false; - } - - // Y: first and second points belong to different rectangles? - if ((orderedY[0] === rect1[1] && orderedY[1] === rect2[1]) || - (orderedY[0] === rect2[1] && orderedY[1] === rect1[1])) { - // Intersection must be between second and third points - result[1] = orderedY[1]; - result[3] = orderedY[2]; - } else { - return false; - } - - return result; - }; - - Util.sign = function Util_sign(num) { - return num < 0 ? -1 : 1; - }; - - Util.appendToArray = function Util_appendToArray(arr1, arr2) { - Array.prototype.push.apply(arr1, arr2); - }; - - Util.prependToArray = function Util_prependToArray(arr1, arr2) { - Array.prototype.unshift.apply(arr1, arr2); - }; - - Util.extendObj = function extendObj(obj1, obj2) { - for (var key in obj2) { - obj1[key] = obj2[key]; - } - }; - - Util.getInheritableProperty = function Util_getInheritableProperty(dict, - name) { - while (dict && !dict.has(name)) { - dict = dict.get('Parent'); - } - if (!dict) { - return null; - } - return dict.get(name); - }; - - Util.inherit = function Util_inherit(sub, base, prototype) { - sub.prototype = Object.create(base.prototype); - sub.prototype.constructor = sub; - for (var prop in prototype) { - sub.prototype[prop] = prototype[prop]; - } - }; - - Util.loadScript = function Util_loadScript(src, callback) { - var script = document.createElement('script'); - var loaded = false; - script.setAttribute('src', src); - if (callback) { - script.onload = function() { - if (!loaded) { - callback(); - } - loaded = true; - }; - } - document.getElementsByTagName('head')[0].appendChild(script); - }; - - return Util; -})(); - -/** - * PDF page viewport created based on scale, rotation and offset. - * @class - * @alias PDFJS.PageViewport - */ -var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() { - /** - * @constructor - * @private - * @param viewBox {Array} xMin, yMin, xMax and yMax coordinates. - * @param scale {number} scale of the viewport. - * @param rotation {number} rotations of the viewport in degrees. - * @param offsetX {number} offset X - * @param offsetY {number} offset Y - * @param dontFlip {boolean} if true, axis Y will not be flipped. - */ - function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) { - this.viewBox = viewBox; - this.scale = scale; - this.rotation = rotation; - this.offsetX = offsetX; - this.offsetY = offsetY; - - // creating transform to convert pdf coordinate system to the normal - // canvas like coordinates taking in account scale and rotation - var centerX = (viewBox[2] + viewBox[0]) / 2; - var centerY = (viewBox[3] + viewBox[1]) / 2; - var rotateA, rotateB, rotateC, rotateD; - rotation = rotation % 360; - rotation = rotation < 0 ? rotation + 360 : rotation; - switch (rotation) { - case 180: - rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1; - break; - case 90: - rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0; - break; - case 270: - rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0; - break; - //case 0: - default: - rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1; - break; - } - - if (dontFlip) { - rotateC = -rotateC; rotateD = -rotateD; - } - - var offsetCanvasX, offsetCanvasY; - var width, height; - if (rotateA === 0) { - offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX; - offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY; - width = Math.abs(viewBox[3] - viewBox[1]) * scale; - height = Math.abs(viewBox[2] - viewBox[0]) * scale; - } else { - offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX; - offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY; - width = Math.abs(viewBox[2] - viewBox[0]) * scale; - height = Math.abs(viewBox[3] - viewBox[1]) * scale; - } - // creating transform for the following operations: - // translate(-centerX, -centerY), rotate and flip vertically, - // scale, and translate(offsetCanvasX, offsetCanvasY) - this.transform = [ - rotateA * scale, - rotateB * scale, - rotateC * scale, - rotateD * scale, - offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, - offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY - ]; - - this.width = width; - this.height = height; - this.fontScale = scale; - } - PageViewport.prototype = /** @lends PDFJS.PageViewport.prototype */ { - /** - * Clones viewport with additional properties. - * @param args {Object} (optional) If specified, may contain the 'scale' or - * 'rotation' properties to override the corresponding properties in - * the cloned viewport. - * @returns {PDFJS.PageViewport} Cloned viewport. - */ - clone: function PageViewPort_clone(args) { - args = args || {}; - var scale = 'scale' in args ? args.scale : this.scale; - var rotation = 'rotation' in args ? args.rotation : this.rotation; - return new PageViewport(this.viewBox.slice(), scale, rotation, - this.offsetX, this.offsetY, args.dontFlip); - }, - /** - * Converts PDF point to the viewport coordinates. For examples, useful for - * converting PDF location into canvas pixel coordinates. - * @param x {number} X coordinate. - * @param y {number} Y coordinate. - * @returns {Object} Object that contains 'x' and 'y' properties of the - * point in the viewport coordinate space. - * @see {@link convertToPdfPoint} - * @see {@link convertToViewportRectangle} - */ - convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) { - return Util.applyTransform([x, y], this.transform); - }, - /** - * Converts PDF rectangle to the viewport coordinates. - * @param rect {Array} xMin, yMin, xMax and yMax coordinates. - * @returns {Array} Contains corresponding coordinates of the rectangle - * in the viewport coordinate space. - * @see {@link convertToViewportPoint} - */ - convertToViewportRectangle: - function PageViewport_convertToViewportRectangle(rect) { - var tl = Util.applyTransform([rect[0], rect[1]], this.transform); - var br = Util.applyTransform([rect[2], rect[3]], this.transform); - return [tl[0], tl[1], br[0], br[1]]; - }, - /** - * Converts viewport coordinates to the PDF location. For examples, useful - * for converting canvas pixel location into PDF one. - * @param x {number} X coordinate. - * @param y {number} Y coordinate. - * @returns {Object} Object that contains 'x' and 'y' properties of the - * point in the PDF coordinate space. - * @see {@link convertToViewportPoint} - */ - convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) { - return Util.applyInverseTransform([x, y], this.transform); - } - }; - return PageViewport; -})(); - -var PDFStringTranslateTable = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, - 0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C, - 0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160, - 0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC -]; - -function stringToPDFString(str) { - var i, n = str.length, strBuf = []; - if (str[0] === '\xFE' && str[1] === '\xFF') { - // UTF16BE BOM - for (i = 2; i < n; i += 2) { - strBuf.push(String.fromCharCode( - (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1))); - } - } else { - for (i = 0; i < n; ++i) { - var code = PDFStringTranslateTable[str.charCodeAt(i)]; - strBuf.push(code ? String.fromCharCode(code) : str.charAt(i)); - } - } - return strBuf.join(''); -} - -function stringToUTF8String(str) { - return decodeURIComponent(escape(str)); -} - -function utf8StringToString(str) { - return unescape(encodeURIComponent(str)); -} - -function isEmptyObj(obj) { - for (var key in obj) { - return false; - } - return true; -} - -function isBool(v) { - return typeof v === 'boolean'; -} - -function isInt(v) { - return typeof v === 'number' && ((v | 0) === v); -} - -function isNum(v) { - return typeof v === 'number'; -} - -function isString(v) { - return typeof v === 'string'; -} - -function isArray(v) { - return v instanceof Array; -} - -function isArrayBuffer(v) { - return typeof v === 'object' && v !== null && v.byteLength !== undefined; -} - -/** - * Promise Capability object. - * - * @typedef {Object} PromiseCapability - * @property {Promise} promise - A promise object. - * @property {function} resolve - Fullfills the promise. - * @property {function} reject - Rejects the promise. - */ - -/** - * Creates a promise capability object. - * @alias PDFJS.createPromiseCapability - * - * @return {PromiseCapability} A capability object contains: - * - a Promise, resolve and reject methods. - */ -function createPromiseCapability() { - var capability = {}; - capability.promise = new Promise(function (resolve, reject) { - capability.resolve = resolve; - capability.reject = reject; - }); - return capability; -} - -PDFJS.createPromiseCapability = createPromiseCapability; - -/** - * Polyfill for Promises: - * The following promise implementation tries to generally implement the - * Promise/A+ spec. Some notable differences from other promise libaries are: - * - There currently isn't a seperate deferred and promise object. - * - Unhandled rejections eventually show an error if they aren't handled. - * - * Based off of the work in: - * https://bugzilla.mozilla.org/show_bug.cgi?id=810490 - */ -(function PromiseClosure() { - if (globalScope.Promise) { - // Promises existing in the DOM/Worker, checking presence of all/resolve - if (typeof globalScope.Promise.all !== 'function') { - globalScope.Promise.all = function (iterable) { - var count = 0, results = [], resolve, reject; - var promise = new globalScope.Promise(function (resolve_, reject_) { - resolve = resolve_; - reject = reject_; - }); - iterable.forEach(function (p, i) { - count++; - p.then(function (result) { - results[i] = result; - count--; - if (count === 0) { - resolve(results); - } - }, reject); - }); - if (count === 0) { - resolve(results); - } - return promise; - }; - } - if (typeof globalScope.Promise.resolve !== 'function') { - globalScope.Promise.resolve = function (value) { - return new globalScope.Promise(function (resolve) { resolve(value); }); - }; - } - if (typeof globalScope.Promise.reject !== 'function') { - globalScope.Promise.reject = function (reason) { - return new globalScope.Promise(function (resolve, reject) { - reject(reason); - }); - }; - } - if (typeof globalScope.Promise.prototype.catch !== 'function') { - globalScope.Promise.prototype.catch = function (onReject) { - return globalScope.Promise.prototype.then(undefined, onReject); - }; - } - return; - } -//#if !MOZCENTRAL - var STATUS_PENDING = 0; - var STATUS_RESOLVED = 1; - var STATUS_REJECTED = 2; - - // In an attempt to avoid silent exceptions, unhandled rejections are - // tracked and if they aren't handled in a certain amount of time an - // error is logged. - var REJECTION_TIMEOUT = 500; - - var HandlerManager = { - handlers: [], - running: false, - unhandledRejections: [], - pendingRejectionCheck: false, - - scheduleHandlers: function scheduleHandlers(promise) { - if (promise._status === STATUS_PENDING) { - return; - } - - this.handlers = this.handlers.concat(promise._handlers); - promise._handlers = []; - - if (this.running) { - return; - } - this.running = true; - - setTimeout(this.runHandlers.bind(this), 0); - }, - - runHandlers: function runHandlers() { - var RUN_TIMEOUT = 1; // ms - var timeoutAt = Date.now() + RUN_TIMEOUT; - while (this.handlers.length > 0) { - var handler = this.handlers.shift(); - - var nextStatus = handler.thisPromise._status; - var nextValue = handler.thisPromise._value; - - try { - if (nextStatus === STATUS_RESOLVED) { - if (typeof handler.onResolve === 'function') { - nextValue = handler.onResolve(nextValue); - } - } else if (typeof handler.onReject === 'function') { - nextValue = handler.onReject(nextValue); - nextStatus = STATUS_RESOLVED; - - if (handler.thisPromise._unhandledRejection) { - this.removeUnhandeledRejection(handler.thisPromise); - } - } - } catch (ex) { - nextStatus = STATUS_REJECTED; - nextValue = ex; - } - - handler.nextPromise._updateStatus(nextStatus, nextValue); - if (Date.now() >= timeoutAt) { - break; - } - } - - if (this.handlers.length > 0) { - setTimeout(this.runHandlers.bind(this), 0); - return; - } - - this.running = false; - }, - - addUnhandledRejection: function addUnhandledRejection(promise) { - this.unhandledRejections.push({ - promise: promise, - time: Date.now() - }); - this.scheduleRejectionCheck(); - }, - - removeUnhandeledRejection: function removeUnhandeledRejection(promise) { - promise._unhandledRejection = false; - for (var i = 0; i < this.unhandledRejections.length; i++) { - if (this.unhandledRejections[i].promise === promise) { - this.unhandledRejections.splice(i); - i--; - } - } - }, - - scheduleRejectionCheck: function scheduleRejectionCheck() { - if (this.pendingRejectionCheck) { - return; - } - this.pendingRejectionCheck = true; - setTimeout(function rejectionCheck() { - this.pendingRejectionCheck = false; - var now = Date.now(); - for (var i = 0; i < this.unhandledRejections.length; i++) { - if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) { - var unhandled = this.unhandledRejections[i].promise._value; - var msg = 'Unhandled rejection: ' + unhandled; - if (unhandled.stack) { - msg += '\n' + unhandled.stack; - } - warn(msg); - this.unhandledRejections.splice(i); - i--; - } - } - if (this.unhandledRejections.length) { - this.scheduleRejectionCheck(); - } - }.bind(this), REJECTION_TIMEOUT); - } - }; - - function Promise(resolver) { - this._status = STATUS_PENDING; - this._handlers = []; - try { - resolver.call(this, this._resolve.bind(this), this._reject.bind(this)); - } catch (e) { - this._reject(e); - } - } - /** - * Builds a promise that is resolved when all the passed in promises are - * resolved. - * @param {array} array of data and/or promises to wait for. - * @return {Promise} New dependant promise. - */ - Promise.all = function Promise_all(promises) { - var resolveAll, rejectAll; - var deferred = new Promise(function (resolve, reject) { - resolveAll = resolve; - rejectAll = reject; - }); - var unresolved = promises.length; - var results = []; - if (unresolved === 0) { - resolveAll(results); - return deferred; - } - function reject(reason) { - if (deferred._status === STATUS_REJECTED) { - return; - } - results = []; - rejectAll(reason); - } - for (var i = 0, ii = promises.length; i < ii; ++i) { - var promise = promises[i]; - var resolve = (function(i) { - return function(value) { - if (deferred._status === STATUS_REJECTED) { - return; - } - results[i] = value; - unresolved--; - if (unresolved === 0) { - resolveAll(results); - } - }; - })(i); - if (Promise.isPromise(promise)) { - promise.then(resolve, reject); - } else { - resolve(promise); - } - } - return deferred; - }; - - /** - * Checks if the value is likely a promise (has a 'then' function). - * @return {boolean} true if value is thenable - */ - Promise.isPromise = function Promise_isPromise(value) { - return value && typeof value.then === 'function'; - }; - - /** - * Creates resolved promise - * @param value resolve value - * @returns {Promise} - */ - Promise.resolve = function Promise_resolve(value) { - return new Promise(function (resolve) { resolve(value); }); - }; - - /** - * Creates rejected promise - * @param reason rejection value - * @returns {Promise} - */ - Promise.reject = function Promise_reject(reason) { - return new Promise(function (resolve, reject) { reject(reason); }); - }; - - Promise.prototype = { - _status: null, - _value: null, - _handlers: null, - _unhandledRejection: null, - - _updateStatus: function Promise__updateStatus(status, value) { - if (this._status === STATUS_RESOLVED || - this._status === STATUS_REJECTED) { - return; - } - - if (status === STATUS_RESOLVED && - Promise.isPromise(value)) { - value.then(this._updateStatus.bind(this, STATUS_RESOLVED), - this._updateStatus.bind(this, STATUS_REJECTED)); - return; - } - - this._status = status; - this._value = value; - - if (status === STATUS_REJECTED && this._handlers.length === 0) { - this._unhandledRejection = true; - HandlerManager.addUnhandledRejection(this); - } - - HandlerManager.scheduleHandlers(this); - }, - - _resolve: function Promise_resolve(value) { - this._updateStatus(STATUS_RESOLVED, value); - }, - - _reject: function Promise_reject(reason) { - this._updateStatus(STATUS_REJECTED, reason); - }, - - then: function Promise_then(onResolve, onReject) { - var nextPromise = new Promise(function (resolve, reject) { - this.resolve = resolve; - this.reject = reject; - }); - this._handlers.push({ - thisPromise: this, - onResolve: onResolve, - onReject: onReject, - nextPromise: nextPromise - }); - HandlerManager.scheduleHandlers(this); - return nextPromise; - }, - - catch: function Promise_catch(onReject) { - return this.then(undefined, onReject); - } - }; - - globalScope.Promise = Promise; -//#else -//throw new Error('DOM Promise is not present'); -//#endif -})(); - -var StatTimer = (function StatTimerClosure() { - function rpad(str, pad, length) { - while (str.length < length) { - str += pad; - } - return str; - } - function StatTimer() { - this.started = {}; - this.times = []; - this.enabled = true; - } - StatTimer.prototype = { - time: function StatTimer_time(name) { - if (!this.enabled) { - return; - } - if (name in this.started) { - warn('Timer is already running for ' + name); - } - this.started[name] = Date.now(); - }, - timeEnd: function StatTimer_timeEnd(name) { - if (!this.enabled) { - return; - } - if (!(name in this.started)) { - warn('Timer has not been started for ' + name); - } - this.times.push({ - 'name': name, - 'start': this.started[name], - 'end': Date.now() - }); - // Remove timer from started so it can be called again. - delete this.started[name]; - }, - toString: function StatTimer_toString() { - var i, ii; - var times = this.times; - var out = ''; - // Find the longest name for padding purposes. - var longest = 0; - for (i = 0, ii = times.length; i < ii; ++i) { - var name = times[i]['name']; - if (name.length > longest) { - longest = name.length; - } - } - for (i = 0, ii = times.length; i < ii; ++i) { - var span = times[i]; - var duration = span.end - span.start; - out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n'; - } - return out; - } - }; - return StatTimer; -})(); - -PDFJS.createBlob = function createBlob(data, contentType) { - if (typeof Blob !== 'undefined') { - return new Blob([data], { type: contentType }); - } - // Blob builder is deprecated in FF14 and removed in FF18. - var bb = new MozBlobBuilder(); - bb.append(data); - return bb.getBlob(contentType); -}; - -PDFJS.createObjectURL = (function createObjectURLClosure() { - // Blob/createObjectURL is not available, falling back to data schema. - var digits = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; - - return function createObjectURL(data, contentType) { - if (!PDFJS.disableCreateObjectURL && - typeof URL !== 'undefined' && URL.createObjectURL) { - var blob = PDFJS.createBlob(data, contentType); - return URL.createObjectURL(blob); - } - - var buffer = 'data:' + contentType + ';base64,'; - for (var i = 0, ii = data.length; i < ii; i += 3) { - var b1 = data[i] & 0xFF; - var b2 = data[i + 1] & 0xFF; - var b3 = data[i + 2] & 0xFF; - var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4); - var d3 = i + 1 < ii ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64; - var d4 = i + 2 < ii ? (b3 & 0x3F) : 64; - buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4]; - } - return buffer; - }; -})(); - -function MessageHandler(sourceName, targetName, comObj) { - this.sourceName = sourceName; - this.targetName = targetName; - this.comObj = comObj; - this.callbackIndex = 1; - this.postMessageTransfers = true; - var callbacksCapabilities = this.callbacksCapabilities = {}; - var ah = this.actionHandler = {}; - - this._onComObjOnMessage = function messageHandlerComObjOnMessage(event) { - var data = event.data; - if (data.targetName !== this.sourceName) { - return; - } - if (data.isReply) { - var callbackId = data.callbackId; - if (data.callbackId in callbacksCapabilities) { - var callback = callbacksCapabilities[callbackId]; - delete callbacksCapabilities[callbackId]; - if ('error' in data) { - callback.reject(data.error); - } else { - callback.resolve(data.data); - } - } else { - error('Cannot resolve callback ' + callbackId); - } - } else if (data.action in ah) { - var action = ah[data.action]; - if (data.callbackId) { - var sourceName = this.sourceName; - var targetName = data.sourceName; - Promise.resolve().then(function () { - return action[0].call(action[1], data.data); - }).then(function (result) { - comObj.postMessage({ - sourceName: sourceName, - targetName: targetName, - isReply: true, - callbackId: data.callbackId, - data: result - }); - }, function (reason) { - if (reason instanceof Error) { - // Serialize error to avoid "DataCloneError" - reason = reason + ''; - } - comObj.postMessage({ - sourceName: sourceName, - targetName: targetName, - isReply: true, - callbackId: data.callbackId, - error: reason - }); - }); - } else { - action[0].call(action[1], data.data); - } - } else { - error('Unknown action from worker: ' + data.action); - } - }.bind(this); - comObj.addEventListener('message', this._onComObjOnMessage); -} - -MessageHandler.prototype = { - on: function messageHandlerOn(actionName, handler, scope) { - var ah = this.actionHandler; - if (ah[actionName]) { - error('There is already an actionName called "' + actionName + '"'); - } - ah[actionName] = [handler, scope]; - }, - /** - * Sends a message to the comObj to invoke the action with the supplied data. - * @param {String} actionName Action to call. - * @param {JSON} data JSON data to send. - * @param {Array} [transfers] Optional list of transfers/ArrayBuffers - */ - send: function messageHandlerSend(actionName, data, transfers) { - var message = { - sourceName: this.sourceName, - targetName: this.targetName, - action: actionName, - data: data - }; - this.postMessage(message, transfers); - }, - /** - * Sends a message to the comObj to invoke the action with the supplied data. - * Expects that other side will callback with the response. - * @param {String} actionName Action to call. - * @param {JSON} data JSON data to send. - * @param {Array} [transfers] Optional list of transfers/ArrayBuffers. - * @returns {Promise} Promise to be resolved with response data. - */ - sendWithPromise: - function messageHandlerSendWithPromise(actionName, data, transfers) { - var callbackId = this.callbackIndex++; - var message = { - sourceName: this.sourceName, - targetName: this.targetName, - action: actionName, - data: data, - callbackId: callbackId - }; - var capability = createPromiseCapability(); - this.callbacksCapabilities[callbackId] = capability; - try { - this.postMessage(message, transfers); - } catch (e) { - capability.reject(e); - } - return capability.promise; - }, - /** - * Sends raw message to the comObj. - * @private - * @param message {Object} Raw message. - * @param transfers List of transfers/ArrayBuffers, or undefined. - */ - postMessage: function (message, transfers) { - if (transfers && this.postMessageTransfers) { - this.comObj.postMessage(message, transfers); - } else { - this.comObj.postMessage(message); - } - }, - - destroy: function () { - this.comObj.removeEventListener('message', this._onComObjOnMessage); - } -}; - -function loadJpegStream(id, imageUrl, objs) { - var img = new Image(); - img.onload = (function loadJpegStream_onloadClosure() { - objs.resolve(id, img); - }); - img.onerror = (function loadJpegStream_onerrorClosure() { - objs.resolve(id, null); - warn('Error during JPEG image loading'); - }); - img.src = imageUrl; -} - -exports.FONT_IDENTITY_MATRIX = FONT_IDENTITY_MATRIX; -exports.IDENTITY_MATRIX = IDENTITY_MATRIX; -exports.OPS = OPS; -exports.UNSUPPORTED_FEATURES = UNSUPPORTED_FEATURES; -exports.AnnotationBorderStyleType = AnnotationBorderStyleType; -exports.AnnotationFlag = AnnotationFlag; -exports.AnnotationType = AnnotationType; -exports.FontType = FontType; -exports.ImageKind = ImageKind; -exports.InvalidPDFException = InvalidPDFException; -exports.LinkTarget = LinkTarget; -exports.LinkTargetStringMap = LinkTargetStringMap; -exports.MessageHandler = MessageHandler; -exports.MissingDataException = MissingDataException; -exports.MissingPDFException = MissingPDFException; -exports.NotImplementedException = NotImplementedException; -exports.PasswordException = PasswordException; -exports.PasswordResponses = PasswordResponses; -exports.StatTimer = StatTimer; -exports.StreamType = StreamType; -exports.TextRenderingMode = TextRenderingMode; -exports.UnexpectedResponseException = UnexpectedResponseException; -exports.UnknownErrorException = UnknownErrorException; -exports.Util = Util; -exports.XRefParseException = XRefParseException; -exports.assert = assert; -exports.bytesToString = bytesToString; -exports.combineUrl = combineUrl; -exports.createPromiseCapability = createPromiseCapability; -exports.deprecated = deprecated; -exports.error = error; -exports.info = info; -exports.isArray = isArray; -exports.isArrayBuffer = isArrayBuffer; -exports.isBool = isBool; -exports.isEmptyObj = isEmptyObj; -exports.isExternalLinkTargetSet = isExternalLinkTargetSet; -exports.isInt = isInt; -exports.isNum = isNum; -exports.isString = isString; -exports.isValidUrl = isValidUrl; -exports.loadJpegStream = loadJpegStream; -exports.log2 = log2; -exports.readInt8 = readInt8; -exports.readUint16 = readUint16; -exports.readUint32 = readUint32; -exports.shadow = shadow; -exports.string32 = string32; -exports.stringToBytes = stringToBytes; -exports.stringToPDFString = stringToPDFString; -exports.stringToUTF8String = stringToUTF8String; -exports.utf8StringToString = utf8StringToString; -exports.warn = warn; -})); - -/* Copyright 2012 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* globals NetworkManager */ - -'use strict'; - -(function (root, factory) { - //if (typeof define === 'function' && define.amd) { - // define('pdfjs/core/chunked_stream', ['exports', 'pdfjs/shared/util'], - // factory); - // } else if (typeof exports !== 'undefined') { - // factory(exports, require('../shared/util.js')); - //} else { - factory((root.pdfjsCoreChunkedStream = {}), root.pdfjsSharedUtil); - //} -}(window, function (exports, sharedUtil) { - -var MissingDataException = sharedUtil.MissingDataException; -var assert = sharedUtil.assert; -var createPromiseCapability = sharedUtil.createPromiseCapability; -var isInt = sharedUtil.isInt; -var isEmptyObj = sharedUtil.isEmptyObj; - -var ChunkedStream = (function ChunkedStreamClosure() { - function ChunkedStream(length, chunkSize, manager) { - this.bytes = new Uint8Array(length); - this.start = 0; - this.pos = 0; - this.end = length; - this.chunkSize = chunkSize; - this.loadedChunks = []; - this.numChunksLoaded = 0; - this.numChunks = Math.ceil(length / chunkSize); - this.manager = manager; - this.progressiveDataLength = 0; - this.lastSuccessfulEnsureByteChunk = -1; // a single-entry cache - } - - // required methods for a stream. if a particular stream does not - // implement these, an error should be thrown - ChunkedStream.prototype = { - - getMissingChunks: function ChunkedStream_getMissingChunks() { - var chunks = []; - for (var chunk = 0, n = this.numChunks; chunk < n; ++chunk) { - if (!this.loadedChunks[chunk]) { - chunks.push(chunk); - } - } - return chunks; - }, - - getBaseStreams: function ChunkedStream_getBaseStreams() { - return [this]; - }, - - allChunksLoaded: function ChunkedStream_allChunksLoaded() { - return this.numChunksLoaded === this.numChunks; - }, - - onReceiveData: function ChunkedStream_onReceiveData(begin, chunk) { - var end = begin + chunk.byteLength; - - assert(begin % this.chunkSize === 0, 'Bad begin offset: ' + begin); - // Using this.length is inaccurate here since this.start can be moved - // See ChunkedStream.moveStart() - var length = this.bytes.length; - assert(end % this.chunkSize === 0 || end === length, - 'Bad end offset: ' + end); - - this.bytes.set(new Uint8Array(chunk), begin); - var chunkSize = this.chunkSize; - var beginChunk = Math.floor(begin / chunkSize); - var endChunk = Math.floor((end - 1) / chunkSize) + 1; - var curChunk; - - for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) { - if (!this.loadedChunks[curChunk]) { - this.loadedChunks[curChunk] = true; - ++this.numChunksLoaded; - } - } - }, - - onReceiveProgressiveData: - function ChunkedStream_onReceiveProgressiveData(data) { - var position = this.progressiveDataLength; - var beginChunk = Math.floor(position / this.chunkSize); - - this.bytes.set(new Uint8Array(data), position); - position += data.byteLength; - this.progressiveDataLength = position; - var endChunk = position >= this.end ? this.numChunks : - Math.floor(position / this.chunkSize); - var curChunk; - for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) { - if (!this.loadedChunks[curChunk]) { - this.loadedChunks[curChunk] = true; - ++this.numChunksLoaded; - } - } - }, - - ensureByte: function ChunkedStream_ensureByte(pos) { - var chunk = Math.floor(pos / this.chunkSize); - if (chunk === this.lastSuccessfulEnsureByteChunk) { - return; - } - - if (!this.loadedChunks[chunk]) { - throw new MissingDataException(pos, pos + 1); - } - this.lastSuccessfulEnsureByteChunk = chunk; - }, - - ensureRange: function ChunkedStream_ensureRange(begin, end) { - if (begin >= end) { - return; - } - - if (end <= this.progressiveDataLength) { - return; - } - - var chunkSize = this.chunkSize; - var beginChunk = Math.floor(begin / chunkSize); - var endChunk = Math.floor((end - 1) / chunkSize) + 1; - for (var chunk = beginChunk; chunk < endChunk; ++chunk) { - if (!this.loadedChunks[chunk]) { - throw new MissingDataException(begin, end); - } - } - }, - - nextEmptyChunk: function ChunkedStream_nextEmptyChunk(beginChunk) { - var chunk, numChunks = this.numChunks; - for (var i = 0; i < numChunks; ++i) { - chunk = (beginChunk + i) % numChunks; // Wrap around to beginning - if (!this.loadedChunks[chunk]) { - return chunk; - } - } - return null; - }, - - hasChunk: function ChunkedStream_hasChunk(chunk) { - return !!this.loadedChunks[chunk]; - }, - - get length() { - return this.end - this.start; - }, - - get isEmpty() { - return this.length === 0; - }, - - getByte: function ChunkedStream_getByte() { - var pos = this.pos; - if (pos >= this.end) { - return -1; - } - this.ensureByte(pos); - return this.bytes[this.pos++]; - }, - - getUint16: function ChunkedStream_getUint16() { - var b0 = this.getByte(); - var b1 = this.getByte(); - if (b0 === -1 || b1 === -1) { - return -1; - } - return (b0 << 8) + b1; - }, - - getInt32: function ChunkedStream_getInt32() { - var b0 = this.getByte(); - var b1 = this.getByte(); - var b2 = this.getByte(); - var b3 = this.getByte(); - return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3; - }, - - // returns subarray of original buffer - // should only be read - getBytes: function ChunkedStream_getBytes(length) { - var bytes = this.bytes; - var pos = this.pos; - var strEnd = this.end; - - if (!length) { - this.ensureRange(pos, strEnd); - return bytes.subarray(pos, strEnd); - } - - var end = pos + length; - if (end > strEnd) { - end = strEnd; - } - this.ensureRange(pos, end); - - this.pos = end; - return bytes.subarray(pos, end); - }, - - peekByte: function ChunkedStream_peekByte() { - var peekedByte = this.getByte(); - this.pos--; - return peekedByte; - }, - - peekBytes: function ChunkedStream_peekBytes(length) { - var bytes = this.getBytes(length); - this.pos -= bytes.length; - return bytes; - }, - - getByteRange: function ChunkedStream_getBytes(begin, end) { - this.ensureRange(begin, end); - return this.bytes.subarray(begin, end); - }, - - skip: function ChunkedStream_skip(n) { - if (!n) { - n = 1; - } - this.pos += n; - }, - - reset: function ChunkedStream_reset() { - this.pos = this.start; - }, - - moveStart: function ChunkedStream_moveStart() { - this.start = this.pos; - }, - - makeSubStream: function ChunkedStream_makeSubStream(start, length, dict) { - this.ensureRange(start, start + length); - - function ChunkedStreamSubstream() {} - ChunkedStreamSubstream.prototype = Object.create(this); - ChunkedStreamSubstream.prototype.getMissingChunks = function() { - var chunkSize = this.chunkSize; - var beginChunk = Math.floor(this.start / chunkSize); - var endChunk = Math.floor((this.end - 1) / chunkSize) + 1; - var missingChunks = []; - for (var chunk = beginChunk; chunk < endChunk; ++chunk) { - if (!this.loadedChunks[chunk]) { - missingChunks.push(chunk); - } - } - return missingChunks; - }; - var subStream = new ChunkedStreamSubstream(); - subStream.pos = subStream.start = start; - subStream.end = start + length || this.end; - subStream.dict = dict; - return subStream; - }, - - isStream: true - }; - - return ChunkedStream; -})(); - -var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { - - function ChunkedStreamManager(length, chunkSize, url, args) { - this.stream = new ChunkedStream(length, chunkSize, this); - this.length = length; - this.chunkSize = chunkSize; - this.url = url; - this.disableAutoFetch = args.disableAutoFetch; - var msgHandler = this.msgHandler = args.msgHandler; - - if (args.chunkedViewerLoading) { - msgHandler.on('OnDataRange', this.onReceiveData.bind(this)); - msgHandler.on('OnDataProgress', this.onProgress.bind(this)); - this.sendRequest = function ChunkedStreamManager_sendRequest(begin, end) { - msgHandler.send('RequestDataRange', { begin: begin, end: end }); - }; - } else { - - var getXhr = function getXhr() { - return new XMLHttpRequest(); - }; - this.networkManager = new NetworkManager(this.url, { - getXhr: getXhr, - httpHeaders: args.httpHeaders, - withCredentials: args.withCredentials - }); - this.sendRequest = function ChunkedStreamManager_sendRequest(begin, end) { - this.networkManager.requestRange(begin, end, { - onDone: this.onReceiveData.bind(this), - onProgress: this.onProgress.bind(this) - }); - }; - } - - this.currRequestId = 0; - - this.chunksNeededByRequest = {}; - this.requestsByChunk = {}; - this.promisesByRequest = {}; - this.progressiveDataLength = 0; - - this._loadedStreamCapability = createPromiseCapability(); - - if (args.initialData) { - this.onReceiveData({chunk: args.initialData}); - } - } - - ChunkedStreamManager.prototype = { - onLoadedStream: function ChunkedStreamManager_getLoadedStream() { - return this._loadedStreamCapability.promise; - }, - - // Get all the chunks that are not yet loaded and groups them into - // contiguous ranges to load in as few requests as possible - requestAllChunks: function ChunkedStreamManager_requestAllChunks() { - var missingChunks = this.stream.getMissingChunks(); - this._requestChunks(missingChunks); - return this._loadedStreamCapability.promise; - }, - - _requestChunks: function ChunkedStreamManager_requestChunks(chunks) { - var requestId = this.currRequestId++; - - var chunksNeeded; - var i, ii; - this.chunksNeededByRequest[requestId] = chunksNeeded = {}; - for (i = 0, ii = chunks.length; i < ii; i++) { - if (!this.stream.hasChunk(chunks[i])) { - chunksNeeded[chunks[i]] = true; - } - } - - if (isEmptyObj(chunksNeeded)) { - return Promise.resolve(); - } - - var capability = createPromiseCapability(); - this.promisesByRequest[requestId] = capability; - - var chunksToRequest = []; - for (var chunk in chunksNeeded) { - chunk = chunk | 0; - if (!(chunk in this.requestsByChunk)) { - this.requestsByChunk[chunk] = []; - chunksToRequest.push(chunk); - } - this.requestsByChunk[chunk].push(requestId); - } - - if (!chunksToRequest.length) { - return capability.promise; - } - - var groupedChunksToRequest = this.groupChunks(chunksToRequest); - - for (i = 0; i < groupedChunksToRequest.length; ++i) { - var groupedChunk = groupedChunksToRequest[i]; - var begin = groupedChunk.beginChunk * this.chunkSize; - var end = Math.min(groupedChunk.endChunk * this.chunkSize, this.length); - this.sendRequest(begin, end); - } - - return capability.promise; - }, - - getStream: function ChunkedStreamManager_getStream() { - return this.stream; - }, - - // Loads any chunks in the requested range that are not yet loaded - requestRange: function ChunkedStreamManager_requestRange(begin, end) { - - end = Math.min(end, this.length); - - var beginChunk = this.getBeginChunk(begin); - var endChunk = this.getEndChunk(end); - - var chunks = []; - for (var chunk = beginChunk; chunk < endChunk; ++chunk) { - chunks.push(chunk); - } - - return this._requestChunks(chunks); - }, - - requestRanges: function ChunkedStreamManager_requestRanges(ranges) { - ranges = ranges || []; - var chunksToRequest = []; - - for (var i = 0; i < ranges.length; i++) { - var beginChunk = this.getBeginChunk(ranges[i].begin); - var endChunk = this.getEndChunk(ranges[i].end); - for (var chunk = beginChunk; chunk < endChunk; ++chunk) { - if (chunksToRequest.indexOf(chunk) < 0) { - chunksToRequest.push(chunk); - } - } - } - - chunksToRequest.sort(function(a, b) { return a - b; }); - return this._requestChunks(chunksToRequest); - }, - - // Groups a sorted array of chunks into as few contiguous larger - // chunks as possible - groupChunks: function ChunkedStreamManager_groupChunks(chunks) { - var groupedChunks = []; - var beginChunk = -1; - var prevChunk = -1; - for (var i = 0; i < chunks.length; ++i) { - var chunk = chunks[i]; - - if (beginChunk < 0) { - beginChunk = chunk; - } - - if (prevChunk >= 0 && prevChunk + 1 !== chunk) { - groupedChunks.push({ beginChunk: beginChunk, - endChunk: prevChunk + 1 }); - beginChunk = chunk; - } - if (i + 1 === chunks.length) { - groupedChunks.push({ beginChunk: beginChunk, - endChunk: chunk + 1 }); - } - - prevChunk = chunk; - } - return groupedChunks; - }, - - onProgress: function ChunkedStreamManager_onProgress(args) { - var bytesLoaded = (this.stream.numChunksLoaded * this.chunkSize + - args.loaded); - this.msgHandler.send('DocProgress', { - loaded: bytesLoaded, - total: this.length - }); - }, - - onReceiveData: function ChunkedStreamManager_onReceiveData(args) { - var chunk = args.chunk; - var isProgressive = args.begin === undefined; - var begin = isProgressive ? this.progressiveDataLength : args.begin; - var end = begin + chunk.byteLength; - - var beginChunk = Math.floor(begin / this.chunkSize); - var endChunk = end < this.length ? Math.floor(end / this.chunkSize) : - Math.ceil(end / this.chunkSize); - - if (isProgressive) { - this.stream.onReceiveProgressiveData(chunk); - this.progressiveDataLength = end; - } else { - this.stream.onReceiveData(begin, chunk); - } - - if (this.stream.allChunksLoaded()) { - this._loadedStreamCapability.resolve(this.stream); - } - - var loadedRequests = []; - var i, requestId; - for (chunk = beginChunk; chunk < endChunk; ++chunk) { - // The server might return more chunks than requested - var requestIds = this.requestsByChunk[chunk] || []; - delete this.requestsByChunk[chunk]; - - for (i = 0; i < requestIds.length; ++i) { - requestId = requestIds[i]; - var chunksNeeded = this.chunksNeededByRequest[requestId]; - if (chunk in chunksNeeded) { - delete chunksNeeded[chunk]; - } - - if (!isEmptyObj(chunksNeeded)) { - continue; - } - - loadedRequests.push(requestId); - } - } - - // If there are no pending requests, automatically fetch the next - // unfetched chunk of the PDF - if (!this.disableAutoFetch && isEmptyObj(this.requestsByChunk)) { - var nextEmptyChunk; - if (this.stream.numChunksLoaded === 1) { - // This is a special optimization so that after fetching the first - // chunk, rather than fetching the second chunk, we fetch the last - // chunk. - var lastChunk = this.stream.numChunks - 1; - if (!this.stream.hasChunk(lastChunk)) { - nextEmptyChunk = lastChunk; - } - } else { - nextEmptyChunk = this.stream.nextEmptyChunk(endChunk); - } - if (isInt(nextEmptyChunk)) { - this._requestChunks([nextEmptyChunk]); - } - } - - for (i = 0; i < loadedRequests.length; ++i) { - requestId = loadedRequests[i]; - var capability = this.promisesByRequest[requestId]; - delete this.promisesByRequest[requestId]; - capability.resolve(); - } - - this.msgHandler.send('DocProgress', { - loaded: this.stream.numChunksLoaded * this.chunkSize, - total: this.length - }); - }, - - onError: function ChunkedStreamManager_onError(err) { - this._loadedStreamCapability.reject(err); - }, - - getBeginChunk: function ChunkedStreamManager_getBeginChunk(begin) { - var chunk = Math.floor(begin / this.chunkSize); - return chunk; - }, - - getEndChunk: function ChunkedStreamManager_getEndChunk(end) { - var chunk = Math.floor((end - 1) / this.chunkSize) + 1; - return chunk; - }, - - abort: function ChunkedStreamManager_abort() { - if (this.networkManager) { - this.networkManager.abortAllRequests(); - } - for(var requestId in this.promisesByRequest) { - var capability = this.promisesByRequest[requestId]; - capability.reject(new Error('Request was aborted')); - } - } - }; - - return ChunkedStreamManager; -})(); - -exports.ChunkedStream = ChunkedStream; -exports.ChunkedStreamManager = ChunkedStreamManager; -})); - -/* Copyright 2012 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* uses XRef */ - -'use strict'; - -(function (root, factory) { - //if (typeof define === 'function' && define.amd) { - // define('pdfjs/core/primitives', ['exports', 'pdfjs/shared/util'], factory); - // } else if (typeof exports !== 'undefined') { - // factory(exports, require('../shared/util.js')); - //} else { - factory((root.pdfjsCorePrimitives = {}), root.pdfjsSharedUtil); - //} -}(window, function (exports, sharedUtil) { - -var isArray = sharedUtil.isArray; - -var Name = (function NameClosure() { - function Name(name) { - this.name = name; - } - - Name.prototype = {}; - - var nameCache = {}; - - Name.get = function Name_get(name) { - var nameValue = nameCache[name]; - return (nameValue ? nameValue : (nameCache[name] = new Name(name))); - }; - - return Name; -})(); - -var Cmd = (function CmdClosure() { - function Cmd(cmd) { - this.cmd = cmd; - } - - Cmd.prototype = {}; - - var cmdCache = {}; - - Cmd.get = function Cmd_get(cmd) { - var cmdValue = cmdCache[cmd]; - return (cmdValue ? cmdValue : (cmdCache[cmd] = new Cmd(cmd))); - }; - - return Cmd; -})(); - -var Dict = (function DictClosure() { - var nonSerializable = function nonSerializableClosure() { - return nonSerializable; // creating closure on some variable - }; - - var GETALL_DICTIONARY_TYPES_WHITELIST = { - 'Background': true, - 'ExtGState': true, - 'Halftone': true, - 'Layout': true, - 'Mask': true, - 'Pagination': true, - 'Printing': true - }; - - function isRecursionAllowedFor(dict) { - if (!isName(dict.Type)) { - return true; - } - var dictType = dict.Type.name; - return GETALL_DICTIONARY_TYPES_WHITELIST[dictType] === true; - } - - // xref is optional - function Dict(xref) { - // Map should only be used internally, use functions below to access. - this.map = Object.create(null); - this.xref = xref; - this.objId = null; - this.__nonSerializable__ = nonSerializable; // disable cloning of the Dict - } - - Dict.prototype = { - assignXref: function Dict_assignXref(newXref) { - this.xref = newXref; - }, - - // automatically dereferences Ref objects - get: function Dict_get(key1, key2, key3) { - var value; - var xref = this.xref; - if (typeof (value = this.map[key1]) !== 'undefined' || key1 in this.map || - typeof key2 === 'undefined') { - return xref ? xref.fetchIfRef(value) : value; - } - if (typeof (value = this.map[key2]) !== 'undefined' || key2 in this.map || - typeof key3 === 'undefined') { - return xref ? xref.fetchIfRef(value) : value; - } - value = this.map[key3] || null; - return xref ? xref.fetchIfRef(value) : value; - }, - - // Same as get(), but returns a promise and uses fetchIfRefAsync(). - getAsync: function Dict_getAsync(key1, key2, key3) { - var value; - var xref = this.xref; - if (typeof (value = this.map[key1]) !== 'undefined' || key1 in this.map || - typeof key2 === 'undefined') { - if (xref) { - return xref.fetchIfRefAsync(value); - } - return Promise.resolve(value); - } - if (typeof (value = this.map[key2]) !== 'undefined' || key2 in this.map || - typeof key3 === 'undefined') { - if (xref) { - return xref.fetchIfRefAsync(value); - } - return Promise.resolve(value); - } - value = this.map[key3] || null; - if (xref) { - return xref.fetchIfRefAsync(value); - } - return Promise.resolve(value); - }, - - // Same as get(), but dereferences all elements if the result is an Array. - getArray: function Dict_getArray(key1, key2, key3) { - var value = this.get(key1, key2, key3); - var xref = this.xref; - if (!isArray(value) || !xref) { - return value; - } - value = value.slice(); // Ensure that we don't modify the Dict data. - for (var i = 0, ii = value.length; i < ii; i++) { - if (!isRef(value[i])) { - continue; - } - value[i] = xref.fetch(value[i]); - } - return value; - }, - - // no dereferencing - getRaw: function Dict_getRaw(key) { - return this.map[key]; - }, - - // creates new map and dereferences all Refs - getAll: function Dict_getAll() { - var all = Object.create(null); - var queue = null; - var key, obj; - for (key in this.map) { - obj = this.get(key); - if (obj instanceof Dict) { - if (isRecursionAllowedFor(obj)) { - (queue || (queue = [])).push({target: all, key: key, obj: obj}); - } else { - all[key] = this.getRaw(key); - } - } else { - all[key] = obj; - } - } - if (!queue) { - return all; - } - - // trying to take cyclic references into the account - var processed = Object.create(null); - while (queue.length > 0) { - var item = queue.shift(); - var itemObj = item.obj; - var objId = itemObj.objId; - if (objId && objId in processed) { - item.target[item.key] = processed[objId]; - continue; - } - var dereferenced = Object.create(null); - for (key in itemObj.map) { - obj = itemObj.get(key); - if (obj instanceof Dict) { - if (isRecursionAllowedFor(obj)) { - queue.push({target: dereferenced, key: key, obj: obj}); - } else { - dereferenced[key] = itemObj.getRaw(key); - } - } else { - dereferenced[key] = obj; - } - } - if (objId) { - processed[objId] = dereferenced; - } - item.target[item.key] = dereferenced; - } - return all; - }, - - getKeys: function Dict_getKeys() { - return Object.keys(this.map); - }, - - set: function Dict_set(key, value) { - this.map[key] = value; - }, - - has: function Dict_has(key) { - return key in this.map; - }, - - forEach: function Dict_forEach(callback) { - for (var key in this.map) { - callback(key, this.get(key)); - } - } - }; - - Dict.empty = new Dict(null); - - Dict.merge = function Dict_merge(xref, dictArray) { - var mergedDict = new Dict(xref); - - for (var i = 0, ii = dictArray.length; i < ii; i++) { - var dict = dictArray[i]; - if (!isDict(dict)) { - continue; - } - for (var keyName in dict.map) { - if (mergedDict.map[keyName]) { - continue; - } - mergedDict.map[keyName] = dict.map[keyName]; - } - } - return mergedDict; - }; - - return Dict; -})(); - -var Ref = (function RefClosure() { - function Ref(num, gen) { - this.num = num; - this.gen = gen; - } - - Ref.prototype = { - toString: function Ref_toString() { - // This function is hot, so we make the string as compact as possible. - // |this.gen| is almost always zero, so we treat that case specially. - var str = this.num + 'R'; - if (this.gen !== 0) { - str += this.gen; - } - return str; - } - }; - - return Ref; -})(); - -// The reference is identified by number and generation. -// This structure stores only one instance of the reference. -var RefSet = (function RefSetClosure() { - function RefSet() { - this.dict = {}; - } - - RefSet.prototype = { - has: function RefSet_has(ref) { - return ref.toString() in this.dict; - }, - - put: function RefSet_put(ref) { - this.dict[ref.toString()] = true; - }, - - remove: function RefSet_remove(ref) { - delete this.dict[ref.toString()]; - } - }; - - return RefSet; -})(); - -var RefSetCache = (function RefSetCacheClosure() { - function RefSetCache() { - this.dict = Object.create(null); - } - - RefSetCache.prototype = { - get: function RefSetCache_get(ref) { - return this.dict[ref.toString()]; - }, - - has: function RefSetCache_has(ref) { - return ref.toString() in this.dict; - }, - - put: function RefSetCache_put(ref, obj) { - this.dict[ref.toString()] = obj; - }, - - putAlias: function RefSetCache_putAlias(ref, aliasRef) { - this.dict[ref.toString()] = this.get(aliasRef); - }, - - forEach: function RefSetCache_forEach(fn, thisArg) { - for (var i in this.dict) { - fn.call(thisArg, this.dict[i]); - } - }, - - clear: function RefSetCache_clear() { - this.dict = Object.create(null); - } - }; - - return RefSetCache; -})(); - -function isName(v) { - return v instanceof Name; -} - -function isCmd(v, cmd) { - return v instanceof Cmd && (cmd === undefined || v.cmd === cmd); -} - -function isDict(v, type) { - if (!(v instanceof Dict)) { - return false; - } - if (!type) { - return true; - } - var dictType = v.get('Type'); - return isName(dictType) && dictType.name === type; -} - -function isRef(v) { - return v instanceof Ref; -} - -function isStream(v) { - return typeof v === 'object' && v !== null && v.getBytes !== undefined; -} - -exports.Cmd = Cmd; -exports.Dict = Dict; -exports.Name = Name; -exports.Ref = Ref; -exports.RefSet = RefSet; -exports.RefSetCache = RefSetCache; -exports.isCmd = isCmd; -exports.isDict = isDict; -exports.isName = isName; -exports.isRef = isRef; -exports.isStream = isStream; -})); - -/* Copyright 2012 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* globals PDFJS */ - -'use strict'; - -(function (root, factory) { - //if (typeof define === 'function' && define.amd) { - // define('pdfjs/core/stream', ['exports', 'pdfjs/shared/util', - // 'pdfjs/core/primitives'], factory); - // } else if (typeof exports !== 'undefined') { - // factory(exports, require('../shared/util.js'), require('./primitives.js')); - //} else { - factory((root.pdfjsCoreStream = {}), root.pdfjsSharedUtil, - root.pdfjsCorePrimitives); - //} -}(window, function (exports, sharedUtil, corePrimitives) { - -var Util = sharedUtil.Util; -var error = sharedUtil.error; -var info = sharedUtil.info; -var isArray = sharedUtil.isArray; -var shadow = sharedUtil.shadow; -var warn = sharedUtil.warn; -var Dict = corePrimitives.Dict; - -var coreParser; // see _setCoreParser below -var EOF; // = coreParser.EOF; -var Lexer; // = coreParser.Lexer; - -var coreColorSpace; // see _setCoreColorSpace below -var ColorSpace; // = coreColorSpace.ColorSpace; - -var Stream = (function StreamClosure() { - function Stream(arrayBuffer, start, length, dict) { - this.bytes = (arrayBuffer instanceof Uint8Array ? - arrayBuffer : new Uint8Array(arrayBuffer)); - this.start = start || 0; - this.pos = this.start; - this.end = (start + length) || this.bytes.length; - this.dict = dict; - } - - // required methods for a stream. if a particular stream does not - // implement these, an error should be thrown - Stream.prototype = { - get length() { - return this.end - this.start; - }, - get isEmpty() { - return this.length === 0; - }, - getByte: function Stream_getByte() { - if (this.pos >= this.end) { - return -1; - } - return this.bytes[this.pos++]; - }, - getUint16: function Stream_getUint16() { - var b0 = this.getByte(); - var b1 = this.getByte(); - if (b0 === -1 || b1 === -1) { - return -1; - } - return (b0 << 8) + b1; - }, - getInt32: function Stream_getInt32() { - var b0 = this.getByte(); - var b1 = this.getByte(); - var b2 = this.getByte(); - var b3 = this.getByte(); - return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3; - }, - // returns subarray of original buffer - // should only be read - getBytes: function Stream_getBytes(length) { - var bytes = this.bytes; - var pos = this.pos; - var strEnd = this.end; - - if (!length) { - return bytes.subarray(pos, strEnd); - } - var end = pos + length; - if (end > strEnd) { - end = strEnd; - } - this.pos = end; - return bytes.subarray(pos, end); - }, - peekByte: function Stream_peekByte() { - var peekedByte = this.getByte(); - this.pos--; - return peekedByte; - }, - peekBytes: function Stream_peekBytes(length) { - var bytes = this.getBytes(length); - this.pos -= bytes.length; - return bytes; - }, - skip: function Stream_skip(n) { - if (!n) { - n = 1; - } - this.pos += n; - }, - reset: function Stream_reset() { - this.pos = this.start; - }, - moveStart: function Stream_moveStart() { - this.start = this.pos; - }, - makeSubStream: function Stream_makeSubStream(start, length, dict) { - return new Stream(this.bytes.buffer, start, length, dict); - }, - isStream: true - }; - - return Stream; -})(); - -var StringStream = (function StringStreamClosure() { - function StringStream(str) { - var length = str.length; - var bytes = new Uint8Array(length); - for (var n = 0; n < length; ++n) { - bytes[n] = str.charCodeAt(n); - } - Stream.call(this, bytes); - } - - StringStream.prototype = Stream.prototype; - - return StringStream; -})(); - -// super class for the decoding streams -var DecodeStream = (function DecodeStreamClosure() { - // Lots of DecodeStreams are created whose buffers are never used. For these - // we share a single empty buffer. This is (a) space-efficient and (b) avoids - // having special cases that would be required if we used |null| for an empty - // buffer. - var emptyBuffer = new Uint8Array(0); - - function DecodeStream(maybeMinBufferLength) { - this.pos = 0; - this.bufferLength = 0; - this.eof = false; - this.buffer = emptyBuffer; - this.minBufferLength = 512; - if (maybeMinBufferLength) { - // Compute the first power of two that is as big as maybeMinBufferLength. - while (this.minBufferLength < maybeMinBufferLength) { - this.minBufferLength *= 2; - } - } - } - - DecodeStream.prototype = { - get isEmpty() { - while (!this.eof && this.bufferLength === 0) { - this.readBlock(); - } - return this.bufferLength === 0; - }, - ensureBuffer: function DecodeStream_ensureBuffer(requested) { - var buffer = this.buffer; - if (requested <= buffer.byteLength) { - return buffer; - } - var size = this.minBufferLength; - while (size < requested) { - size *= 2; - } - var buffer2 = new Uint8Array(size); - buffer2.set(buffer); - return (this.buffer = buffer2); - }, - getByte: function DecodeStream_getByte() { - var pos = this.pos; - while (this.bufferLength <= pos) { - if (this.eof) { - return -1; - } - this.readBlock(); - } - return this.buffer[this.pos++]; - }, - getUint16: function DecodeStream_getUint16() { - var b0 = this.getByte(); - var b1 = this.getByte(); - if (b0 === -1 || b1 === -1) { - return -1; - } - return (b0 << 8) + b1; - }, - getInt32: function DecodeStream_getInt32() { - var b0 = this.getByte(); - var b1 = this.getByte(); - var b2 = this.getByte(); - var b3 = this.getByte(); - return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3; - }, - getBytes: function DecodeStream_getBytes(length) { - var end, pos = this.pos; - - if (length) { - this.ensureBuffer(pos + length); - end = pos + length; - - while (!this.eof && this.bufferLength < end) { - this.readBlock(); - } - var bufEnd = this.bufferLength; - if (end > bufEnd) { - end = bufEnd; - } - } else { - while (!this.eof) { - this.readBlock(); - } - end = this.bufferLength; - } - - this.pos = end; - return this.buffer.subarray(pos, end); - }, - peekByte: function DecodeStream_peekByte() { - var peekedByte = this.getByte(); - this.pos--; - return peekedByte; - }, - peekBytes: function DecodeStream_peekBytes(length) { - var bytes = this.getBytes(length); - this.pos -= bytes.length; - return bytes; - }, - makeSubStream: function DecodeStream_makeSubStream(start, length, dict) { - var end = start + length; - while (this.bufferLength <= end && !this.eof) { - this.readBlock(); - } - return new Stream(this.buffer, start, length, dict); - }, - skip: function DecodeStream_skip(n) { - if (!n) { - n = 1; - } - this.pos += n; - }, - reset: function DecodeStream_reset() { - this.pos = 0; - }, - getBaseStreams: function DecodeStream_getBaseStreams() { - if (this.str && this.str.getBaseStreams) { - return this.str.getBaseStreams(); - } - return []; - } - }; - - return DecodeStream; -})(); - -var StreamsSequenceStream = (function StreamsSequenceStreamClosure() { - function StreamsSequenceStream(streams) { - this.streams = streams; - DecodeStream.call(this, /* maybeLength = */ null); - } - - StreamsSequenceStream.prototype = Object.create(DecodeStream.prototype); - - StreamsSequenceStream.prototype.readBlock = - function streamSequenceStreamReadBlock() { - - var streams = this.streams; - if (streams.length === 0) { - this.eof = true; - return; - } - var stream = streams.shift(); - var chunk = stream.getBytes(); - var bufferLength = this.bufferLength; - var newLength = bufferLength + chunk.length; - var buffer = this.ensureBuffer(newLength); - buffer.set(chunk, bufferLength); - this.bufferLength = newLength; - }; - - StreamsSequenceStream.prototype.getBaseStreams = - function StreamsSequenceStream_getBaseStreams() { - - var baseStreams = []; - for (var i = 0, ii = this.streams.length; i < ii; i++) { - var stream = this.streams[i]; - if (stream.getBaseStreams) { - Util.appendToArray(baseStreams, stream.getBaseStreams()); - } - } - return baseStreams; - }; - - return StreamsSequenceStream; -})(); - -var FlateStream = (function FlateStreamClosure() { - var codeLenCodeMap = new Int32Array([ - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 - ]); - - var lengthDecode = new Int32Array([ - 0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009, 0x0000a, - 0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017, 0x2001b, 0x2001f, - 0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043, 0x40053, 0x40063, 0x40073, - 0x50083, 0x500a3, 0x500c3, 0x500e3, 0x00102, 0x00102, 0x00102 - ]); - - var distDecode = new Int32Array([ - 0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009, 0x2000d, - 0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061, 0x60081, 0x600c1, - 0x70101, 0x70181, 0x80201, 0x80301, 0x90401, 0x90601, 0xa0801, 0xa0c01, - 0xb1001, 0xb1801, 0xc2001, 0xc3001, 0xd4001, 0xd6001 - ]); - - var fixedLitCodeTab = [new Int32Array([ - 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c0, - 0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080, 0x80040, 0x900e0, - 0x70104, 0x80058, 0x80018, 0x90090, 0x70114, 0x80078, 0x80038, 0x900d0, - 0x7010c, 0x80068, 0x80028, 0x900b0, 0x80008, 0x80088, 0x80048, 0x900f0, - 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c8, - 0x7010a, 0x80064, 0x80024, 0x900a8, 0x80004, 0x80084, 0x80044, 0x900e8, - 0x70106, 0x8005c, 0x8001c, 0x90098, 0x70116, 0x8007c, 0x8003c, 0x900d8, - 0x7010e, 0x8006c, 0x8002c, 0x900b8, 0x8000c, 0x8008c, 0x8004c, 0x900f8, - 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c4, - 0x70109, 0x80062, 0x80022, 0x900a4, 0x80002, 0x80082, 0x80042, 0x900e4, - 0x70105, 0x8005a, 0x8001a, 0x90094, 0x70115, 0x8007a, 0x8003a, 0x900d4, - 0x7010d, 0x8006a, 0x8002a, 0x900b4, 0x8000a, 0x8008a, 0x8004a, 0x900f4, - 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cc, - 0x7010b, 0x80066, 0x80026, 0x900ac, 0x80006, 0x80086, 0x80046, 0x900ec, - 0x70107, 0x8005e, 0x8001e, 0x9009c, 0x70117, 0x8007e, 0x8003e, 0x900dc, - 0x7010f, 0x8006e, 0x8002e, 0x900bc, 0x8000e, 0x8008e, 0x8004e, 0x900fc, - 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c2, - 0x70108, 0x80061, 0x80021, 0x900a2, 0x80001, 0x80081, 0x80041, 0x900e2, - 0x70104, 0x80059, 0x80019, 0x90092, 0x70114, 0x80079, 0x80039, 0x900d2, - 0x7010c, 0x80069, 0x80029, 0x900b2, 0x80009, 0x80089, 0x80049, 0x900f2, - 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900ca, - 0x7010a, 0x80065, 0x80025, 0x900aa, 0x80005, 0x80085, 0x80045, 0x900ea, - 0x70106, 0x8005d, 0x8001d, 0x9009a, 0x70116, 0x8007d, 0x8003d, 0x900da, - 0x7010e, 0x8006d, 0x8002d, 0x900ba, 0x8000d, 0x8008d, 0x8004d, 0x900fa, - 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c6, - 0x70109, 0x80063, 0x80023, 0x900a6, 0x80003, 0x80083, 0x80043, 0x900e6, - 0x70105, 0x8005b, 0x8001b, 0x90096, 0x70115, 0x8007b, 0x8003b, 0x900d6, - 0x7010d, 0x8006b, 0x8002b, 0x900b6, 0x8000b, 0x8008b, 0x8004b, 0x900f6, - 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900ce, - 0x7010b, 0x80067, 0x80027, 0x900ae, 0x80007, 0x80087, 0x80047, 0x900ee, - 0x70107, 0x8005f, 0x8001f, 0x9009e, 0x70117, 0x8007f, 0x8003f, 0x900de, - 0x7010f, 0x8006f, 0x8002f, 0x900be, 0x8000f, 0x8008f, 0x8004f, 0x900fe, - 0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030, 0x900c1, - 0x70108, 0x80060, 0x80020, 0x900a1, 0x80000, 0x80080, 0x80040, 0x900e1, - 0x70104, 0x80058, 0x80018, 0x90091, 0x70114, 0x80078, 0x80038, 0x900d1, - 0x7010c, 0x80068, 0x80028, 0x900b1, 0x80008, 0x80088, 0x80048, 0x900f1, - 0x70102, 0x80054, 0x80014, 0x8011c, 0x70112, 0x80074, 0x80034, 0x900c9, - 0x7010a, 0x80064, 0x80024, 0x900a9, 0x80004, 0x80084, 0x80044, 0x900e9, - 0x70106, 0x8005c, 0x8001c, 0x90099, 0x70116, 0x8007c, 0x8003c, 0x900d9, - 0x7010e, 0x8006c, 0x8002c, 0x900b9, 0x8000c, 0x8008c, 0x8004c, 0x900f9, - 0x70101, 0x80052, 0x80012, 0x8011a, 0x70111, 0x80072, 0x80032, 0x900c5, - 0x70109, 0x80062, 0x80022, 0x900a5, 0x80002, 0x80082, 0x80042, 0x900e5, - 0x70105, 0x8005a, 0x8001a, 0x90095, 0x70115, 0x8007a, 0x8003a, 0x900d5, - 0x7010d, 0x8006a, 0x8002a, 0x900b5, 0x8000a, 0x8008a, 0x8004a, 0x900f5, - 0x70103, 0x80056, 0x80016, 0x8011e, 0x70113, 0x80076, 0x80036, 0x900cd, - 0x7010b, 0x80066, 0x80026, 0x900ad, 0x80006, 0x80086, 0x80046, 0x900ed, - 0x70107, 0x8005e, 0x8001e, 0x9009d, 0x70117, 0x8007e, 0x8003e, 0x900dd, - 0x7010f, 0x8006e, 0x8002e, 0x900bd, 0x8000e, 0x8008e, 0x8004e, 0x900fd, - 0x70100, 0x80051, 0x80011, 0x80119, 0x70110, 0x80071, 0x80031, 0x900c3, - 0x70108, 0x80061, 0x80021, 0x900a3, 0x80001, 0x80081, 0x80041, 0x900e3, - 0x70104, 0x80059, 0x80019, 0x90093, 0x70114, 0x80079, 0x80039, 0x900d3, - 0x7010c, 0x80069, 0x80029, 0x900b3, 0x80009, 0x80089, 0x80049, 0x900f3, - 0x70102, 0x80055, 0x80015, 0x8011d, 0x70112, 0x80075, 0x80035, 0x900cb, - 0x7010a, 0x80065, 0x80025, 0x900ab, 0x80005, 0x80085, 0x80045, 0x900eb, - 0x70106, 0x8005d, 0x8001d, 0x9009b, 0x70116, 0x8007d, 0x8003d, 0x900db, - 0x7010e, 0x8006d, 0x8002d, 0x900bb, 0x8000d, 0x8008d, 0x8004d, 0x900fb, - 0x70101, 0x80053, 0x80013, 0x8011b, 0x70111, 0x80073, 0x80033, 0x900c7, - 0x70109, 0x80063, 0x80023, 0x900a7, 0x80003, 0x80083, 0x80043, 0x900e7, - 0x70105, 0x8005b, 0x8001b, 0x90097, 0x70115, 0x8007b, 0x8003b, 0x900d7, - 0x7010d, 0x8006b, 0x8002b, 0x900b7, 0x8000b, 0x8008b, 0x8004b, 0x900f7, - 0x70103, 0x80057, 0x80017, 0x8011f, 0x70113, 0x80077, 0x80037, 0x900cf, - 0x7010b, 0x80067, 0x80027, 0x900af, 0x80007, 0x80087, 0x80047, 0x900ef, - 0x70107, 0x8005f, 0x8001f, 0x9009f, 0x70117, 0x8007f, 0x8003f, 0x900df, - 0x7010f, 0x8006f, 0x8002f, 0x900bf, 0x8000f, 0x8008f, 0x8004f, 0x900ff - ]), 9]; - - var fixedDistCodeTab = [new Int32Array([ - 0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c, 0x5001c, - 0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016, 0x5000e, 0x00000, - 0x50001, 0x50011, 0x50009, 0x50019, 0x50005, 0x50015, 0x5000d, 0x5001d, - 0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000 - ]), 5]; - - function FlateStream(str, maybeLength) { - this.str = str; - this.dict = str.dict; - - var cmf = str.getByte(); - var flg = str.getByte(); - if (cmf === -1 || flg === -1) { - error('Invalid header in flate stream: ' + cmf + ', ' + flg); - } - if ((cmf & 0x0f) !== 0x08) { - error('Unknown compression method in flate stream: ' + cmf + ', ' + flg); - } - if ((((cmf << 8) + flg) % 31) !== 0) { - error('Bad FCHECK in flate stream: ' + cmf + ', ' + flg); - } - if (flg & 0x20) { - error('FDICT bit set in flate stream: ' + cmf + ', ' + flg); - } - - this.codeSize = 0; - this.codeBuf = 0; - - DecodeStream.call(this, maybeLength); - } - - FlateStream.prototype = Object.create(DecodeStream.prototype); - - FlateStream.prototype.getBits = function FlateStream_getBits(bits) { - var str = this.str; - var codeSize = this.codeSize; - var codeBuf = this.codeBuf; - - var b; - while (codeSize < bits) { - if ((b = str.getByte()) === -1) { - error('Bad encoding in flate stream'); - } - codeBuf |= b << codeSize; - codeSize += 8; - } - b = codeBuf & ((1 << bits) - 1); - this.codeBuf = codeBuf >> bits; - this.codeSize = codeSize -= bits; - - return b; - }; - - FlateStream.prototype.getCode = function FlateStream_getCode(table) { - var str = this.str; - var codes = table[0]; - var maxLen = table[1]; - var codeSize = this.codeSize; - var codeBuf = this.codeBuf; - - var b; - while (codeSize < maxLen) { - if ((b = str.getByte()) === -1) { - // premature end of stream. code might however still be valid. - // codeSize < codeLen check below guards against incomplete codeVal. - break; - } - codeBuf |= (b << codeSize); - codeSize += 8; - } - var code = codes[codeBuf & ((1 << maxLen) - 1)]; - var codeLen = code >> 16; - var codeVal = code & 0xffff; - if (codeLen < 1 || codeSize < codeLen) { - error('Bad encoding in flate stream'); - } - this.codeBuf = (codeBuf >> codeLen); - this.codeSize = (codeSize - codeLen); - return codeVal; - }; - - FlateStream.prototype.generateHuffmanTable = - function flateStreamGenerateHuffmanTable(lengths) { - var n = lengths.length; - - // find max code length - var maxLen = 0; - var i; - for (i = 0; i < n; ++i) { - if (lengths[i] > maxLen) { - maxLen = lengths[i]; - } - } - - // build the table - var size = 1 << maxLen; - var codes = new Int32Array(size); - for (var len = 1, code = 0, skip = 2; - len <= maxLen; - ++len, code <<= 1, skip <<= 1) { - for (var val = 0; val < n; ++val) { - if (lengths[val] === len) { - // bit-reverse the code - var code2 = 0; - var t = code; - for (i = 0; i < len; ++i) { - code2 = (code2 << 1) | (t & 1); - t >>= 1; - } - - // fill the table entries - for (i = code2; i < size; i += skip) { - codes[i] = (len << 16) | val; - } - ++code; - } - } - } - - return [codes, maxLen]; - }; - - FlateStream.prototype.readBlock = function FlateStream_readBlock() { - var buffer, len; - var str = this.str; - // read block header - var hdr = this.getBits(3); - if (hdr & 1) { - this.eof = true; - } - hdr >>= 1; - - if (hdr === 0) { // uncompressed block - var b; - - if ((b = str.getByte()) === -1) { - error('Bad block header in flate stream'); - } - var blockLen = b; - if ((b = str.getByte()) === -1) { - error('Bad block header in flate stream'); - } - blockLen |= (b << 8); - if ((b = str.getByte()) === -1) { - error('Bad block header in flate stream'); - } - var check = b; - if ((b = str.getByte()) === -1) { - error('Bad block header in flate stream'); - } - check |= (b << 8); - if (check !== (~blockLen & 0xffff) && - (blockLen !== 0 || check !== 0)) { - // Ignoring error for bad "empty" block (see issue 1277) - error('Bad uncompressed block length in flate stream'); - } - - this.codeBuf = 0; - this.codeSize = 0; - - var bufferLength = this.bufferLength; - buffer = this.ensureBuffer(bufferLength + blockLen); - var end = bufferLength + blockLen; - this.bufferLength = end; - if (blockLen === 0) { - if (str.peekByte() === -1) { - this.eof = true; - } - } else { - for (var n = bufferLength; n < end; ++n) { - if ((b = str.getByte()) === -1) { - this.eof = true; - break; - } - buffer[n] = b; - } - } - return; - } - - var litCodeTable; - var distCodeTable; - if (hdr === 1) { // compressed block, fixed codes - litCodeTable = fixedLitCodeTab; - distCodeTable = fixedDistCodeTab; - } else if (hdr === 2) { // compressed block, dynamic codes - var numLitCodes = this.getBits(5) + 257; - var numDistCodes = this.getBits(5) + 1; - var numCodeLenCodes = this.getBits(4) + 4; - - // build the code lengths code table - var codeLenCodeLengths = new Uint8Array(codeLenCodeMap.length); - - var i; - for (i = 0; i < numCodeLenCodes; ++i) { - codeLenCodeLengths[codeLenCodeMap[i]] = this.getBits(3); - } - var codeLenCodeTab = this.generateHuffmanTable(codeLenCodeLengths); - - // build the literal and distance code tables - len = 0; - i = 0; - var codes = numLitCodes + numDistCodes; - var codeLengths = new Uint8Array(codes); - var bitsLength, bitsOffset, what; - while (i < codes) { - var code = this.getCode(codeLenCodeTab); - if (code === 16) { - bitsLength = 2; bitsOffset = 3; what = len; - } else if (code === 17) { - bitsLength = 3; bitsOffset = 3; what = (len = 0); - } else if (code === 18) { - bitsLength = 7; bitsOffset = 11; what = (len = 0); - } else { - codeLengths[i++] = len = code; - continue; - } - - var repeatLength = this.getBits(bitsLength) + bitsOffset; - while (repeatLength-- > 0) { - codeLengths[i++] = what; - } - } - - litCodeTable = - this.generateHuffmanTable(codeLengths.subarray(0, numLitCodes)); - distCodeTable = - this.generateHuffmanTable(codeLengths.subarray(numLitCodes, codes)); - } else { - error('Unknown block type in flate stream'); - } - - buffer = this.buffer; - var limit = buffer ? buffer.length : 0; - var pos = this.bufferLength; - while (true) { - var code1 = this.getCode(litCodeTable); - if (code1 < 256) { - if (pos + 1 >= limit) { - buffer = this.ensureBuffer(pos + 1); - limit = buffer.length; - } - buffer[pos++] = code1; - continue; - } - if (code1 === 256) { - this.bufferLength = pos; - return; - } - code1 -= 257; - code1 = lengthDecode[code1]; - var code2 = code1 >> 16; - if (code2 > 0) { - code2 = this.getBits(code2); - } - len = (code1 & 0xffff) + code2; - code1 = this.getCode(distCodeTable); - code1 = distDecode[code1]; - code2 = code1 >> 16; - if (code2 > 0) { - code2 = this.getBits(code2); - } - var dist = (code1 & 0xffff) + code2; - if (pos + len >= limit) { - buffer = this.ensureBuffer(pos + len); - limit = buffer.length; - } - for (var k = 0; k < len; ++k, ++pos) { - buffer[pos] = buffer[pos - dist]; - } - } - }; - - return FlateStream; -})(); - -var PredictorStream = (function PredictorStreamClosure() { - function PredictorStream(str, maybeLength, params) { - var predictor = this.predictor = params.get('Predictor') || 1; - - if (predictor <= 1) { - return str; // no prediction - } - if (predictor !== 2 && (predictor < 10 || predictor > 15)) { - error('Unsupported predictor: ' + predictor); - } - - if (predictor === 2) { - this.readBlock = this.readBlockTiff; - } else { - this.readBlock = this.readBlockPng; - } - - this.str = str; - this.dict = str.dict; - - var colors = this.colors = params.get('Colors') || 1; - var bits = this.bits = params.get('BitsPerComponent') || 8; - var columns = this.columns = params.get('Columns') || 1; - - this.pixBytes = (colors * bits + 7) >> 3; - this.rowBytes = (columns * colors * bits + 7) >> 3; - - DecodeStream.call(this, maybeLength); - return this; - } - - PredictorStream.prototype = Object.create(DecodeStream.prototype); - - PredictorStream.prototype.readBlockTiff = - function predictorStreamReadBlockTiff() { - var rowBytes = this.rowBytes; - - var bufferLength = this.bufferLength; - var buffer = this.ensureBuffer(bufferLength + rowBytes); - - var bits = this.bits; - var colors = this.colors; - - var rawBytes = this.str.getBytes(rowBytes); - this.eof = !rawBytes.length; - if (this.eof) { - return; - } - - var inbuf = 0, outbuf = 0; - var inbits = 0, outbits = 0; - var pos = bufferLength; - var i; - - if (bits === 1) { - for (i = 0; i < rowBytes; ++i) { - var c = rawBytes[i]; - inbuf = (inbuf << 8) | c; - // bitwise addition is exclusive or - // first shift inbuf and then add - buffer[pos++] = (c ^ (inbuf >> colors)) & 0xFF; - // truncate inbuf (assumes colors < 16) - inbuf &= 0xFFFF; - } - } else if (bits === 8) { - for (i = 0; i < colors; ++i) { - buffer[pos++] = rawBytes[i]; - } - for (; i < rowBytes; ++i) { - buffer[pos] = buffer[pos - colors] + rawBytes[i]; - pos++; - } - } else { - var compArray = new Uint8Array(colors + 1); - var bitMask = (1 << bits) - 1; - var j = 0, k = bufferLength; - var columns = this.columns; - for (i = 0; i < columns; ++i) { - for (var kk = 0; kk < colors; ++kk) { - if (inbits < bits) { - inbuf = (inbuf << 8) | (rawBytes[j++] & 0xFF); - inbits += 8; - } - compArray[kk] = (compArray[kk] + - (inbuf >> (inbits - bits))) & bitMask; - inbits -= bits; - outbuf = (outbuf << bits) | compArray[kk]; - outbits += bits; - if (outbits >= 8) { - buffer[k++] = (outbuf >> (outbits - 8)) & 0xFF; - outbits -= 8; - } - } - } - if (outbits > 0) { - buffer[k++] = (outbuf << (8 - outbits)) + - (inbuf & ((1 << (8 - outbits)) - 1)); - } - } - this.bufferLength += rowBytes; - }; - - PredictorStream.prototype.readBlockPng = - function predictorStreamReadBlockPng() { - - var rowBytes = this.rowBytes; - var pixBytes = this.pixBytes; - - var predictor = this.str.getByte(); - var rawBytes = this.str.getBytes(rowBytes); - this.eof = !rawBytes.length; - if (this.eof) { - return; - } - - var bufferLength = this.bufferLength; - var buffer = this.ensureBuffer(bufferLength + rowBytes); - - var prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength); - if (prevRow.length === 0) { - prevRow = new Uint8Array(rowBytes); - } - - var i, j = bufferLength, up, c; - switch (predictor) { - case 0: - for (i = 0; i < rowBytes; ++i) { - buffer[j++] = rawBytes[i]; - } - break; - case 1: - for (i = 0; i < pixBytes; ++i) { - buffer[j++] = rawBytes[i]; - } - for (; i < rowBytes; ++i) { - buffer[j] = (buffer[j - pixBytes] + rawBytes[i]) & 0xFF; - j++; - } - break; - case 2: - for (i = 0; i < rowBytes; ++i) { - buffer[j++] = (prevRow[i] + rawBytes[i]) & 0xFF; - } - break; - case 3: - for (i = 0; i < pixBytes; ++i) { - buffer[j++] = (prevRow[i] >> 1) + rawBytes[i]; - } - for (; i < rowBytes; ++i) { - buffer[j] = (((prevRow[i] + buffer[j - pixBytes]) >> 1) + - rawBytes[i]) & 0xFF; - j++; - } - break; - case 4: - // we need to save the up left pixels values. the simplest way - // is to create a new buffer - for (i = 0; i < pixBytes; ++i) { - up = prevRow[i]; - c = rawBytes[i]; - buffer[j++] = up + c; - } - for (; i < rowBytes; ++i) { - up = prevRow[i]; - var upLeft = prevRow[i - pixBytes]; - var left = buffer[j - pixBytes]; - var p = left + up - upLeft; - - var pa = p - left; - if (pa < 0) { - pa = -pa; - } - var pb = p - up; - if (pb < 0) { - pb = -pb; - } - var pc = p - upLeft; - if (pc < 0) { - pc = -pc; - } - - c = rawBytes[i]; - if (pa <= pb && pa <= pc) { - buffer[j++] = left + c; - } else if (pb <= pc) { - buffer[j++] = up + c; - } else { - buffer[j++] = upLeft + c; - } - } - break; - default: - error('Unsupported predictor: ' + predictor); - } - this.bufferLength += rowBytes; - }; - - return PredictorStream; -})(); - -/** - * Depending on the type of JPEG a JpegStream is handled in different ways. For - * JPEG's that are supported natively such as DeviceGray and DeviceRGB the image - * data is stored and then loaded by the browser. For unsupported JPEG's we use - * a library to decode these images and the stream behaves like all the other - * DecodeStreams. - */ -var JpegStream = (function JpegStreamClosure() { - function JpegStream(stream, maybeLength, dict, xref) { - // Some images may contain 'junk' before the SOI (start-of-image) marker. - // Note: this seems to mainly affect inline images. - var ch; - while ((ch = stream.getByte()) !== -1) { - if (ch === 0xFF) { // Find the first byte of the SOI marker (0xFFD8). - stream.skip(-1); // Reset the stream position to the SOI. - break; - } - } - this.stream = stream; - this.maybeLength = maybeLength; - this.dict = dict; - - DecodeStream.call(this, maybeLength); - } - - JpegStream.prototype = Object.create(DecodeStream.prototype); - - Object.defineProperty(JpegStream.prototype, 'bytes', { - get: function JpegStream_bytes() { - // If this.maybeLength is null, we'll get the entire stream. - return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength)); - }, - configurable: true - }); - - JpegStream.prototype.ensureBuffer = function JpegStream_ensureBuffer(req) { - if (this.bufferLength) { - return; - } - try { - var jpegImage = new JpegImage(); - - // checking if values needs to be transformed before conversion - if (this.forceRGB && this.dict && isArray(this.dict.get('Decode'))) { - var decodeArr = this.dict.get('Decode'); - var bitsPerComponent = this.dict.get('BitsPerComponent') || 8; - var decodeArrLength = decodeArr.length; - var transform = new Int32Array(decodeArrLength); - var transformNeeded = false; - var maxValue = (1 << bitsPerComponent) - 1; - for (var i = 0; i < decodeArrLength; i += 2) { - transform[i] = ((decodeArr[i + 1] - decodeArr[i]) * 256) | 0; - transform[i + 1] = (decodeArr[i] * maxValue) | 0; - if (transform[i] !== 256 || transform[i + 1] !== 0) { - transformNeeded = true; - } - } - if (transformNeeded) { - jpegImage.decodeTransform = transform; - } - } - - jpegImage.parse(this.bytes); - var data = jpegImage.getData(this.drawWidth, this.drawHeight, - this.forceRGB); - this.buffer = data; - this.bufferLength = data.length; - this.eof = true; - } catch (e) { - error('JPEG error: ' + e); - } - }; - - JpegStream.prototype.getBytes = function JpegStream_getBytes(length) { - this.ensureBuffer(); - return this.buffer; - }; - - JpegStream.prototype.getIR = function JpegStream_getIR() { - return PDFJS.createObjectURL(this.bytes, 'image/jpeg'); - }; - /** - * Checks if the image can be decoded and displayed by the browser without any - * further processing such as color space conversions. - */ - JpegStream.prototype.isNativelySupported = - function JpegStream_isNativelySupported(xref, res) { - var cs = ColorSpace.parse(this.dict.get('ColorSpace', 'CS'), xref, res); - return (cs.name === 'DeviceGray' || cs.name === 'DeviceRGB') && - cs.isDefaultDecode(this.dict.get('Decode', 'D')); - }; - /** - * Checks if the image can be decoded by the browser. - */ - JpegStream.prototype.isNativelyDecodable = - function JpegStream_isNativelyDecodable(xref, res) { - var cs = ColorSpace.parse(this.dict.get('ColorSpace', 'CS'), xref, res); - return (cs.numComps === 1 || cs.numComps === 3) && - cs.isDefaultDecode(this.dict.get('Decode', 'D')); - }; - - return JpegStream; -})(); - -/** - * For JPEG 2000's we use a library to decode these images and - * the stream behaves like all the other DecodeStreams. - */ -var JpxStream = (function JpxStreamClosure() { - function JpxStream(stream, maybeLength, dict) { - this.stream = stream; - this.maybeLength = maybeLength; - this.dict = dict; - - DecodeStream.call(this, maybeLength); - } - - JpxStream.prototype = Object.create(DecodeStream.prototype); - - Object.defineProperty(JpxStream.prototype, 'bytes', { - get: function JpxStream_bytes() { - // If this.maybeLength is null, we'll get the entire stream. - return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength)); - }, - configurable: true - }); - - JpxStream.prototype.ensureBuffer = function JpxStream_ensureBuffer(req) { - if (this.bufferLength) { - return; - } - - var jpxImage = new JpxImage(); - jpxImage.parse(this.bytes); - - var width = jpxImage.width; - var height = jpxImage.height; - var componentsCount = jpxImage.componentsCount; - var tileCount = jpxImage.tiles.length; - if (tileCount === 1) { - this.buffer = jpxImage.tiles[0].items; - } else { - var data = new Uint8Array(width * height * componentsCount); - - for (var k = 0; k < tileCount; k++) { - var tileComponents = jpxImage.tiles[k]; - var tileWidth = tileComponents.width; - var tileHeight = tileComponents.height; - var tileLeft = tileComponents.left; - var tileTop = tileComponents.top; - - var src = tileComponents.items; - var srcPosition = 0; - var dataPosition = (width * tileTop + tileLeft) * componentsCount; - var imgRowSize = width * componentsCount; - var tileRowSize = tileWidth * componentsCount; - - for (var j = 0; j < tileHeight; j++) { - var rowBytes = src.subarray(srcPosition, srcPosition + tileRowSize); - data.set(rowBytes, dataPosition); - srcPosition += tileRowSize; - dataPosition += imgRowSize; - } - } - this.buffer = data; - } - this.bufferLength = this.buffer.length; - this.eof = true; - }; - - return JpxStream; -})(); - -/** - * For JBIG2's we use a library to decode these images and - * the stream behaves like all the other DecodeStreams. - */ -var Jbig2Stream = (function Jbig2StreamClosure() { - function Jbig2Stream(stream, maybeLength, dict) { - this.stream = stream; - this.maybeLength = maybeLength; - this.dict = dict; - - DecodeStream.call(this, maybeLength); - } - - Jbig2Stream.prototype = Object.create(DecodeStream.prototype); - - Object.defineProperty(Jbig2Stream.prototype, 'bytes', { - get: function Jbig2Stream_bytes() { - // If this.maybeLength is null, we'll get the entire stream. - return shadow(this, 'bytes', this.stream.getBytes(this.maybeLength)); - }, - configurable: true - }); - - Jbig2Stream.prototype.ensureBuffer = function Jbig2Stream_ensureBuffer(req) { - if (this.bufferLength) { - return; - } - - var jbig2Image = new Jbig2Image(); - - var chunks = [], xref = this.dict.xref; - var decodeParams = xref.fetchIfRef(this.dict.get('DecodeParms')); - - // According to the PDF specification, DecodeParms can be either - // a dictionary, or an array whose elements are dictionaries. - if (isArray(decodeParams)) { - if (decodeParams.length > 1) { - warn('JBIG2 - \'DecodeParms\' array with multiple elements ' + - 'not supported.'); - } - decodeParams = xref.fetchIfRef(decodeParams[0]); - } - if (decodeParams && decodeParams.has('JBIG2Globals')) { - var globalsStream = decodeParams.get('JBIG2Globals'); - var globals = globalsStream.getBytes(); - chunks.push({data: globals, start: 0, end: globals.length}); - } - chunks.push({data: this.bytes, start: 0, end: this.bytes.length}); - var data = jbig2Image.parseChunks(chunks); - var dataLength = data.length; - - // JBIG2 had black as 1 and white as 0, inverting the colors - for (var i = 0; i < dataLength; i++) { - data[i] ^= 0xFF; - } - - this.buffer = data; - this.bufferLength = dataLength; - this.eof = true; - }; - - return Jbig2Stream; -})(); - -var DecryptStream = (function DecryptStreamClosure() { - function DecryptStream(str, maybeLength, decrypt) { - this.str = str; - this.dict = str.dict; - this.decrypt = decrypt; - this.nextChunk = null; - this.initialized = false; - - DecodeStream.call(this, maybeLength); - } - - var chunkSize = 512; - - DecryptStream.prototype = Object.create(DecodeStream.prototype); - - DecryptStream.prototype.readBlock = function DecryptStream_readBlock() { - var chunk; - if (this.initialized) { - chunk = this.nextChunk; - } else { - chunk = this.str.getBytes(chunkSize); - this.initialized = true; - } - if (!chunk || chunk.length === 0) { - this.eof = true; - return; - } - this.nextChunk = this.str.getBytes(chunkSize); - var hasMoreData = this.nextChunk && this.nextChunk.length > 0; - - var decrypt = this.decrypt; - chunk = decrypt(chunk, !hasMoreData); - - 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; - }; - - return DecryptStream; -})(); - -var Ascii85Stream = (function Ascii85StreamClosure() { - function Ascii85Stream(str, maybeLength) { - this.str = str; - this.dict = str.dict; - this.input = new Uint8Array(5); - - // Most streams increase in size when decoded, but Ascii85 streams - // typically shrink by ~20%. - if (maybeLength) { - maybeLength = 0.8 * maybeLength; - } - DecodeStream.call(this, maybeLength); - } - - Ascii85Stream.prototype = Object.create(DecodeStream.prototype); - - Ascii85Stream.prototype.readBlock = function Ascii85Stream_readBlock() { - var TILDA_CHAR = 0x7E; // '~' - var Z_LOWER_CHAR = 0x7A; // 'z' - var EOF = -1; - - var str = this.str; - - var c = str.getByte(); - while (Lexer.isSpace(c)) { - c = str.getByte(); - } - - if (c === EOF || c === TILDA_CHAR) { - this.eof = true; - return; - } - - var bufferLength = this.bufferLength, buffer; - var i; - - // special code for z - if (c === Z_LOWER_CHAR) { - buffer = this.ensureBuffer(bufferLength + 4); - for (i = 0; i < 4; ++i) { - buffer[bufferLength + i] = 0; - } - this.bufferLength += 4; - } else { - var input = this.input; - input[0] = c; - for (i = 1; i < 5; ++i) { - c = str.getByte(); - while (Lexer.isSpace(c)) { - c = str.getByte(); - } - - input[i] = c; - - if (c === EOF || c === TILDA_CHAR) { - break; - } - } - buffer = this.ensureBuffer(bufferLength + i - 1); - this.bufferLength += i - 1; - - // partial ending; - if (i < 5) { - for (; i < 5; ++i) { - input[i] = 0x21 + 84; - } - this.eof = true; - } - var t = 0; - for (i = 0; i < 5; ++i) { - t = t * 85 + (input[i] - 0x21); - } - - for (i = 3; i >= 0; --i) { - buffer[bufferLength + i] = t & 0xFF; - t >>= 8; - } - } - }; - - return Ascii85Stream; -})(); - -var AsciiHexStream = (function AsciiHexStreamClosure() { - function AsciiHexStream(str, maybeLength) { - this.str = str; - this.dict = str.dict; - - this.firstDigit = -1; - - // Most streams increase in size when decoded, but AsciiHex streams shrink - // by 50%. - if (maybeLength) { - maybeLength = 0.5 * maybeLength; - } - DecodeStream.call(this, maybeLength); - } - - AsciiHexStream.prototype = Object.create(DecodeStream.prototype); - - AsciiHexStream.prototype.readBlock = function AsciiHexStream_readBlock() { - var UPSTREAM_BLOCK_SIZE = 8000; - var bytes = this.str.getBytes(UPSTREAM_BLOCK_SIZE); - if (!bytes.length) { - this.eof = true; - return; - } - - var maxDecodeLength = (bytes.length + 1) >> 1; - var buffer = this.ensureBuffer(this.bufferLength + maxDecodeLength); - var bufferLength = this.bufferLength; - - var firstDigit = this.firstDigit; - for (var i = 0, ii = bytes.length; i < ii; i++) { - var ch = bytes[i], digit; - if (ch >= 0x30 && ch <= 0x39) { // '0'-'9' - digit = ch & 0x0F; - } else if ((ch >= 0x41 && ch <= 0x46) || (ch >= 0x61 && ch <= 0x66)) { - // 'A'-'Z', 'a'-'z' - digit = (ch & 0x0F) + 9; - } else if (ch === 0x3E) { // '>' - this.eof = true; - break; - } else { // probably whitespace - continue; // ignoring - } - if (firstDigit < 0) { - firstDigit = digit; - } else { - buffer[bufferLength++] = (firstDigit << 4) | digit; - firstDigit = -1; - } - } - if (firstDigit >= 0 && this.eof) { - // incomplete byte - buffer[bufferLength++] = (firstDigit << 4); - firstDigit = -1; - } - this.firstDigit = firstDigit; - this.bufferLength = bufferLength; - }; - - return AsciiHexStream; -})(); - -var RunLengthStream = (function RunLengthStreamClosure() { - function RunLengthStream(str, maybeLength) { - this.str = str; - this.dict = str.dict; - - DecodeStream.call(this, maybeLength); - } - - RunLengthStream.prototype = Object.create(DecodeStream.prototype); - - RunLengthStream.prototype.readBlock = function RunLengthStream_readBlock() { - // The repeatHeader has following format. The first byte defines type of run - // and amount of bytes to repeat/copy: n = 0 through 127 - copy next n bytes - // (in addition to the second byte from the header), n = 129 through 255 - - // duplicate the second byte from the header (257 - n) times, n = 128 - end. - var repeatHeader = this.str.getBytes(2); - if (!repeatHeader || repeatHeader.length < 2 || repeatHeader[0] === 128) { - this.eof = true; - return; - } - - var buffer; - var bufferLength = this.bufferLength; - var n = repeatHeader[0]; - if (n < 128) { - // copy n bytes - buffer = this.ensureBuffer(bufferLength + n + 1); - buffer[bufferLength++] = repeatHeader[1]; - if (n > 0) { - var source = this.str.getBytes(n); - buffer.set(source, bufferLength); - bufferLength += n; - } - } else { - n = 257 - n; - var b = repeatHeader[1]; - buffer = this.ensureBuffer(bufferLength + n + 1); - for (var i = 0; i < n; i++) { - buffer[bufferLength++] = b; - } - } - this.bufferLength = bufferLength; - }; - - return RunLengthStream; -})(); - -var CCITTFaxStream = (function CCITTFaxStreamClosure() { - - var ccittEOL = -2; - var twoDimPass = 0; - var twoDimHoriz = 1; - var twoDimVert0 = 2; - var twoDimVertR1 = 3; - var twoDimVertL1 = 4; - var twoDimVertR2 = 5; - var twoDimVertL2 = 6; - var twoDimVertR3 = 7; - var twoDimVertL3 = 8; - - var 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] - ]; - - var 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 - ]; - - var 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] - ]; - - var 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] - ]; - - var 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] - ]; - - var 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 CCITTFaxStream(str, maybeLength, params) { - this.str = str; - this.dict = str.dict; - - params = params || Dict.empty; - - 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 === undefined) { - 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 = 0; - this.outputBits = 0; - - 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, maybeLength); - } - - CCITTFaxStream.prototype = Object.create(DecodeStream.prototype); - - CCITTFaxStream.prototype.readBlock = function CCITTFaxStream_readBlock() { - while (!this.eof) { - var c = this.lookChar(); - this.ensureBuffer(this.bufferLength + 1); - this.buffer[this.bufferLength++] = c; - } - }; - - CCITTFaxStream.prototype.addPixels = - function ccittFaxStreamAddPixels(a1, blackPixels) { - var codingLine = this.codingLine; - var codingPos = this.codingPos; - - if (a1 > codingLine[codingPos]) { - if (a1 > this.columns) { - info('row is wrong length'); - this.err = true; - a1 = this.columns; - } - if ((codingPos & 1) ^ blackPixels) { - ++codingPos; - } - - codingLine[codingPos] = a1; - } - this.codingPos = codingPos; - }; - - CCITTFaxStream.prototype.addPixelsNeg = - function ccittFaxStreamAddPixelsNeg(a1, blackPixels) { - var codingLine = this.codingLine; - var codingPos = this.codingPos; - - if (a1 > codingLine[codingPos]) { - if (a1 > this.columns) { - info('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) { - info('invalid code'); - this.err = true; - a1 = 0; - } - while (codingPos > 0 && a1 < codingLine[codingPos - 1]) { - --codingPos; - } - codingLine[codingPos] = a1; - } - - this.codingPos = codingPos; - }; - - CCITTFaxStream.prototype.lookChar = function CCITTFaxStream_lookChar() { - var refLine = this.refLine; - var codingLine = this.codingLine; - var columns = this.columns; - - var refPos, blackPixels, bits, i; - - if (this.outputBits === 0) { - if (this.eof) { - return null; - } - this.err = false; - - var code1, code2, code3; - if (this.nextLine2D) { - for (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) { - code1 = this.getTwoDimCode(); - switch (code1) { - case twoDimPass: - this.addPixels(refLine[refPos + 1], blackPixels); - if (refLine[refPos + 1] < columns) { - refPos += 2; - } - break; - case twoDimHoriz: - code1 = code2 = 0; - if (blackPixels) { - do { - code1 += (code3 = this.getBlackCode()); - } while (code3 >= 64); - do { - code2 += (code3 = this.getWhiteCode()); - } while (code3 >= 64); - } else { - 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: - info('bad 2d code'); - this.addPixels(columns, 0); - this.err = true; - } - } - } 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; - } - } - - var gotEOL = false; - - if (this.byteAlign) { - this.inputBits &= ~7; - } - - if (!this.eoblock && this.row === this.rows - 1) { - this.eof = true; - } else { - code1 = this.lookBits(12); - if (this.eoline) { - while (code1 !== EOF && code1 !== 1) { - this.eatBits(1); - code1 = this.lookBits(12); - } - } else { - 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 && this.byteAlign) { - 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 (i = 0; i < 4; ++i) { - code1 = this.lookBits(12); - if (code1 !== 1) { - info('bad rtc code: ' + code1); - } - this.eatBits(12); - if (this.encoding > 0) { - this.lookBits(1); - this.eatBits(1); - } - } - } - this.eof = true; - } - } else if (this.err && this.eoline) { - while (true) { - code1 = this.lookBits(13); - if (code1 === EOF) { - this.eof = true; - return null; - } - 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++; - } - - var c; - if (this.outputBits >= 8) { - c = (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 { - bits = 8; - c = 0; - do { - if (this.outputBits > bits) { - c <<= bits; - if (!(this.codingPos & 1)) { - c |= 0xFF >> (8 - bits); - } - this.outputBits -= bits; - bits = 0; - } else { - c <<= this.outputBits; - if (!(this.codingPos & 1)) { - c |= 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) { - c <<= bits; - bits = 0; - } - } - } while (bits); - } - if (this.black) { - c ^= 0xFF; - } - return c; - }; - - // This functions returns the code found from the table. - // The start and end parameters set the boundaries for searching the table. - // The limit parameter is optional. Function returns an array with three - // values. The first array element indicates whether a valid code is being - // returned. The second array element is the actual code. The third array - // element indicates whether EOF was reached. - CCITTFaxStream.prototype.findTableCode = - function ccittFaxStreamFindTableCode(start, end, table, limit) { - - var limitValue = limit || 0; - for (var i = start; i <= end; ++i) { - var code = this.lookBits(i); - if (code === EOF) { - return [true, 1, false]; - } - if (i < end) { - code <<= end - i; - } - if (!limitValue || code >= limitValue) { - var p = table[code - limitValue]; - if (p[0] === i) { - this.eatBits(i); - return [true, p[1], true]; - } - } - } - return [false, 0, false]; - }; - - CCITTFaxStream.prototype.getTwoDimCode = - function ccittFaxStreamGetTwoDimCode() { - - var code = 0; - var p; - if (this.eoblock) { - code = this.lookBits(7); - p = twoDimTable[code]; - if (p && p[0] > 0) { - this.eatBits(p[0]); - return p[1]; - } - } else { - var result = this.findTableCode(1, 7, twoDimTable); - if (result[0] && result[2]) { - return result[1]; - } - } - info('Bad two dim code'); - return EOF; - }; - - CCITTFaxStream.prototype.getWhiteCode = - function ccittFaxStreamGetWhiteCode() { - - var code = 0; - var p; - 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 { - var result = this.findTableCode(1, 9, whiteTable2); - if (result[0]) { - return result[1]; - } - - result = this.findTableCode(11, 12, whiteTable1); - if (result[0]) { - return result[1]; - } - } - info('bad white code'); - this.eatBits(1); - return 1; - }; - - CCITTFaxStream.prototype.getBlackCode = - function ccittFaxStreamGetBlackCode() { - - var code, p; - 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 { - var result = this.findTableCode(2, 6, blackTable3); - if (result[0]) { - return result[1]; - } - - result = this.findTableCode(7, 12, blackTable2, 64); - if (result[0]) { - return result[1]; - } - - result = this.findTableCode(10, 13, blackTable1); - if (result[0]) { - return result[1]; - } - } - info('bad black code'); - this.eatBits(1); - return 1; - }; - - CCITTFaxStream.prototype.lookBits = function CCITTFaxStream_lookBits(n) { - var c; - while (this.inputBits < n) { - if ((c = this.str.getByte()) === -1) { - 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)); - }; - - CCITTFaxStream.prototype.eatBits = function CCITTFaxStream_eatBits(n) { - if ((this.inputBits -= n) < 0) { - this.inputBits = 0; - } - }; - - return CCITTFaxStream; -})(); - -var LZWStream = (function LZWStreamClosure() { - function LZWStream(str, maybeLength, earlyChange) { - this.str = str; - this.dict = str.dict; - this.cachedData = 0; - this.bitsCached = 0; - - var maxLzwDictionarySize = 4096; - var lzwState = { - earlyChange: earlyChange, - codeLength: 9, - nextCode: 258, - dictionaryValues: new Uint8Array(maxLzwDictionarySize), - dictionaryLengths: new Uint16Array(maxLzwDictionarySize), - dictionaryPrevCodes: new Uint16Array(maxLzwDictionarySize), - currentSequence: new Uint8Array(maxLzwDictionarySize), - currentSequenceLength: 0 - }; - for (var i = 0; i < 256; ++i) { - lzwState.dictionaryValues[i] = i; - lzwState.dictionaryLengths[i] = 1; - } - this.lzwState = lzwState; - - DecodeStream.call(this, maybeLength); - } - - LZWStream.prototype = Object.create(DecodeStream.prototype); - - LZWStream.prototype.readBits = function LZWStream_readBits(n) { - var bitsCached = this.bitsCached; - var cachedData = this.cachedData; - while (bitsCached < n) { - var c = this.str.getByte(); - if (c === -1) { - this.eof = true; - return null; - } - cachedData = (cachedData << 8) | c; - bitsCached += 8; - } - this.bitsCached = (bitsCached -= n); - this.cachedData = cachedData; - this.lastCode = null; - return (cachedData >>> bitsCached) & ((1 << n) - 1); - }; - - LZWStream.prototype.readBlock = function LZWStream_readBlock() { - var blockSize = 512; - var estimatedDecodedSize = blockSize * 2, decodedSizeDelta = blockSize; - var i, j, q; - - var lzwState = this.lzwState; - if (!lzwState) { - return; // eof was found - } - - var earlyChange = lzwState.earlyChange; - var nextCode = lzwState.nextCode; - var dictionaryValues = lzwState.dictionaryValues; - var dictionaryLengths = lzwState.dictionaryLengths; - var dictionaryPrevCodes = lzwState.dictionaryPrevCodes; - var codeLength = lzwState.codeLength; - var prevCode = lzwState.prevCode; - var currentSequence = lzwState.currentSequence; - var currentSequenceLength = lzwState.currentSequenceLength; - - var decodedLength = 0; - var currentBufferLength = this.bufferLength; - var buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize); - - for (i = 0; i < blockSize; i++) { - var code = this.readBits(codeLength); - var hasPrev = currentSequenceLength > 0; - if (code < 256) { - currentSequence[0] = code; - currentSequenceLength = 1; - } else if (code >= 258) { - if (code < nextCode) { - currentSequenceLength = dictionaryLengths[code]; - for (j = currentSequenceLength - 1, q = code; j >= 0; j--) { - currentSequence[j] = dictionaryValues[q]; - q = dictionaryPrevCodes[q]; - } - } else { - currentSequence[currentSequenceLength++] = currentSequence[0]; - } - } else if (code === 256) { - codeLength = 9; - nextCode = 258; - currentSequenceLength = 0; - continue; - } else { - this.eof = true; - delete this.lzwState; - break; - } - - if (hasPrev) { - dictionaryPrevCodes[nextCode] = prevCode; - dictionaryLengths[nextCode] = dictionaryLengths[prevCode] + 1; - dictionaryValues[nextCode] = currentSequence[0]; - nextCode++; - codeLength = (nextCode + earlyChange) & (nextCode + earlyChange - 1) ? - codeLength : Math.min(Math.log(nextCode + earlyChange) / - 0.6931471805599453 + 1, 12) | 0; - } - prevCode = code; - - decodedLength += currentSequenceLength; - if (estimatedDecodedSize < decodedLength) { - do { - estimatedDecodedSize += decodedSizeDelta; - } while (estimatedDecodedSize < decodedLength); - buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize); - } - for (j = 0; j < currentSequenceLength; j++) { - buffer[currentBufferLength++] = currentSequence[j]; - } - } - lzwState.nextCode = nextCode; - lzwState.codeLength = codeLength; - lzwState.prevCode = prevCode; - lzwState.currentSequenceLength = currentSequenceLength; - - this.bufferLength = currentBufferLength; - }; - - return LZWStream; -})(); - -var NullStream = (function NullStreamClosure() { - function NullStream() { - Stream.call(this, new Uint8Array(0)); - } - - NullStream.prototype = Stream.prototype; - - return NullStream; -})(); - -// TODO refactor to remove dependency on parser.js -function _setCoreParser(coreParser_) { - coreParser = coreParser_; - EOF = coreParser_.EOF; - Lexer = coreParser_.Lexer; -} -exports._setCoreParser = _setCoreParser; - -// TODO refactor to remove dependency on colorspace.js -function _setCoreColorSpace(coreColorSpace_) { - coreColorSpace = coreColorSpace_; - ColorSpace = coreColorSpace_.ColorSpace; -} -exports._setCoreColorSpace = _setCoreColorSpace; - -exports.Ascii85Stream = Ascii85Stream; -exports.AsciiHexStream = AsciiHexStream; -exports.CCITTFaxStream = CCITTFaxStream; -exports.DecryptStream = DecryptStream; -exports.DecodeStream = DecodeStream; -exports.FlateStream = FlateStream; -exports.Jbig2Stream = Jbig2Stream; -exports.JpegStream = JpegStream; -exports.JpxStream = JpxStream; -exports.NullStream = NullStream; -exports.PredictorStream = PredictorStream; -exports.RunLengthStream = RunLengthStream; -exports.Stream = Stream; -exports.StreamsSequenceStream = StreamsSequenceStream; -exports.StringStream = StringStream; -exports.LZWStream = LZWStream; -})); - -/* Copyright 2012 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -(function (root, factory) { - //if (typeof define === 'function' && define.amd) { - // define('pdfjs/core/parser', ['exports', 'pdfjs/shared/util', - // 'pdfjs/core/primitives', 'pdfjs/core/stream'], factory); - // } else if (typeof exports !== 'undefined') { - // factory(exports, require('../shared/util.js'), require('./primitives.js'), - // require('./stream.js')); - //} else { - factory((root.pdfjsCoreParser = {}), root.pdfjsSharedUtil, - root.pdfjsCorePrimitives, root.pdfjsCoreStream); - //} -}(window, function (exports, sharedUtil, corePrimitives, coreStream) { - -var MissingDataException = sharedUtil.MissingDataException; -var StreamType = sharedUtil.StreamType; -var assert = sharedUtil.assert; -var error = sharedUtil.error; -var info = sharedUtil.info; -var isArray = sharedUtil.isArray; -var isInt = sharedUtil.isInt; -var isNum = sharedUtil.isNum; -var isString = sharedUtil.isString; -var warn = sharedUtil.warn; -var Cmd = corePrimitives.Cmd; -var Dict = corePrimitives.Dict; -var Name = corePrimitives.Name; -var Ref = corePrimitives.Ref; -var isCmd = corePrimitives.isCmd; -var isDict = corePrimitives.isDict; -var isName = corePrimitives.isName; -var Ascii85Stream = coreStream.Ascii85Stream; -var AsciiHexStream = coreStream.AsciiHexStream; -var CCITTFaxStream = coreStream.CCITTFaxStream; -var FlateStream = coreStream.FlateStream; -var Jbig2Stream = coreStream.Jbig2Stream; -var JpegStream = coreStream.JpegStream; -var JpxStream = coreStream.JpxStream; -var LZWStream = coreStream.LZWStream; -var NullStream = coreStream.NullStream; -var PredictorStream = coreStream.PredictorStream; -var RunLengthStream = coreStream.RunLengthStream; - -var EOF = {}; - -function isEOF(v) { - return (v === EOF); -} - -var MAX_LENGTH_TO_CACHE = 1000; - -var Parser = (function ParserClosure() { - function Parser(lexer, allowStreams, xref) { - this.lexer = lexer; - this.allowStreams = allowStreams; - this.xref = xref; - this.imageCache = {}; - this.refill(); - } - - Parser.prototype = { - refill: function Parser_refill() { - this.buf1 = this.lexer.getObj(); - this.buf2 = this.lexer.getObj(); - }, - shift: function Parser_shift() { - if (isCmd(this.buf2, 'ID')) { - this.buf1 = this.buf2; - this.buf2 = null; - } else { - this.buf1 = this.buf2; - this.buf2 = this.lexer.getObj(); - } - }, - tryShift: function Parser_tryShift() { - try { - this.shift(); - return true; - } catch (e) { - if (e instanceof MissingDataException) { - throw e; - } - // Upon failure, the caller should reset this.lexer.pos to a known good - // state and call this.shift() twice to reset the buffers. - return false; - } - }, - getObj: function Parser_getObj(cipherTransform) { - var buf1 = this.buf1; - this.shift(); - - if (buf1 instanceof Cmd) { - switch (buf1.cmd) { - case 'BI': // inline image - return this.makeInlineImage(cipherTransform); - case '[': // array - var array = []; - while (!isCmd(this.buf1, ']') && !isEOF(this.buf1)) { - array.push(this.getObj(cipherTransform)); - } - if (isEOF(this.buf1)) { - error('End of file inside array'); - } - this.shift(); - return array; - case '<<': // dictionary or stream - var dict = new Dict(this.xref); - while (!isCmd(this.buf1, '>>') && !isEOF(this.buf1)) { - if (!isName(this.buf1)) { - info('Malformed dictionary: key must be a name object'); - this.shift(); - continue; - } - var pos = this.lexer.stream.pos; - var key = this.buf1.name; - dict.set('#' +key+ '_offset', pos); - this.shift(); - if (isEOF(this.buf1)) { - break; - } - dict.set(key, this.getObj(cipherTransform)); - - } - if (isEOF(this.buf1)) { - error('End of file inside dictionary'); - } - - // Stream objects are not allowed inside content streams or - // object streams. - if (isCmd(this.buf2, 'stream')) { - return (this.allowStreams ? - this.makeStream(dict, cipherTransform) : dict); - } - this.shift(); - return dict; - default: // simple object - return buf1; - } - } - - if (isInt(buf1)) { // indirect reference or integer - var num = buf1; - if (isInt(this.buf1) && isCmd(this.buf2, 'R')) { - var ref = new Ref(num, this.buf1); - this.shift(); - this.shift(); - return ref; - } - return num; - } - - if (isString(buf1)) { // string - var str = buf1; - if (cipherTransform) { - str = cipherTransform.decryptString(str); - } - return str; - } - - // simple object - return buf1; - }, - /** - * Find the end of the stream by searching for the /EI\s/. - * @returns {number} The inline stream length. - */ - findDefaultInlineStreamEnd: - function Parser_findDefaultInlineStreamEnd(stream) { - var E = 0x45, I = 0x49, SPACE = 0x20, LF = 0xA, CR = 0xD; - var startPos = stream.pos, state = 0, ch, i, n, followingBytes; - while ((ch = stream.getByte()) !== -1) { - if (state === 0) { - state = (ch === E) ? 1 : 0; - } else if (state === 1) { - state = (ch === I) ? 2 : 0; - } else { - assert(state === 2); - if (ch === SPACE || ch === LF || ch === CR) { - // Let's check the next five bytes are ASCII... just be sure. - n = 5; - followingBytes = stream.peekBytes(n); - for (i = 0; i < n; i++) { - ch = followingBytes[i]; - if (ch !== LF && ch !== CR && (ch < SPACE || ch > 0x7F)) { - // Not a LF, CR, SPACE or any visible ASCII character, i.e. - // it's binary stuff. Resetting the state. - state = 0; - break; - } - } - if (state === 2) { - break; // Finished! - } - } else { - state = 0; - } - } - } - return ((stream.pos - 4) - startPos); - }, - /** - * Find the EOI (end-of-image) marker 0xFFD9 of the stream. - * @returns {number} The inline stream length. - */ - findDCTDecodeInlineStreamEnd: - function Parser_findDCTDecodeInlineStreamEnd(stream) { - var startPos = stream.pos, foundEOI = false, b, markerLength, length; - while ((b = stream.getByte()) !== -1) { - if (b !== 0xFF) { // Not a valid marker. - continue; - } - switch (stream.getByte()) { - case 0x00: // Byte stuffing. - // 0xFF00 appears to be a very common byte sequence in JPEG images. - break; - - case 0xFF: // Fill byte. - // Avoid skipping a valid marker, resetting the stream position. - stream.skip(-1); - break; - - case 0xD9: // EOI - foundEOI = true; - break; - - case 0xC0: // SOF0 - case 0xC1: // SOF1 - case 0xC2: // SOF2 - case 0xC3: // SOF3 - - case 0xC5: // SOF5 - case 0xC6: // SOF6 - case 0xC7: // SOF7 - - case 0xC9: // SOF9 - case 0xCA: // SOF10 - case 0xCB: // SOF11 - - case 0xCD: // SOF13 - case 0xCE: // SOF14 - case 0xCF: // SOF15 - - case 0xC4: // DHT - case 0xCC: // DAC - - case 0xDA: // SOS - case 0xDB: // DQT - case 0xDC: // DNL - case 0xDD: // DRI - case 0xDE: // DHP - case 0xDF: // EXP - - case 0xE0: // APP0 - case 0xE1: // APP1 - case 0xE2: // APP2 - case 0xE3: // APP3 - case 0xE4: // APP4 - case 0xE5: // APP5 - case 0xE6: // APP6 - case 0xE7: // APP7 - case 0xE8: // APP8 - case 0xE9: // APP9 - case 0xEA: // APP10 - case 0xEB: // APP11 - case 0xEC: // APP12 - case 0xED: // APP13 - case 0xEE: // APP14 - case 0xEF: // APP15 - - case 0xFE: // COM - // The marker should be followed by the length of the segment. - markerLength = stream.getUint16(); - if (markerLength > 2) { - // |markerLength| contains the byte length of the marker segment, - // including its own length (2 bytes) and excluding the marker. - stream.skip(markerLength - 2); // Jump to the next marker. - } else { - // The marker length is invalid, resetting the stream position. - stream.skip(-2); - } - break; - } - if (foundEOI) { - break; - } - } - length = stream.pos - startPos; - if (b === -1) { - warn('Inline DCTDecode image stream: ' + - 'EOI marker not found, searching for /EI/ instead.'); - stream.skip(-length); // Reset the stream position. - return this.findDefaultInlineStreamEnd(stream); - } - this.inlineStreamSkipEI(stream); - return length; - }, - /** - * Find the EOD (end-of-data) marker '~>' (i.e. TILDE + GT) of the stream. - * @returns {number} The inline stream length. - */ - findASCII85DecodeInlineStreamEnd: - function Parser_findASCII85DecodeInlineStreamEnd(stream) { - var TILDE = 0x7E, GT = 0x3E; - var startPos = stream.pos, ch, length; - while ((ch = stream.getByte()) !== -1) { - if (ch === TILDE && stream.peekByte() === GT) { - stream.skip(); - break; - } - } - length = stream.pos - startPos; - if (ch === -1) { - warn('Inline ASCII85Decode image stream: ' + - 'EOD marker not found, searching for /EI/ instead.'); - stream.skip(-length); // Reset the stream position. - return this.findDefaultInlineStreamEnd(stream); - } - this.inlineStreamSkipEI(stream); - return length; - }, - /** - * Find the EOD (end-of-data) marker '>' (i.e. GT) of the stream. - * @returns {number} The inline stream length. - */ - findASCIIHexDecodeInlineStreamEnd: - function Parser_findASCIIHexDecodeInlineStreamEnd(stream) { - var GT = 0x3E; - var startPos = stream.pos, ch, length; - while ((ch = stream.getByte()) !== -1) { - if (ch === GT) { - break; - } - } - length = stream.pos - startPos; - if (ch === -1) { - warn('Inline ASCIIHexDecode image stream: ' + - 'EOD marker not found, searching for /EI/ instead.'); - stream.skip(-length); // Reset the stream position. - return this.findDefaultInlineStreamEnd(stream); - } - this.inlineStreamSkipEI(stream); - return length; - }, - /** - * Skip over the /EI/ for streams where we search for an EOD marker. - */ - inlineStreamSkipEI: function Parser_inlineStreamSkipEI(stream) { - var E = 0x45, I = 0x49; - var state = 0, ch; - while ((ch = stream.getByte()) !== -1) { - if (state === 0) { - state = (ch === E) ? 1 : 0; - } else if (state === 1) { - state = (ch === I) ? 2 : 0; - } else if (state === 2) { - break; - } - } - }, - makeInlineImage: function Parser_makeInlineImage(cipherTransform) { - var lexer = this.lexer; - var stream = lexer.stream; - - // Parse dictionary. - var dict = new Dict(this.xref); - while (!isCmd(this.buf1, 'ID') && !isEOF(this.buf1)) { - if (!isName(this.buf1)) { - error('Dictionary key must be a name object'); - } - var key = this.buf1.name; - this.shift(); - if (isEOF(this.buf1)) { - break; - } - dict.set(key, this.getObj(cipherTransform)); - } - - // Extract the name of the first (i.e. the current) image filter. - var filter = dict.get('Filter', 'F'), filterName; - if (isName(filter)) { - filterName = filter.name; - } else if (isArray(filter) && isName(filter[0])) { - filterName = filter[0].name; - } - - // Parse image stream. - var startPos = stream.pos, length, i, ii; - if (filterName === 'DCTDecode' || filterName === 'DCT') { - length = this.findDCTDecodeInlineStreamEnd(stream); - } else if (filterName === 'ASCII85Decide' || filterName === 'A85') { - length = this.findASCII85DecodeInlineStreamEnd(stream); - } else if (filterName === 'ASCIIHexDecode' || filterName === 'AHx') { - length = this.findASCIIHexDecodeInlineStreamEnd(stream); - } else { - length = this.findDefaultInlineStreamEnd(stream); - } - var imageStream = stream.makeSubStream(startPos, length, dict); - - // Cache all images below the MAX_LENGTH_TO_CACHE threshold by their - // adler32 checksum. - var adler32; - if (length < MAX_LENGTH_TO_CACHE) { - var imageBytes = imageStream.getBytes(); - imageStream.reset(); - - var a = 1; - var b = 0; - for (i = 0, ii = imageBytes.length; i < ii; ++i) { - // No modulo required in the loop if imageBytes.length < 5552. - a += imageBytes[i] & 0xff; - b += a; - } - adler32 = ((b % 65521) << 16) | (a % 65521); - - if (this.imageCache.adler32 === adler32) { - this.buf2 = Cmd.get('EI'); - this.shift(); - - this.imageCache[adler32].reset(); - return this.imageCache[adler32]; - } - } - - if (cipherTransform) { - imageStream = cipherTransform.createStream(imageStream, length); - } - - imageStream = this.filter(imageStream, dict, length); - imageStream.dict = dict; - if (adler32 !== undefined) { - imageStream.cacheKey = 'inline_' + length + '_' + adler32; - this.imageCache[adler32] = imageStream; - } - - this.buf2 = Cmd.get('EI'); - this.shift(); - - return imageStream; - }, - makeStream: function Parser_makeStream(dict, cipherTransform) { - var lexer = this.lexer; - var stream = lexer.stream; - - // get stream start position - lexer.skipToNextLine(); - var pos = stream.pos - 1; - - // get length - var length = dict.get('Length'); - if (!isInt(length)) { - info('Bad ' + length + ' attribute in stream'); - length = 0; - } - - // skip over the stream data - stream.pos = pos + length; - lexer.nextChar(); - - // Shift '>>' and check whether the new object marks the end of the stream - if (this.tryShift() && isCmd(this.buf2, 'endstream')) { - this.shift(); // 'stream' - } else { - // bad stream length, scanning for endstream - stream.pos = pos; - var SCAN_BLOCK_SIZE = 2048; - var ENDSTREAM_SIGNATURE_LENGTH = 9; - var ENDSTREAM_SIGNATURE = [0x65, 0x6E, 0x64, 0x73, 0x74, 0x72, 0x65, - 0x61, 0x6D]; - var skipped = 0, found = false, i, j; - while (stream.pos < stream.end) { - var scanBytes = stream.peekBytes(SCAN_BLOCK_SIZE); - var scanLength = scanBytes.length - ENDSTREAM_SIGNATURE_LENGTH; - if (scanLength <= 0) { - break; - } - found = false; - for (i = 0, j = 0; i < scanLength; i++) { - var b = scanBytes[i]; - if (b !== ENDSTREAM_SIGNATURE[j]) { - i -= j; - j = 0; - } else { - j++; - if (j >= ENDSTREAM_SIGNATURE_LENGTH) { - i++; - found = true; - break; - } - } - } - if (found) { - skipped += i - ENDSTREAM_SIGNATURE_LENGTH; - stream.pos += i - ENDSTREAM_SIGNATURE_LENGTH; - break; - } - skipped += scanLength; - stream.pos += scanLength; - } - if (!found) { - error('Missing endstream'); - } - length = skipped; - - lexer.nextChar(); - this.shift(); - this.shift(); - } - this.shift(); // 'endstream' - - stream = stream.makeSubStream(pos, length, dict); - if (cipherTransform) { - stream = cipherTransform.createStream(stream, length); - } - stream = this.filter(stream, dict, length); - stream.dict = dict; - return stream; - }, - filter: function Parser_filter(stream, dict, length) { - var filter = dict.get('Filter', 'F'); - var params = dict.get('DecodeParms', 'DP'); - if (isName(filter)) { - return this.makeFilter(stream, filter.name, length, params); - } - - var maybeLength = length; - if (isArray(filter)) { - var filterArray = filter; - var paramsArray = params; - for (var i = 0, ii = filterArray.length; i < ii; ++i) { - filter = filterArray[i]; - if (!isName(filter)) { - error('Bad filter name: ' + filter); - } - - params = null; - if (isArray(paramsArray) && (i in paramsArray)) { - params = paramsArray[i]; - } - stream = this.makeFilter(stream, filter.name, maybeLength, params); - // after the first stream the length variable is invalid - maybeLength = null; - } - } - return stream; - }, - makeFilter: function Parser_makeFilter(stream, name, maybeLength, params) { - if (stream.dict.get('Length') === 0 && !maybeLength) { - warn('Empty "' + name + '" stream.'); - return new NullStream(stream); - } - try { - if (params && this.xref) { - params = this.xref.fetchIfRef(params); - } - var xrefStreamStats = this.xref.stats.streamTypes; - if (name === 'FlateDecode' || name === 'Fl') { - xrefStreamStats[StreamType.FLATE] = true; - if (params) { - return new PredictorStream(new FlateStream(stream, maybeLength), - maybeLength, params); - } - return new FlateStream(stream, maybeLength); - } - if (name === 'LZWDecode' || name === 'LZW') { - xrefStreamStats[StreamType.LZW] = true; - var earlyChange = 1; - if (params) { - if (params.has('EarlyChange')) { - earlyChange = params.get('EarlyChange'); - } - return new PredictorStream( - new LZWStream(stream, maybeLength, earlyChange), - maybeLength, params); - } - return new LZWStream(stream, maybeLength, earlyChange); - } - if (name === 'DCTDecode' || name === 'DCT') { - xrefStreamStats[StreamType.DCT] = true; - return new JpegStream(stream, maybeLength, stream.dict, this.xref); - } - if (name === 'JPXDecode' || name === 'JPX') { - xrefStreamStats[StreamType.JPX] = true; - return new JpxStream(stream, maybeLength, stream.dict); - } - if (name === 'ASCII85Decode' || name === 'A85') { - xrefStreamStats[StreamType.A85] = true; - return new Ascii85Stream(stream, maybeLength); - } - if (name === 'ASCIIHexDecode' || name === 'AHx') { - xrefStreamStats[StreamType.AHX] = true; - return new AsciiHexStream(stream, maybeLength); - } - if (name === 'CCITTFaxDecode' || name === 'CCF') { - xrefStreamStats[StreamType.CCF] = true; - return new CCITTFaxStream(stream, maybeLength, params); - } - if (name === 'RunLengthDecode' || name === 'RL') { - xrefStreamStats[StreamType.RL] = true; - return new RunLengthStream(stream, maybeLength); - } - if (name === 'JBIG2Decode') { - xrefStreamStats[StreamType.JBIG] = true; - return new Jbig2Stream(stream, maybeLength, stream.dict); - } - warn('filter "' + name + '" not supported yet'); - return stream; - } catch (ex) { - if (ex instanceof MissingDataException) { - throw ex; - } - warn('Invalid stream: \"' + ex + '\"'); - return new NullStream(stream); - } - } - }; - - return Parser; -})(); - -var Lexer = (function LexerClosure() { - function Lexer(stream, knownCommands) { - this.stream = stream; - this.nextChar(); - - // While lexing, we build up many strings one char at a time. Using += for - // this can result in lots of garbage strings. It's better to build an - // array of single-char strings and then join() them together at the end. - // And reusing a single array (i.e. |this.strBuf|) over and over for this - // purpose uses less memory than using a new array for each string. - this.strBuf = []; - - // The PDFs might have "glued" commands with other commands, operands or - // literals, e.g. "q1". The knownCommands is a dictionary of the valid - // commands and their prefixes. The prefixes are built the following way: - // if there a command that is a prefix of the other valid command or - // literal (e.g. 'f' and 'false') the following prefixes must be included, - // 'fa', 'fal', 'fals'. The prefixes are not needed, if the command has no - // other commands or literals as a prefix. The knowCommands is optional. - this.knownCommands = knownCommands; - } - - Lexer.isSpace = function Lexer_isSpace(ch) { - // Space is one of the following characters: SPACE, TAB, CR or LF. - return (ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A); - }; - - // A '1' in this array means the character is white space. A '1' or - // '2' means the character ends a name or command. - var specialChars = [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, // 2x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 3x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 5x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ax - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // bx - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // cx - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // dx - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ex - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx - ]; - - function toHexDigit(ch) { - if (ch >= 0x30 && ch <= 0x39) { // '0'-'9' - return ch & 0x0F; - } - if ((ch >= 0x41 && ch <= 0x46) || (ch >= 0x61 && ch <= 0x66)) { - // 'A'-'F', 'a'-'f' - return (ch & 0x0F) + 9; - } - return -1; - } - - Lexer.prototype = { - nextChar: function Lexer_nextChar() { - return (this.currentChar = this.stream.getByte()); - }, - peekChar: function Lexer_peekChar() { - return this.stream.peekByte(); - }, - getNumber: function Lexer_getNumber() { - var ch = this.currentChar; - var eNotation = false; - var divideBy = 0; // different from 0 if it's a floating point value - var sign = 1; - - if (ch === 0x2D) { // '-' - sign = -1; - ch = this.nextChar(); - - if (ch === 0x2D) { // '-' - // Ignore double negative (this is consistent with Adobe Reader). - ch = this.nextChar(); - } - } else if (ch === 0x2B) { // '+' - ch = this.nextChar(); - } - if (ch === 0x2E) { // '.' - divideBy = 10; - ch = this.nextChar(); - } - if (ch < 0x30 || ch > 0x39) { // '0' - '9' - error('Invalid number: ' + String.fromCharCode(ch)); - return 0; - } - - var baseValue = ch - 0x30; // '0' - var powerValue = 0; - var powerValueSign = 1; - - while ((ch = this.nextChar()) >= 0) { - if (0x30 <= ch && ch <= 0x39) { // '0' - '9' - var currentDigit = ch - 0x30; // '0' - if (eNotation) { // We are after an 'e' or 'E' - powerValue = powerValue * 10 + currentDigit; - } else { - if (divideBy !== 0) { // We are after a point - divideBy *= 10; - } - baseValue = baseValue * 10 + currentDigit; - } - } else if (ch === 0x2E) { // '.' - if (divideBy === 0) { - divideBy = 1; - } else { - // A number can have only one '.' - break; - } - } else if (ch === 0x2D) { // '-' - // ignore minus signs in the middle of numbers to match - // Adobe's behavior - warn('Badly formated number'); - } else if (ch === 0x45 || ch === 0x65) { // 'E', 'e' - // 'E' can be either a scientific notation or the beginning of a new - // operator - ch = this.peekChar(); - if (ch === 0x2B || ch === 0x2D) { // '+', '-' - powerValueSign = (ch === 0x2D) ? -1 : 1; - this.nextChar(); // Consume the sign character - } else if (ch < 0x30 || ch > 0x39) { // '0' - '9' - // The 'E' must be the beginning of a new operator - break; - } - eNotation = true; - } else { - // the last character doesn't belong to us - break; - } - } - - if (divideBy !== 0) { - baseValue /= divideBy; - } - if (eNotation) { - baseValue *= Math.pow(10, powerValueSign * powerValue); - } - return sign * baseValue; - }, - getString: function Lexer_getString() { - var numParen = 1; - var done = false; - var strBuf = this.strBuf; - strBuf.length = 0; - - var ch = this.nextChar(); - while (true) { - var charBuffered = false; - switch (ch | 0) { - case -1: - warn('Unterminated string'); - done = true; - break; - case 0x28: // '(' - ++numParen; - strBuf.push('('); - break; - case 0x29: // ')' - if (--numParen === 0) { - this.nextChar(); // consume strings ')' - done = true; - } else { - strBuf.push(')'); - } - break; - case 0x5C: // '\\' - ch = this.nextChar(); - switch (ch) { - case -1: - warn('Unterminated string'); - done = true; - break; - case 0x6E: // 'n' - strBuf.push('\n'); - break; - case 0x72: // 'r' - strBuf.push('\r'); - break; - case 0x74: // 't' - strBuf.push('\t'); - break; - case 0x62: // 'b' - strBuf.push('\b'); - break; - case 0x66: // 'f' - strBuf.push('\f'); - break; - case 0x5C: // '\' - case 0x28: // '(' - case 0x29: // ')' - strBuf.push(String.fromCharCode(ch)); - break; - case 0x30: case 0x31: case 0x32: case 0x33: // '0'-'3' - case 0x34: case 0x35: case 0x36: case 0x37: // '4'-'7' - var x = ch & 0x0F; - ch = this.nextChar(); - charBuffered = true; - if (ch >= 0x30 && ch <= 0x37) { // '0'-'7' - x = (x << 3) + (ch & 0x0F); - ch = this.nextChar(); - if (ch >= 0x30 && ch <= 0x37) { // '0'-'7' - charBuffered = false; - x = (x << 3) + (ch & 0x0F); - } - } - strBuf.push(String.fromCharCode(x)); - break; - case 0x0D: // CR - if (this.peekChar() === 0x0A) { // LF - this.nextChar(); - } - break; - case 0x0A: // LF - break; - default: - strBuf.push(String.fromCharCode(ch)); - break; - } - break; - default: - strBuf.push(String.fromCharCode(ch)); - break; - } - if (done) { - break; - } - if (!charBuffered) { - ch = this.nextChar(); - } - } - return strBuf.join(''); - }, - getName: function Lexer_getName() { - var ch, previousCh; - var strBuf = this.strBuf; - strBuf.length = 0; - while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) { - if (ch === 0x23) { // '#' - ch = this.nextChar(); - if (specialChars[ch]) { - warn('Lexer_getName: ' + - 'NUMBER SIGN (#) should be followed by a hexadecimal number.'); - strBuf.push('#'); - break; - } - var x = toHexDigit(ch); - if (x !== -1) { - previousCh = ch; - ch = this.nextChar(); - var x2 = toHexDigit(ch); - if (x2 === -1) { - warn('Lexer_getName: Illegal digit (' + - String.fromCharCode(ch) +') in hexadecimal number.'); - strBuf.push('#', String.fromCharCode(previousCh)); - if (specialChars[ch]) { - break; - } - strBuf.push(String.fromCharCode(ch)); - continue; - } - strBuf.push(String.fromCharCode((x << 4) | x2)); - } else { - strBuf.push('#', String.fromCharCode(ch)); - } - } else { - strBuf.push(String.fromCharCode(ch)); - } - } - if (strBuf.length > 127) { - warn('name token is longer than allowed by the spec: ' + strBuf.length); - } - return Name.get(strBuf.join('')); - }, - getHexString: function Lexer_getHexString() { - var strBuf = this.strBuf; - strBuf.length = 0; - var ch = this.currentChar; - var isFirstHex = true; - var firstDigit; - var secondDigit; - while (true) { - if (ch < 0) { - warn('Unterminated hex string'); - break; - } else if (ch === 0x3E) { // '>' - this.nextChar(); - break; - } else if (specialChars[ch] === 1) { - ch = this.nextChar(); - continue; - } else { - if (isFirstHex) { - firstDigit = toHexDigit(ch); - if (firstDigit === -1) { - warn('Ignoring invalid character "' + ch + '" in hex string'); - ch = this.nextChar(); - continue; - } - } else { - secondDigit = toHexDigit(ch); - if (secondDigit === -1) { - warn('Ignoring invalid character "' + ch + '" in hex string'); - ch = this.nextChar(); - continue; - } - strBuf.push(String.fromCharCode((firstDigit << 4) | secondDigit)); - } - isFirstHex = !isFirstHex; - ch = this.nextChar(); - } - } - return strBuf.join(''); - }, - getObj: function Lexer_getObj() { - // skip whitespace and comments - var comment = false; - var ch = this.currentChar; - while (true) { - if (ch < 0) { - return EOF; - } - if (comment) { - if (ch === 0x0A || ch === 0x0D) { // LF, CR - comment = false; - } - } else if (ch === 0x25) { // '%' - comment = true; - } else if (specialChars[ch] !== 1) { - break; - } - ch = this.nextChar(); - } - - // start reading token - switch (ch | 0) { - case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // '0'-'4' - case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: // '5'-'9' - case 0x2B: case 0x2D: case 0x2E: // '+', '-', '.' - return this.getNumber(); - case 0x28: // '(' - return this.getString(); - case 0x2F: // '/' - return this.getName(); - // array punctuation - case 0x5B: // '[' - this.nextChar(); - return Cmd.get('['); - case 0x5D: // ']' - this.nextChar(); - return Cmd.get(']'); - // hex string or dict punctuation - case 0x3C: // '<' - ch = this.nextChar(); - if (ch === 0x3C) { - // dict punctuation - this.nextChar(); - return Cmd.get('<<'); - } - return this.getHexString(); - // dict punctuation - case 0x3E: // '>' - ch = this.nextChar(); - if (ch === 0x3E) { - this.nextChar(); - return Cmd.get('>>'); - } - return Cmd.get('>'); - case 0x7B: // '{' - this.nextChar(); - return Cmd.get('{'); - case 0x7D: // '}' - this.nextChar(); - return Cmd.get('}'); - case 0x29: // ')' - error('Illegal character: ' + ch); - break; - } - - // command - var str = String.fromCharCode(ch); - var knownCommands = this.knownCommands; - var knownCommandFound = knownCommands && knownCommands[str] !== undefined; - while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) { - // stop if known command is found and next character does not make - // the str a command - var possibleCommand = str + String.fromCharCode(ch); - if (knownCommandFound && knownCommands[possibleCommand] === undefined) { - break; - } - if (str.length === 128) { - error('Command token too long: ' + str.length); - } - str = possibleCommand; - knownCommandFound = knownCommands && knownCommands[str] !== undefined; - } - if (str === 'true') { - return true; - } - if (str === 'false') { - return false; - } - if (str === 'null') { - return null; - } - return Cmd.get(str); - }, - skipToNextLine: function Lexer_skipToNextLine() { - var ch = this.currentChar; - while (ch >= 0) { - if (ch === 0x0D) { // CR - ch = this.nextChar(); - if (ch === 0x0A) { // LF - this.nextChar(); - } - break; - } else if (ch === 0x0A) { // LF - this.nextChar(); - break; - } - ch = this.nextChar(); - } - } - }; - - return Lexer; -})(); - -var Linearization = { - create: function LinearizationCreate(stream) { - function getInt(name, allowZeroValue) { - var obj = linDict.get(name); - if (isInt(obj) && (allowZeroValue ? obj >= 0 : obj > 0)) { - return obj; - } - throw new Error('The "' + name + '" parameter in the linearization ' + - 'dictionary is invalid.'); - } - function getHints() { - var hints = linDict.get('H'), hintsLength, item; - if (isArray(hints) && - ((hintsLength = hints.length) === 2 || hintsLength === 4)) { - for (var index = 0; index < hintsLength; index++) { - if (!(isInt(item = hints[index]) && item > 0)) { - throw new Error('Hint (' + index + - ') in the linearization dictionary is invalid.'); - } - } - return hints; - } - throw new Error('Hint array in the linearization dictionary is invalid.'); - } - var parser = new Parser(new Lexer(stream), false, null); - var obj1 = parser.getObj(); - var obj2 = parser.getObj(); - var obj3 = parser.getObj(); - var linDict = parser.getObj(); - var obj, length; - if (!(isInt(obj1) && isInt(obj2) && isCmd(obj3, 'obj') && isDict(linDict) && - isNum(obj = linDict.get('Linearized')) && obj > 0)) { - return null; // No valid linearization dictionary found. - } else if ((length = getInt('L')) !== stream.length) { - throw new Error('The "L" parameter in the linearization dictionary ' + - 'does not equal the stream length.'); - } - return { - length: length, - hints: getHints(), - objectNumberFirst: getInt('O'), - endFirst: getInt('E'), - numPages: getInt('N'), - mainXRefEntriesOffset: getInt('T'), - pageFirst: (linDict.has('P') ? getInt('P', true) : 0) - }; - } -}; - -exports.EOF = EOF; -exports.Lexer = Lexer; -exports.Linearization = Linearization; -exports.Parser = Parser; -exports.isEOF = isEOF; - -// TODO refactor to remove dependency on stream.js -coreStream._setCoreParser(exports); -})); - -/* Copyright 2012 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -(function (root, factory) { - //if (typeof define === 'function' && define.amd) { - // define('pdfjs/core/crypto', ['exports', 'pdfjs/shared/util', - // 'pdfjs/core/primitives', 'pdfjs/core/stream'], factory); - // } else if (typeof exports !== 'undefined') { - // factory(exports, require('../shared/util.js'), require('./primitives.js'), - // require('./stream.js')); - //} else { - factory((root.pdfjsCoreCrypto = {}), root.pdfjsSharedUtil, - root.pdfjsCorePrimitives, root.pdfjsCoreStream); - //} -}(window, function (exports, sharedUtil, corePrimitives, coreStream) { - -var PasswordException = sharedUtil.PasswordException; -var PasswordResponses = sharedUtil.PasswordResponses; -var bytesToString = sharedUtil.bytesToString; -var error = sharedUtil.error; -var isInt = sharedUtil.isInt; -var stringToBytes = sharedUtil.stringToBytes; -var utf8StringToString = sharedUtil.utf8StringToString; -var warn = sharedUtil.warn; -var Name = corePrimitives.Name; -var isName = corePrimitives.isName; -var DecryptStream = coreStream.DecryptStream; - -var ARCFourCipher = (function ARCFourCipherClosure() { - function ARCFourCipher(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; - } - - ARCFourCipher.prototype = { - encryptBlock: function ARCFourCipher_encryptBlock(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) { - 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; - } - }; - ARCFourCipher.prototype.decryptBlock = ARCFourCipher.prototype.encryptBlock; - - return ARCFourCipher; -})(); - -var calculateMD5 = (function calculateMD5Closure() { - var 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]); - - var 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; - while (i < n) { - 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; - 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 Word64 = (function Word64Closure() { - function Word64(highInteger, lowInteger) { - this.high = highInteger | 0; - this.low = lowInteger | 0; - } - Word64.prototype = { - and: function Word64_and(word) { - this.high &= word.high; - this.low &= word.low; - }, - xor: function Word64_xor(word) { - this.high ^= word.high; - this.low ^= word.low; - }, - - or: function Word64_or(word) { - this.high |= word.high; - this.low |= word.low; - }, - - shiftRight: function Word64_shiftRight(places) { - if (places >= 32) { - this.low = (this.high >>> (places - 32)) | 0; - this.high = 0; - } else { - this.low = (this.low >>> places) | (this.high << (32 - places)); - this.high = (this.high >>> places) | 0; - } - }, - - shiftLeft: function Word64_shiftLeft(places) { - if (places >= 32) { - this.high = this.low << (places - 32); - this.low = 0; - } else { - this.high = (this.high << places) | (this.low >>> (32 - places)); - this.low = this.low << places; - } - }, - - rotateRight: function Word64_rotateRight(places) { - var low, high; - if (places & 32) { - high = this.low; - low = this.high; - } else { - low = this.low; - high = this.high; - } - places &= 31; - this.low = (low >>> places) | (high << (32 - places)); - this.high = (high >>> places) | (low << (32 - places)); - }, - - not: function Word64_not() { - this.high = ~this.high; - this.low = ~this.low; - }, - - add: function Word64_add(word) { - var lowAdd = (this.low >>> 0) + (word.low >>> 0); - var highAdd = (this.high >>> 0) + (word.high >>> 0); - if (lowAdd > 0xFFFFFFFF) { - highAdd += 1; - } - this.low = lowAdd | 0; - this.high = highAdd | 0; - }, - - copyTo: function Word64_copyTo(bytes, offset) { - bytes[offset] = (this.high >>> 24) & 0xFF; - bytes[offset + 1] = (this.high >> 16) & 0xFF; - bytes[offset + 2] = (this.high >> 8) & 0xFF; - bytes[offset + 3] = this.high & 0xFF; - bytes[offset + 4] = (this.low >>> 24) & 0xFF; - bytes[offset + 5] = (this.low >> 16) & 0xFF; - bytes[offset + 6] = (this.low >> 8) & 0xFF; - bytes[offset + 7] = this.low & 0xFF; - }, - - assign: function Word64_assign(word) { - this.high = word.high; - this.low = word.low; - } - }; - return Word64; -})(); - -var calculateSHA256 = (function calculateSHA256Closure() { - function rotr(x, n) { - return (x >>> n) | (x << 32 - n); - } - - function ch(x, y, z) { - return (x & y) ^ (~x & z); - } - - function maj(x, y, z) { - return (x & y) ^ (x & z) ^ (y & z); - } - - function sigma(x) { - return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22); - } - - function sigmaPrime(x) { - return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25); - } - - function littleSigma(x) { - return rotr(x, 7) ^ rotr(x, 18) ^ x >>> 3; - } - - function littleSigmaPrime(x) { - return rotr(x, 17) ^ rotr(x, 19) ^ x >>> 10; - } - - var k = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2]; - - function hash(data, offset, length) { - // initial hash values - var h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372, - h3 = 0xa54ff53a, h4 = 0x510e527f, h5 = 0x9b05688c, - h6 = 0x1f83d9ab, h7 = 0x5be0cd19; - // pre-processing - var paddedLength = Math.ceil((length + 9) / 64) * 64; - 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; - while (i < n) { - padded[i++] = 0; - } - padded[i++] = 0; - padded[i++] = 0; - padded[i++] = 0; - padded[i++] = (length >>> 29) & 0xFF; - padded[i++] = (length >> 21) & 0xFF; - padded[i++] = (length >> 13) & 0xFF; - padded[i++] = (length >> 5) & 0xFF; - padded[i++] = (length << 3) & 0xFF; - var w = new Uint32Array(64); - // for each 512 bit block - for (i = 0; i < paddedLength;) { - for (j = 0; j < 16; ++j) { - w[j] = (padded[i] << 24 | (padded[i + 1] << 16) | - (padded[i + 2] << 8) | (padded[i + 3])); - i += 4; - } - - for (j = 16; j < 64; ++j) { - w[j] = littleSigmaPrime(w[j - 2]) + w[j - 7] + - littleSigma(w[j - 15]) + w[j - 16] | 0; - } - var a = h0, b = h1, c = h2, d = h3, e = h4, - f = h5, g = h6, h = h7, t1, t2; - for (j = 0; j < 64; ++j) { - t1 = h + sigmaPrime(e) + ch(e, f, g) + k[j] + w[j]; - t2 = sigma(a) + maj(a, b, c); - h = g; - g = f; - f = e; - e = (d + t1) | 0; - d = c; - c = b; - b = a; - a = (t1 + t2) | 0; - } - h0 = (h0 + a) | 0; - h1 = (h1 + b) | 0; - h2 = (h2 + c) | 0; - h3 = (h3 + d) | 0; - h4 = (h4 + e) | 0; - h5 = (h5 + f) | 0; - h6 = (h6 + g) | 0; - h7 = (h7 + h) | 0; - } - return new Uint8Array([ - (h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, (h0) & 0xFF, - (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, (h1) & 0xFF, - (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, (h2) & 0xFF, - (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, (h3) & 0xFF, - (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, (h4) & 0xFF, - (h5 >> 24) & 0xFF, (h5 >> 16) & 0xFF, (h5 >> 8) & 0xFF, (h5) & 0xFF, - (h6 >> 24) & 0xFF, (h6 >> 16) & 0xFF, (h6 >> 8) & 0xFF, (h6) & 0xFF, - (h7 >> 24) & 0xFF, (h7 >> 16) & 0xFF, (h7 >> 8) & 0xFF, (h7) & 0xFF - ]); - } - - return hash; -})(); - -var calculateSHA512 = (function calculateSHA512Closure() { - function ch(result, x, y, z, tmp) { - result.assign(x); - result.and(y); - tmp.assign(x); - tmp.not(); - tmp.and(z); - result.xor(tmp); - } - - function maj(result, x, y, z, tmp) { - result.assign(x); - result.and(y); - tmp.assign(x); - tmp.and(z); - result.xor(tmp); - tmp.assign(y); - tmp.and(z); - result.xor(tmp); - } - - function sigma(result, x, tmp) { - result.assign(x); - result.rotateRight(28); - tmp.assign(x); - tmp.rotateRight(34); - result.xor(tmp); - tmp.assign(x); - tmp.rotateRight(39); - result.xor(tmp); - } - - function sigmaPrime(result, x, tmp) { - result.assign(x); - result.rotateRight(14); - tmp.assign(x); - tmp.rotateRight(18); - result.xor(tmp); - tmp.assign(x); - tmp.rotateRight(41); - result.xor(tmp); - } - - function littleSigma(result, x, tmp) { - result.assign(x); - result.rotateRight(1); - tmp.assign(x); - tmp.rotateRight(8); - result.xor(tmp); - tmp.assign(x); - tmp.shiftRight(7); - result.xor(tmp); - } - - function littleSigmaPrime(result, x, tmp) { - result.assign(x); - result.rotateRight(19); - tmp.assign(x); - tmp.rotateRight(61); - result.xor(tmp); - tmp.assign(x); - tmp.shiftRight(6); - result.xor(tmp); - } - - var k = [ - new Word64(0x428a2f98, 0xd728ae22), new Word64(0x71374491, 0x23ef65cd), - new Word64(0xb5c0fbcf, 0xec4d3b2f), new Word64(0xe9b5dba5, 0x8189dbbc), - new Word64(0x3956c25b, 0xf348b538), new Word64(0x59f111f1, 0xb605d019), - new Word64(0x923f82a4, 0xaf194f9b), new Word64(0xab1c5ed5, 0xda6d8118), - new Word64(0xd807aa98, 0xa3030242), new Word64(0x12835b01, 0x45706fbe), - new Word64(0x243185be, 0x4ee4b28c), new Word64(0x550c7dc3, 0xd5ffb4e2), - new Word64(0x72be5d74, 0xf27b896f), new Word64(0x80deb1fe, 0x3b1696b1), - new Word64(0x9bdc06a7, 0x25c71235), new Word64(0xc19bf174, 0xcf692694), - new Word64(0xe49b69c1, 0x9ef14ad2), new Word64(0xefbe4786, 0x384f25e3), - new Word64(0x0fc19dc6, 0x8b8cd5b5), new Word64(0x240ca1cc, 0x77ac9c65), - new Word64(0x2de92c6f, 0x592b0275), new Word64(0x4a7484aa, 0x6ea6e483), - new Word64(0x5cb0a9dc, 0xbd41fbd4), new Word64(0x76f988da, 0x831153b5), - new Word64(0x983e5152, 0xee66dfab), new Word64(0xa831c66d, 0x2db43210), - new Word64(0xb00327c8, 0x98fb213f), new Word64(0xbf597fc7, 0xbeef0ee4), - new Word64(0xc6e00bf3, 0x3da88fc2), new Word64(0xd5a79147, 0x930aa725), - new Word64(0x06ca6351, 0xe003826f), new Word64(0x14292967, 0x0a0e6e70), - new Word64(0x27b70a85, 0x46d22ffc), new Word64(0x2e1b2138, 0x5c26c926), - new Word64(0x4d2c6dfc, 0x5ac42aed), new Word64(0x53380d13, 0x9d95b3df), - new Word64(0x650a7354, 0x8baf63de), new Word64(0x766a0abb, 0x3c77b2a8), - new Word64(0x81c2c92e, 0x47edaee6), new Word64(0x92722c85, 0x1482353b), - new Word64(0xa2bfe8a1, 0x4cf10364), new Word64(0xa81a664b, 0xbc423001), - new Word64(0xc24b8b70, 0xd0f89791), new Word64(0xc76c51a3, 0x0654be30), - new Word64(0xd192e819, 0xd6ef5218), new Word64(0xd6990624, 0x5565a910), - new Word64(0xf40e3585, 0x5771202a), new Word64(0x106aa070, 0x32bbd1b8), - new Word64(0x19a4c116, 0xb8d2d0c8), new Word64(0x1e376c08, 0x5141ab53), - new Word64(0x2748774c, 0xdf8eeb99), new Word64(0x34b0bcb5, 0xe19b48a8), - new Word64(0x391c0cb3, 0xc5c95a63), new Word64(0x4ed8aa4a, 0xe3418acb), - new Word64(0x5b9cca4f, 0x7763e373), new Word64(0x682e6ff3, 0xd6b2b8a3), - new Word64(0x748f82ee, 0x5defb2fc), new Word64(0x78a5636f, 0x43172f60), - new Word64(0x84c87814, 0xa1f0ab72), new Word64(0x8cc70208, 0x1a6439ec), - new Word64(0x90befffa, 0x23631e28), new Word64(0xa4506ceb, 0xde82bde9), - new Word64(0xbef9a3f7, 0xb2c67915), new Word64(0xc67178f2, 0xe372532b), - new Word64(0xca273ece, 0xea26619c), new Word64(0xd186b8c7, 0x21c0c207), - new Word64(0xeada7dd6, 0xcde0eb1e), new Word64(0xf57d4f7f, 0xee6ed178), - new Word64(0x06f067aa, 0x72176fba), new Word64(0x0a637dc5, 0xa2c898a6), - new Word64(0x113f9804, 0xbef90dae), new Word64(0x1b710b35, 0x131c471b), - new Word64(0x28db77f5, 0x23047d84), new Word64(0x32caab7b, 0x40c72493), - new Word64(0x3c9ebe0a, 0x15c9bebc), new Word64(0x431d67c4, 0x9c100d4c), - new Word64(0x4cc5d4be, 0xcb3e42b6), new Word64(0x597f299c, 0xfc657e2a), - new Word64(0x5fcb6fab, 0x3ad6faec), new Word64(0x6c44198c, 0x4a475817)]; - - function hash(data, offset, length, mode384) { - mode384 = !!mode384; - // initial hash values - var h0, h1, h2, h3, h4, h5, h6, h7; - if (!mode384) { - h0 = new Word64(0x6a09e667, 0xf3bcc908); - h1 = new Word64(0xbb67ae85, 0x84caa73b); - h2 = new Word64(0x3c6ef372, 0xfe94f82b); - h3 = new Word64(0xa54ff53a, 0x5f1d36f1); - h4 = new Word64(0x510e527f, 0xade682d1); - h5 = new Word64(0x9b05688c, 0x2b3e6c1f); - h6 = new Word64(0x1f83d9ab, 0xfb41bd6b); - h7 = new Word64(0x5be0cd19, 0x137e2179); - } - else { - // SHA384 is exactly the same - // except with different starting values and a trimmed result - h0 = new Word64(0xcbbb9d5d, 0xc1059ed8); - h1 = new Word64(0x629a292a, 0x367cd507); - h2 = new Word64(0x9159015a, 0x3070dd17); - h3 = new Word64(0x152fecd8, 0xf70e5939); - h4 = new Word64(0x67332667, 0xffc00b31); - h5 = new Word64(0x8eb44a87, 0x68581511); - h6 = new Word64(0xdb0c2e0d, 0x64f98fa7); - h7 = new Word64(0x47b5481d, 0xbefa4fa4); - } - - // pre-processing - var paddedLength = Math.ceil((length + 17) / 128) * 128; - var padded = new Uint8Array(paddedLength); - var i, j, n; - for (i = 0; i < length; ++i) { - padded[i] = data[offset++]; - } - padded[i++] = 0x80; - n = paddedLength - 16; - while (i < n) { - padded[i++] = 0; - } - padded[i++] = 0; - padded[i++] = 0; - padded[i++] = 0; - padded[i++] = 0; - padded[i++] = 0; - padded[i++] = 0; - padded[i++] = 0; - padded[i++] = 0; - padded[i++] = 0; - padded[i++] = 0; - padded[i++] = 0; - padded[i++] = (length >>> 29) & 0xFF; - padded[i++] = (length >> 21) & 0xFF; - padded[i++] = (length >> 13) & 0xFF; - padded[i++] = (length >> 5) & 0xFF; - padded[i++] = (length << 3) & 0xFF; - - var w = new Array(80); - for (i = 0; i < 80; i++) { - w[i] = new Word64(0, 0); - } - var a = new Word64(0, 0), b = new Word64(0, 0), c = new Word64(0, 0); - var d = new Word64(0, 0), e = new Word64(0, 0), f = new Word64(0, 0); - var g = new Word64(0, 0), h = new Word64(0, 0); - var t1 = new Word64(0, 0), t2 = new Word64(0, 0); - var tmp1 = new Word64(0, 0), tmp2 = new Word64(0, 0), tmp3; - - // for each 1024 bit block - for (i = 0; i < paddedLength;) { - for (j = 0; j < 16; ++j) { - w[j].high = (padded[i] << 24) | (padded[i + 1] << 16) | - (padded[i + 2] << 8) | (padded[i + 3]); - w[j].low = (padded[i + 4]) << 24 | (padded[i + 5]) << 16 | - (padded[i + 6]) << 8 | (padded[i + 7]); - i += 8; - } - for (j = 16; j < 80; ++j) { - tmp3 = w[j]; - littleSigmaPrime(tmp3, w[j - 2], tmp2); - tmp3.add(w[j - 7]); - littleSigma(tmp1, w[j - 15], tmp2); - tmp3.add(tmp1); - tmp3.add(w[j - 16]); - } - - a.assign(h0); b.assign(h1); c.assign(h2); d.assign(h3); - e.assign(h4); f.assign(h5); g.assign(h6); h.assign(h7); - for (j = 0; j < 80; ++j) { - t1.assign(h); - sigmaPrime(tmp1, e, tmp2); - t1.add(tmp1); - ch(tmp1, e, f, g, tmp2); - t1.add(tmp1); - t1.add(k[j]); - t1.add(w[j]); - - sigma(t2, a, tmp2); - maj(tmp1, a, b, c, tmp2); - t2.add(tmp1); - - tmp3 = h; - h = g; - g = f; - f = e; - d.add(t1); - e = d; - d = c; - c = b; - b = a; - tmp3.assign(t1); - tmp3.add(t2); - a = tmp3; - } - h0.add(a); - h1.add(b); - h2.add(c); - h3.add(d); - h4.add(e); - h5.add(f); - h6.add(g); - h7.add(h); - } - - var result; - if (!mode384) { - result = new Uint8Array(64); - h0.copyTo(result,0); - h1.copyTo(result,8); - h2.copyTo(result,16); - h3.copyTo(result,24); - h4.copyTo(result,32); - h5.copyTo(result,40); - h6.copyTo(result,48); - h7.copyTo(result,56); - } - else { - result = new Uint8Array(48); - h0.copyTo(result,0); - h1.copyTo(result,8); - h2.copyTo(result,16); - h3.copyTo(result,24); - h4.copyTo(result,32); - h5.copyTo(result,40); - } - return result; - } - - return hash; -})(); -var calculateSHA384 = (function calculateSHA384Closure() { - function hash(data, offset, length) { - return calculateSHA512(data, offset, length, true); - } - - return hash; -})(); -var NullCipher = (function NullCipherClosure() { - function NullCipher() { - } - - NullCipher.prototype = { - decryptBlock: function NullCipher_decryptBlock(data) { - return data; - } - }; - - return NullCipher; -})(); - -var AES128Cipher = (function AES128CipherClosure() { - var rcon = new Uint8Array([ - 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, - 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, - 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, - 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, - 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, - 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, - 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, - 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, - 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, - 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, - 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, - 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, - 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, - 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, - 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, - 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, - 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, - 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, - 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, - 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, - 0x74, 0xe8, 0xcb, 0x8d]); - - var s = new Uint8Array([ - 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, - 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, - 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, - 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, - 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, - 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, - 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, - 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, - 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, - 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, - 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, - 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, - 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, - 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, - 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, - 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, - 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, - 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, - 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, - 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, - 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, - 0xb0, 0x54, 0xbb, 0x16]); - - var inv_s = new Uint8Array([ - 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, - 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, - 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, - 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, - 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, - 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, - 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, - 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, - 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, - 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, - 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, - 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, - 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, - 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, - 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, - 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, - 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, - 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, - 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, - 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, - 0x55, 0x21, 0x0c, 0x7d]); - var mixCol = new Uint8Array(256); - for (var i = 0; i < 256; i++) { - if (i < 128) { - mixCol[i] = i << 1; - } else { - mixCol[i] = (i << 1) ^ 0x1b; - } - } - var mix = new Uint32Array([ - 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, - 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, - 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, - 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, - 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, - 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, - 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, - 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, - 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, - 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, - 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, - 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, - 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, - 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, - 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, - 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, - 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, - 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, - 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, - 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, - 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, - 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, - 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, - 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, - 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, - 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, - 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, - 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, - 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, - 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, - 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, - 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, - 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, - 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, - 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, - 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, - 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, - 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, - 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, - 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, - 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, - 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, - 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]); - - function expandKey128(cipherKey) { - var b = 176, result = new Uint8Array(b); - result.set(cipherKey); - for (var j = 16, i = 1; j < b; ++i) { - // RotWord - var t1 = result[j - 3], t2 = result[j - 2], - t3 = result[j - 1], t4 = result[j - 4]; - // SubWord - t1 = s[t1]; - t2 = s[t2]; - t3 = s[t3]; - t4 = s[t4]; - // Rcon - t1 = t1 ^ rcon[i]; - for (var n = 0; n < 4; ++n) { - result[j] = (t1 ^= result[j - 16]); - j++; - result[j] = (t2 ^= result[j - 16]); - j++; - result[j] = (t3 ^= result[j - 16]); - j++; - result[j] = (t4 ^= result[j - 16]); - j++; - } - } - return result; - } - - function decrypt128(input, key) { - var state = new Uint8Array(16); - state.set(input); - var i, j, k; - var t, u, v; - // AddRoundKey - for (j = 0, k = 160; j < 16; ++j, ++k) { - state[j] ^= key[k]; - } - for (i = 9; i >= 1; --i) { - // InvShiftRows - t = state[13]; - state[13] = state[9]; - state[9] = state[5]; - state[5] = state[1]; - state[1] = t; - t = state[14]; - u = state[10]; - state[14] = state[6]; - state[10] = state[2]; - state[6] = t; - state[2] = u; - t = state[15]; - u = state[11]; - v = state[7]; - state[15] = state[3]; - state[11] = t; - state[7] = u; - state[3] = v; - // InvSubBytes - for (j = 0; j < 16; ++j) { - state[j] = inv_s[state[j]]; - } - // AddRoundKey - for (j = 0, k = i * 16; j < 16; ++j, ++k) { - state[j] ^= key[k]; - } - // InvMixColumns - for (j = 0; j < 16; j += 4) { - var s0 = mix[state[j]], s1 = mix[state[j + 1]], - s2 = mix[state[j + 2]], s3 = mix[state[j + 3]]; - t = (s0 ^ (s1 >>> 8) ^ (s1 << 24) ^ (s2 >>> 16) ^ (s2 << 16) ^ - (s3 >>> 24) ^ (s3 << 8)); - state[j] = (t >>> 24) & 0xFF; - state[j + 1] = (t >> 16) & 0xFF; - state[j + 2] = (t >> 8) & 0xFF; - state[j + 3] = t & 0xFF; - } - } - // InvShiftRows - t = state[13]; - state[13] = state[9]; - state[9] = state[5]; - state[5] = state[1]; - state[1] = t; - t = state[14]; - u = state[10]; - state[14] = state[6]; - state[10] = state[2]; - state[6] = t; - state[2] = u; - t = state[15]; - u = state[11]; - v = state[7]; - state[15] = state[3]; - state[11] = t; - state[7] = u; - state[3] = v; - for (j = 0; j < 16; ++j) { - // InvSubBytes - state[j] = inv_s[state[j]]; - // AddRoundKey - state[j] ^= key[j]; - } - return state; - } - - function encrypt128(input, key) { - var t, u, v, k; - var state = new Uint8Array(16); - state.set(input); - for (j = 0; j < 16; ++j) { - // AddRoundKey - state[j] ^= key[j]; - } - - for (i = 1; i < 10; i++) { - //SubBytes - for (j = 0; j < 16; ++j) { - state[j] = s[state[j]]; - } - //ShiftRows - v = state[1]; - state[1] = state[5]; - state[5] = state[9]; - state[9] = state[13]; - state[13] = v; - v = state[2]; - u = state[6]; - state[2] = state[10]; - state[6] = state[14]; - state[10] = v; - state[14] = u; - v = state[3]; - u = state[7]; - t = state[11]; - state[3] = state[15]; - state[7] = v; - state[11] = u; - state[15] = t; - //MixColumns - for (var j = 0; j < 16; j += 4) { - var s0 = state[j + 0], s1 = state[j + 1]; - var s2 = state[j + 2], s3 = state[j + 3]; - t = s0 ^ s1 ^ s2 ^ s3; - state[j + 0] ^= t ^ mixCol[s0 ^ s1]; - state[j + 1] ^= t ^ mixCol[s1 ^ s2]; - state[j + 2] ^= t ^ mixCol[s2 ^ s3]; - state[j + 3] ^= t ^ mixCol[s3 ^ s0]; - } - //AddRoundKey - for (j = 0, k = i * 16; j < 16; ++j, ++k) { - state[j] ^= key[k]; - } - } - - //SubBytes - for (j = 0; j < 16; ++j) { - state[j] = s[state[j]]; - } - //ShiftRows - v = state[1]; - state[1] = state[5]; - state[5] = state[9]; - state[9] = state[13]; - state[13] = v; - v = state[2]; - u = state[6]; - state[2] = state[10]; - state[6] = state[14]; - state[10] = v; - state[14] = u; - v = state[3]; - u = state[7]; - t = state[11]; - state[3] = state[15]; - state[7] = v; - state[11] = u; - state[15] = t; - //AddRoundKey - for (j = 0, k = 160; j < 16; ++j, ++k) { - state[j] ^= key[k]; - } - return state; - } - - function AES128Cipher(key) { - this.key = expandKey128(key); - this.buffer = new Uint8Array(16); - this.bufferPosition = 0; - } - - function decryptBlock2(data, finalize) { - var i, j, ii, sourceLength = data.length, - buffer = this.buffer, bufferLength = this.bufferPosition, - result = [], iv = this.iv; - for (i = 0; i < sourceLength; ++i) { - buffer[bufferLength] = data[i]; - ++bufferLength; - if (bufferLength < 16) { - continue; - } - // buffer is full, decrypting - var plain = decrypt128(buffer, this.key); - // xor-ing the IV vector to get plain text - for (j = 0; j < 16; ++j) { - plain[j] ^= iv[j]; - } - iv = buffer; - result.push(plain); - buffer = new Uint8Array(16); - bufferLength = 0; - } - // saving incomplete buffer - this.buffer = buffer; - this.bufferLength = bufferLength; - this.iv = iv; - if (result.length === 0) { - return new Uint8Array([]); - } - // combining plain text blocks into one - var outputLength = 16 * result.length; - if (finalize) { - // undo a padding that is described in RFC 2898 - var lastBlock = result[result.length - 1]; - var psLen = lastBlock[15]; - if (psLen <= 16) { - for (i = 15, ii = 16 - psLen; i >= ii; --i) { - if (lastBlock[i] !== psLen) { - // Invalid padding, assume that the block has no padding. - psLen = 0; - break; - } - } - outputLength -= psLen; - result[result.length - 1] = lastBlock.subarray(0, 16 - psLen); - } - } - var output = new Uint8Array(outputLength); - for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) { - output.set(result[i], j); - } - return output; - } - - AES128Cipher.prototype = { - decryptBlock: function AES128Cipher_decryptBlock(data, finalize) { - var i, sourceLength = data.length; - var buffer = this.buffer, bufferLength = this.bufferPosition; - // waiting for IV values -- they are at the start of the stream - for (i = 0; bufferLength < 16 && i < sourceLength; ++i, ++bufferLength) { - buffer[bufferLength] = data[i]; - } - if (bufferLength < 16) { - // need more data - this.bufferLength = bufferLength; - return new Uint8Array([]); - } - this.iv = buffer; - this.buffer = new Uint8Array(16); - this.bufferLength = 0; - // starting decryption - this.decryptBlock = decryptBlock2; - return this.decryptBlock(data.subarray(16), finalize); - }, - encrypt: function AES128Cipher_encrypt(data, iv) { - var i, j, ii, sourceLength = data.length, - buffer = this.buffer, bufferLength = this.bufferPosition, - result = []; - if (!iv) { - iv = new Uint8Array(16); - } - for (i = 0; i < sourceLength; ++i) { - buffer[bufferLength] = data[i]; - ++bufferLength; - if (bufferLength < 16) { - continue; - } - for (j = 0; j < 16; ++j) { - buffer[j] ^= iv[j]; - } - - // buffer is full, encrypting - var cipher = encrypt128(buffer, this.key); - iv = cipher; - result.push(cipher); - buffer = new Uint8Array(16); - bufferLength = 0; - } - // saving incomplete buffer - this.buffer = buffer; - this.bufferLength = bufferLength; - this.iv = iv; - if (result.length === 0) { - return new Uint8Array([]); - } - // combining plain text blocks into one - var outputLength = 16 * result.length; - var output = new Uint8Array(outputLength); - for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) { - output.set(result[i], j); - } - return output; - } - }; - - return AES128Cipher; -})(); - -var AES256Cipher = (function AES256CipherClosure() { - var rcon = new Uint8Array([ - 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, - 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, - 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, - 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, - 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, - 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, - 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, - 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, - 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, - 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, - 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, - 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, - 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, - 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, - 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, - 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, - 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, - 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, - 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, - 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, - 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, - 0x74, 0xe8, 0xcb, 0x8d]); - - var s = new Uint8Array([ - 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, - 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, - 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, - 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, - 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, - 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, - 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, - 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, - 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, - 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, - 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, - 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, - 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, - 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, - 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, - 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, - 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, - 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, - 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, - 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, - 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, - 0xb0, 0x54, 0xbb, 0x16]); - - var inv_s = new Uint8Array([ - 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, - 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, - 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, - 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, - 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, - 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, - 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, - 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, - 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, - 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, - 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, - 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, - 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, - 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, - 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, - 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, - 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, - 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, - 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, - 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, - 0x55, 0x21, 0x0c, 0x7d]); - - var mixCol = new Uint8Array(256); - for (var i = 0; i < 256; i++) { - if (i < 128) { - mixCol[i] = i << 1; - } else { - mixCol[i] = (i << 1) ^ 0x1b; - } - } - var mix = new Uint32Array([ - 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, - 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, - 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, - 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, - 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, - 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, - 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, - 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, - 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, - 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, - 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, - 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, - 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, - 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, - 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, - 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, - 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, - 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, - 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, - 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, - 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, - 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, - 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, - 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, - 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, - 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, - 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, - 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, - 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, - 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, - 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, - 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, - 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, - 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, - 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, - 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, - 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, - 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, - 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, - 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, - 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, - 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, - 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3]); - - function expandKey256(cipherKey) { - var b = 240, result = new Uint8Array(b); - var r = 1; - - result.set(cipherKey); - for (var j = 32, i = 1; j < b; ++i) { - if (j % 32 === 16) { - t1 = s[t1]; - t2 = s[t2]; - t3 = s[t3]; - t4 = s[t4]; - } else if (j % 32 === 0) { - // RotWord - var t1 = result[j - 3], t2 = result[j - 2], - t3 = result[j - 1], t4 = result[j - 4]; - // SubWord - t1 = s[t1]; - t2 = s[t2]; - t3 = s[t3]; - t4 = s[t4]; - // Rcon - t1 = t1 ^ r; - if ((r <<= 1) >= 256) { - r = (r ^ 0x1b) & 0xFF; - } - } - - for (var n = 0; n < 4; ++n) { - result[j] = (t1 ^= result[j - 32]); - j++; - result[j] = (t2 ^= result[j - 32]); - j++; - result[j] = (t3 ^= result[j - 32]); - j++; - result[j] = (t4 ^= result[j - 32]); - j++; - } - } - return result; - } - - function decrypt256(input, key) { - var state = new Uint8Array(16); - state.set(input); - var i, j, k; - var t, u, v; - // AddRoundKey - for (j = 0, k = 224; j < 16; ++j, ++k) { - state[j] ^= key[k]; - } - for (i = 13; i >= 1; --i) { - // InvShiftRows - t = state[13]; - state[13] = state[9]; - state[9] = state[5]; - state[5] = state[1]; - state[1] = t; - t = state[14]; - u = state[10]; - state[14] = state[6]; - state[10] = state[2]; - state[6] = t; - state[2] = u; - t = state[15]; - u = state[11]; - v = state[7]; - state[15] = state[3]; - state[11] = t; - state[7] = u; - state[3] = v; - // InvSubBytes - for (j = 0; j < 16; ++j) { - state[j] = inv_s[state[j]]; - } - // AddRoundKey - for (j = 0, k = i * 16; j < 16; ++j, ++k) { - state[j] ^= key[k]; - } - // InvMixColumns - for (j = 0; j < 16; j += 4) { - var s0 = mix[state[j]], s1 = mix[state[j + 1]], - s2 = mix[state[j + 2]], s3 = mix[state[j + 3]]; - t = (s0 ^ (s1 >>> 8) ^ (s1 << 24) ^ (s2 >>> 16) ^ (s2 << 16) ^ - (s3 >>> 24) ^ (s3 << 8)); - state[j] = (t >>> 24) & 0xFF; - state[j + 1] = (t >> 16) & 0xFF; - state[j + 2] = (t >> 8) & 0xFF; - state[j + 3] = t & 0xFF; - } - } - // InvShiftRows - t = state[13]; - state[13] = state[9]; - state[9] = state[5]; - state[5] = state[1]; - state[1] = t; - t = state[14]; - u = state[10]; - state[14] = state[6]; - state[10] = state[2]; - state[6] = t; - state[2] = u; - t = state[15]; - u = state[11]; - v = state[7]; - state[15] = state[3]; - state[11] = t; - state[7] = u; - state[3] = v; - for (j = 0; j < 16; ++j) { - // InvSubBytes - state[j] = inv_s[state[j]]; - // AddRoundKey - state[j] ^= key[j]; - } - return state; - } - - function encrypt256(input, key) { - var t, u, v, k; - var state = new Uint8Array(16); - state.set(input); - for (j = 0; j < 16; ++j) { - // AddRoundKey - state[j] ^= key[j]; - } - - for (i = 1; i < 14; i++) { - //SubBytes - for (j = 0; j < 16; ++j) { - state[j] = s[state[j]]; - } - //ShiftRows - v = state[1]; - state[1] = state[5]; - state[5] = state[9]; - state[9] = state[13]; - state[13] = v; - v = state[2]; - u = state[6]; - state[2] = state[10]; - state[6] = state[14]; - state[10] = v; - state[14] = u; - v = state[3]; - u = state[7]; - t = state[11]; - state[3] = state[15]; - state[7] = v; - state[11] = u; - state[15] = t; - //MixColumns - for (var j = 0; j < 16; j += 4) { - var s0 = state[j + 0], s1 = state[j + 1]; - var s2 = state[j + 2], s3 = state[j + 3]; - t = s0 ^ s1 ^ s2 ^ s3; - state[j + 0] ^= t ^ mixCol[s0 ^ s1]; - state[j + 1] ^= t ^ mixCol[s1 ^ s2]; - state[j + 2] ^= t ^ mixCol[s2 ^ s3]; - state[j + 3] ^= t ^ mixCol[s3 ^ s0]; - } - //AddRoundKey - for (j = 0, k = i * 16; j < 16; ++j, ++k) { - state[j] ^= key[k]; - } - } - - //SubBytes - for (j = 0; j < 16; ++j) { - state[j] = s[state[j]]; - } - //ShiftRows - v = state[1]; - state[1] = state[5]; - state[5] = state[9]; - state[9] = state[13]; - state[13] = v; - v = state[2]; - u = state[6]; - state[2] = state[10]; - state[6] = state[14]; - state[10] = v; - state[14] = u; - v = state[3]; - u = state[7]; - t = state[11]; - state[3] = state[15]; - state[7] = v; - state[11] = u; - state[15] = t; - //AddRoundKey - for (j = 0, k = 224; j < 16; ++j, ++k) { - state[j] ^= key[k]; - } - - return state; - - } - - function AES256Cipher(key) { - this.key = expandKey256(key); - this.buffer = new Uint8Array(16); - this.bufferPosition = 0; - } - - function decryptBlock2(data, finalize) { - var i, j, ii, sourceLength = data.length, - buffer = this.buffer, bufferLength = this.bufferPosition, - result = [], iv = this.iv; - - for (i = 0; i < sourceLength; ++i) { - buffer[bufferLength] = data[i]; - ++bufferLength; - if (bufferLength < 16) { - continue; - } - // buffer is full, decrypting - var plain = decrypt256(buffer, this.key); - // xor-ing the IV vector to get plain text - for (j = 0; j < 16; ++j) { - plain[j] ^= iv[j]; - } - iv = buffer; - result.push(plain); - buffer = new Uint8Array(16); - bufferLength = 0; - } - // saving incomplete buffer - this.buffer = buffer; - this.bufferLength = bufferLength; - this.iv = iv; - if (result.length === 0) { - return new Uint8Array([]); - } - // combining plain text blocks into one - var outputLength = 16 * result.length; - if (finalize) { - // undo a padding that is described in RFC 2898 - var lastBlock = result[result.length - 1]; - var psLen = lastBlock[15]; - if (psLen <= 16) { - for (i = 15, ii = 16 - psLen; i >= ii; --i) { - if (lastBlock[i] !== psLen) { - // Invalid padding, assume that the block has no padding. - psLen = 0; - break; - } - } - outputLength -= psLen; - result[result.length - 1] = lastBlock.subarray(0, 16 - psLen); - } - } - var output = new Uint8Array(outputLength); - for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) { - output.set(result[i], j); - } - return output; - - } - - AES256Cipher.prototype = { - decryptBlock: function AES256Cipher_decryptBlock(data, finalize, iv) { - var i, sourceLength = data.length; - var buffer = this.buffer, bufferLength = this.bufferPosition; - // if not supplied an IV wait for IV values - // they are at the start of the stream - if (iv) { - this.iv = iv; - } else { - for (i = 0; bufferLength < 16 && - i < sourceLength; ++i, ++bufferLength) { - buffer[bufferLength] = data[i]; - } - if (bufferLength < 16) { - //need more data - this.bufferLength = bufferLength; - return new Uint8Array([]); - } - this.iv = buffer; - data = data.subarray(16); - } - this.buffer = new Uint8Array(16); - this.bufferLength = 0; - // starting decryption - this.decryptBlock = decryptBlock2; - return this.decryptBlock(data, finalize); - }, - encrypt: function AES256Cipher_encrypt(data, iv) { - var i, j, ii, sourceLength = data.length, - buffer = this.buffer, bufferLength = this.bufferPosition, - result = []; - if (!iv) { - iv = new Uint8Array(16); - } - for (i = 0; i < sourceLength; ++i) { - buffer[bufferLength] = data[i]; - ++bufferLength; - if (bufferLength < 16) { - continue; - } - for (j = 0; j < 16; ++j) { - buffer[j] ^= iv[j]; - } - - // buffer is full, encrypting - var cipher = encrypt256(buffer, this.key); - this.iv = cipher; - result.push(cipher); - buffer = new Uint8Array(16); - bufferLength = 0; - } - // saving incomplete buffer - this.buffer = buffer; - this.bufferLength = bufferLength; - this.iv = iv; - if (result.length === 0) { - return new Uint8Array([]); - } - // combining plain text blocks into one - var outputLength = 16 * result.length; - var output = new Uint8Array(outputLength); - for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) { - output.set(result[i], j); - } - return output; - } - }; - - return AES256Cipher; -})(); - -var PDF17 = (function PDF17Closure() { - - function compareByteArrays(array1, array2) { - if (array1.length !== array2.length) { - return false; - } - for (var i = 0; i < array1.length; i++) { - if (array1[i] !== array2[i]) { - return false; - } - } - return true; - } - - function PDF17() { - } - - PDF17.prototype = { - checkOwnerPassword: function PDF17_checkOwnerPassword(password, - ownerValidationSalt, - userBytes, - ownerPassword) { - var hashData = new Uint8Array(password.length + 56); - hashData.set(password, 0); - hashData.set(ownerValidationSalt, password.length); - hashData.set(userBytes, password.length + ownerValidationSalt.length); - var result = calculateSHA256(hashData, 0, hashData.length); - return compareByteArrays(result, ownerPassword); - }, - checkUserPassword: function PDF17_checkUserPassword(password, - userValidationSalt, - userPassword) { - var hashData = new Uint8Array(password.length + 8); - hashData.set(password, 0); - hashData.set(userValidationSalt, password.length); - var result = calculateSHA256(hashData, 0, hashData.length); - return compareByteArrays(result, userPassword); - }, - getOwnerKey: function PDF17_getOwnerKey(password, ownerKeySalt, userBytes, - ownerEncryption) { - var hashData = new Uint8Array(password.length + 56); - hashData.set(password, 0); - hashData.set(ownerKeySalt, password.length); - hashData.set(userBytes, password.length + ownerKeySalt.length); - var key = calculateSHA256(hashData, 0, hashData.length); - var cipher = new AES256Cipher(key); - return cipher.decryptBlock(ownerEncryption, - false, - new Uint8Array(16)); - - }, - getUserKey: function PDF17_getUserKey(password, userKeySalt, - userEncryption) { - var hashData = new Uint8Array(password.length + 8); - hashData.set(password, 0); - hashData.set(userKeySalt, password.length); - //key is the decryption key for the UE string - var key = calculateSHA256(hashData, 0, hashData.length); - var cipher = new AES256Cipher(key); - return cipher.decryptBlock(userEncryption, - false, - new Uint8Array(16)); - } - }; - return PDF17; -})(); - -var PDF20 = (function PDF20Closure() { - - function concatArrays(array1, array2) { - var t = new Uint8Array(array1.length + array2.length); - t.set(array1, 0); - t.set(array2, array1.length); - return t; - } - - function calculatePDF20Hash(password, input, userBytes) { - //This refers to Algorithm 2.B as defined in ISO 32000-2 - var k = calculateSHA256(input, 0, input.length).subarray(0, 32); - var e = [0]; - var i = 0; - while (i < 64 || e[e.length - 1] > i - 32) { - var arrayLength = password.length + k.length + userBytes.length; - - var k1 = new Uint8Array(arrayLength * 64); - var array = concatArrays(password, k); - array = concatArrays(array, userBytes); - for (var j = 0, pos = 0; j < 64; j++, pos += arrayLength) { - k1.set(array, pos); - } - //AES128 CBC NO PADDING with - //first 16 bytes of k as the key and the second 16 as the iv. - var cipher = new AES128Cipher(k.subarray(0, 16)); - e = cipher.encrypt(k1, k.subarray(16, 32)); - //Now we have to take the first 16 bytes of an unsigned - //big endian integer... and compute the remainder - //modulo 3.... That is a fairly large number and - //JavaScript isn't going to handle that well... - //So we're using a trick that allows us to perform - //modulo math byte by byte - var remainder = 0; - for (var z = 0; z < 16; z++) { - remainder *= (256 % 3); - remainder %= 3; - remainder += ((e[z] >>> 0) % 3); - remainder %= 3; - } - if (remainder === 0) { - k = calculateSHA256(e, 0, e.length); - } - else if (remainder === 1) { - k = calculateSHA384(e, 0, e.length); - } - else if (remainder === 2) { - k = calculateSHA512(e, 0, e.length); - } - i++; - } - return k.subarray(0, 32); - } - - function PDF20() { - } - - function compareByteArrays(array1, array2) { - if (array1.length !== array2.length) { - return false; - } - for (var i = 0; i < array1.length; i++) { - if (array1[i] !== array2[i]) { - return false; - } - } - return true; - } - - PDF20.prototype = { - hash: function PDF20_hash(password, concatBytes, userBytes) { - return calculatePDF20Hash(password, concatBytes, userBytes); - }, - checkOwnerPassword: function PDF20_checkOwnerPassword(password, - ownerValidationSalt, - userBytes, - ownerPassword) { - var hashData = new Uint8Array(password.length + 56); - hashData.set(password, 0); - hashData.set(ownerValidationSalt, password.length); - hashData.set(userBytes, password.length + ownerValidationSalt.length); - var result = calculatePDF20Hash(password, hashData, userBytes); - return compareByteArrays(result, ownerPassword); - }, - checkUserPassword: function PDF20_checkUserPassword(password, - userValidationSalt, - userPassword) { - var hashData = new Uint8Array(password.length + 8); - hashData.set(password, 0); - hashData.set(userValidationSalt, password.length); - var result = calculatePDF20Hash(password, hashData, []); - return compareByteArrays(result, userPassword); - }, - getOwnerKey: function PDF20_getOwnerKey(password, ownerKeySalt, userBytes, - ownerEncryption) { - var hashData = new Uint8Array(password.length + 56); - hashData.set(password, 0); - hashData.set(ownerKeySalt, password.length); - hashData.set(userBytes, password.length + ownerKeySalt.length); - var key = calculatePDF20Hash(password, hashData, userBytes); - var cipher = new AES256Cipher(key); - return cipher.decryptBlock(ownerEncryption, - false, - new Uint8Array(16)); - - }, - getUserKey: function PDF20_getUserKey(password, userKeySalt, - userEncryption) { - var hashData = new Uint8Array(password.length + 8); - hashData.set(password, 0); - hashData.set(userKeySalt, password.length); - //key is the decryption key for the UE string - var key = calculatePDF20Hash(password, hashData, []); - var cipher = new AES256Cipher(key); - return cipher.decryptBlock(userEncryption, - false, - new Uint8Array(16)); - } - }; - return PDF20; -})(); - -var CipherTransform = (function CipherTransformClosure() { - function CipherTransform(stringCipherConstructor, streamCipherConstructor) { - this.stringCipherConstructor = stringCipherConstructor; - this.streamCipherConstructor = streamCipherConstructor; - } - - CipherTransform.prototype = { - createStream: function CipherTransform_createStream(stream, length) { - var cipher = new this.streamCipherConstructor(); - return new DecryptStream(stream, length, - function cipherTransformDecryptStream(data, finalize) { - return cipher.decryptBlock(data, finalize); - } - ); - }, - decryptString: function CipherTransform_decryptString(s) { - var cipher = new this.stringCipherConstructor(); - var data = stringToBytes(s); - data = cipher.decryptBlock(data, true); - return bytesToString(data); - } - }; - return CipherTransform; -})(); - -var CipherTransformFactory = (function CipherTransformFactoryClosure() { - var 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]); - - function createEncryptionKey20(revision, password, ownerPassword, - ownerValidationSalt, ownerKeySalt, uBytes, - userPassword, userValidationSalt, userKeySalt, - ownerEncryption, userEncryption, perms) { - if (password) { - var passwordLength = Math.min(127, password.length); - password = password.subarray(0, passwordLength); - } else { - password = []; - } - var pdfAlgorithm; - if (revision === 6) { - pdfAlgorithm = new PDF20(); - } else { - pdfAlgorithm = new PDF17(); - } - - if (pdfAlgorithm) { - if (pdfAlgorithm.checkUserPassword(password, userValidationSalt, - userPassword)) { - return pdfAlgorithm.getUserKey(password, userKeySalt, userEncryption); - } else if (password.length && pdfAlgorithm.checkOwnerPassword(password, - ownerValidationSalt, - uBytes, - ownerPassword)) { - return pdfAlgorithm.getOwnerKey(password, ownerKeySalt, uBytes, - ownerEncryption); - } - } - - return null; - } - - function prepareKeyData(fileId, password, ownerPassword, userPassword, - flags, revision, keyLength, encryptMetadata) { - var hashDataSize = 40 + ownerPassword.length + fileId.length; - var hashData = new Uint8Array(hashDataSize), 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]; - } - if (revision >= 4 && !encryptMetadata) { - hashData[i++] = 0xFF; - hashData[i++] = 0xFF; - hashData[i++] = 0xFF; - hashData[i++] = 0xFF; - } - var hash = calculateMD5(hashData, 0, i); - var keyLengthInBytes = keyLength >> 3; - if (revision >= 3) { - for (j = 0; j < 50; ++j) { - hash = calculateMD5(hash, 0, keyLengthInBytes); - } - } - var encryptionKey = hash.subarray(0, keyLengthInBytes); - var cipher, checkData; - - if (revision >= 3) { - for (i = 0; i < 32; ++i) { - hashData[i] = defaultPasswordBytes[i]; - } - for (j = 0, n = fileId.length; j < n; ++j) { - hashData[i++] = fileId[j]; - } - cipher = new ARCFourCipher(encryptionKey); - checkData = cipher.encryptBlock(calculateMD5(hashData, 0, i)); - n = encryptionKey.length; - var derivedKey = new Uint8Array(n), k; - for (j = 1; j <= 19; ++j) { - for (k = 0; k < n; ++k) { - derivedKey[k] = encryptionKey[k] ^ j; - } - cipher = new ARCFourCipher(derivedKey); - checkData = cipher.encryptBlock(checkData); - } - for (j = 0, n = checkData.length; j < n; ++j) { - if (userPassword[j] !== checkData[j]) { - return null; - } - } - } else { - cipher = new ARCFourCipher(encryptionKey); - checkData = cipher.encryptBlock(defaultPasswordBytes); - for (j = 0, n = checkData.length; j < n; ++j) { - if (userPassword[j] !== checkData[j]) { - return null; - } - } - } - return encryptionKey; - } - - function decodeUserPassword(password, ownerPassword, revision, keyLength) { - var hashData = new Uint8Array(32), i = 0, j, n; - n = Math.min(32, password.length); - for (; i < n; ++i) { - hashData[i] = password[i]; - } - j = 0; - while (i < 32) { - hashData[i++] = defaultPasswordBytes[j++]; - } - var hash = calculateMD5(hashData, 0, i); - var keyLengthInBytes = keyLength >> 3; - if (revision >= 3) { - for (j = 0; j < 50; ++j) { - hash = calculateMD5(hash, 0, hash.length); - } - } - - var cipher, userPassword; - if (revision >= 3) { - userPassword = ownerPassword; - var derivedKey = new Uint8Array(keyLengthInBytes), k; - for (j = 19; j >= 0; j--) { - for (k = 0; k < keyLengthInBytes; ++k) { - derivedKey[k] = hash[k] ^ j; - } - cipher = new ARCFourCipher(derivedKey); - userPassword = cipher.encryptBlock(userPassword); - } - } else { - cipher = new ARCFourCipher(hash.subarray(0, keyLengthInBytes)); - userPassword = cipher.encryptBlock(ownerPassword); - } - return userPassword; - } - - var identityName = Name.get('Identity'); - - function CipherTransformFactory(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 && algorithm !== 4 && - algorithm !== 5)) { - error('unsupported encryption algorithm'); - } - this.algorithm = algorithm; - 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')).subarray(0, 32); - var userPassword = stringToBytes(dict.get('U')).subarray(0, 32); - var flags = dict.get('P'); - var revision = dict.get('R'); - // meaningful when V is 4 or 5 - var encryptMetadata = ((algorithm === 4 || algorithm === 5) && - dict.get('EncryptMetadata') !== false); - this.encryptMetadata = encryptMetadata; - - var fileIdBytes = stringToBytes(fileId); - var passwordBytes; - if (password) { - if (revision === 6) { - try { - password = utf8StringToString(password); - } catch (ex) { - warn('CipherTransformFactory: ' + - 'Unable to convert UTF8 encoded password.'); - } - } - passwordBytes = stringToBytes(password); - } - - var encryptionKey; - if (algorithm !== 5) { - encryptionKey = prepareKeyData(fileIdBytes, passwordBytes, - ownerPassword, userPassword, flags, - revision, keyLength, encryptMetadata); - } - else { - var ownerValidationSalt = stringToBytes(dict.get('O')).subarray(32, 40); - var ownerKeySalt = stringToBytes(dict.get('O')).subarray(40, 48); - var uBytes = stringToBytes(dict.get('U')).subarray(0, 48); - var userValidationSalt = stringToBytes(dict.get('U')).subarray(32, 40); - var userKeySalt = stringToBytes(dict.get('U')).subarray(40, 48); - var ownerEncryption = stringToBytes(dict.get('OE')); - var userEncryption = stringToBytes(dict.get('UE')); - var perms = stringToBytes(dict.get('Perms')); - encryptionKey = - createEncryptionKey20(revision, passwordBytes, - ownerPassword, ownerValidationSalt, - ownerKeySalt, uBytes, - userPassword, userValidationSalt, - userKeySalt, ownerEncryption, - userEncryption, perms); - } - if (!encryptionKey && !password) { - throw new PasswordException('No password given', - PasswordResponses.NEED_PASSWORD); - } else if (!encryptionKey && password) { - // Attempting use the password as an owner password - var decodedPassword = decodeUserPassword(passwordBytes, ownerPassword, - revision, keyLength); - encryptionKey = prepareKeyData(fileIdBytes, decodedPassword, - ownerPassword, userPassword, flags, - revision, keyLength, encryptMetadata); - } - - if (!encryptionKey) { - throw new PasswordException('Incorrect Password', - PasswordResponses.INCORRECT_PASSWORD); - } - - this.encryptionKey = encryptionKey; - - if (algorithm >= 4) { - this.cf = dict.get('CF'); - this.stmf = dict.get('StmF') || identityName; - this.strf = dict.get('StrF') || identityName; - this.eff = dict.get('EFF') || this.stmf; - } - } - - function buildObjectKey(num, gen, encryptionKey, isAes) { - var key = new Uint8Array(encryptionKey.length + 9), 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; - if (isAes) { - key[i++] = 0x73; - key[i++] = 0x41; - key[i++] = 0x6C; - key[i++] = 0x54; - } - var hash = calculateMD5(key, 0, i); - return hash.subarray(0, Math.min(encryptionKey.length + 5, 16)); - } - - function buildCipherConstructor(cf, name, num, gen, key) { - var cryptFilter = cf.get(name.name); - var cfm; - if (cryptFilter !== null && cryptFilter !== undefined) { - cfm = cryptFilter.get('CFM'); - } - if (!cfm || cfm.name === 'None') { - return function cipherTransformFactoryBuildCipherConstructorNone() { - return new NullCipher(); - }; - } - if ('V2' === cfm.name) { - return function cipherTransformFactoryBuildCipherConstructorV2() { - return new ARCFourCipher(buildObjectKey(num, gen, key, false)); - }; - } - if ('AESV2' === cfm.name) { - return function cipherTransformFactoryBuildCipherConstructorAESV2() { - return new AES128Cipher(buildObjectKey(num, gen, key, true)); - }; - } - if ('AESV3' === cfm.name) { - return function cipherTransformFactoryBuildCipherConstructorAESV3() { - return new AES256Cipher(key); - }; - } - error('Unknown crypto method'); - } - - CipherTransformFactory.prototype = { - createCipherTransform: - function CipherTransformFactory_createCipherTransform(num, gen) { - if (this.algorithm === 4 || this.algorithm === 5) { - return new CipherTransform( - buildCipherConstructor(this.cf, this.stmf, - num, gen, this.encryptionKey), - buildCipherConstructor(this.cf, this.strf, - num, gen, this.encryptionKey)); - } - // algorithms 1 and 2 - var key = buildObjectKey(num, gen, this.encryptionKey, false); - var cipherConstructor = function buildCipherCipherConstructor() { - return new ARCFourCipher(key); - }; - return new CipherTransform(cipherConstructor, cipherConstructor); - } - }; - - return CipherTransformFactory; -})(); - -exports.AES128Cipher = AES128Cipher; -exports.AES256Cipher = AES256Cipher; -exports.ARCFourCipher = ARCFourCipher; -exports.CipherTransformFactory = CipherTransformFactory; -exports.PDF17 = PDF17; -exports.PDF20 = PDF20; -exports.calculateMD5 = calculateMD5; -exports.calculateSHA256 = calculateSHA256; -exports.calculateSHA384 = calculateSHA384; -exports.calculateSHA512 = calculateSHA512; -})); - -/* Copyright 2012 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -(function (root, factory) { - //if (typeof define === 'function' && define.amd) { - // define('pdfjs/core/obj', ['exports', 'pdfjs/shared/util', - // 'pdfjs/core/primitives', 'pdfjs/core/crypto', 'pdfjs/core/parser', - // 'pdfjs/core/chunked_stream'], factory); - // } else if (typeof exports !== 'undefined') { - // factory(exports, require('../shared/util.js'), require('./primitives.js'), - // require('./crypto.js'), require('./parser.js'), - // require('./chunked_stream.js')); - //} else { - factory((root.pdfjsCoreObj = {}), root.pdfjsSharedUtil, - root.pdfjsCorePrimitives, root.pdfjsCoreCrypto, root.pdfjsCoreParser, - root.pdfjsCoreChunkedStream); - //} -}(window, function (exports, sharedUtil, corePrimitives, coreCrypto, coreParser, - coreChunkedStream) { - -var InvalidPDFException = sharedUtil.InvalidPDFException; -var MissingDataException = sharedUtil.MissingDataException; -var XRefParseException = sharedUtil.XRefParseException; -var assert = sharedUtil.assert; -var bytesToString = sharedUtil.bytesToString; -var createPromiseCapability = sharedUtil.createPromiseCapability; -var error = sharedUtil.error; -var info = sharedUtil.info; -var isArray = sharedUtil.isArray; -var isInt = sharedUtil.isInt; -var isString = sharedUtil.isString; -var shadow = sharedUtil.shadow; -var stringToPDFString = sharedUtil.stringToPDFString; -var stringToUTF8String = sharedUtil.stringToUTF8String; -var warn = sharedUtil.warn; -var Ref = corePrimitives.Ref; -var RefSet = corePrimitives.RefSet; -var RefSetCache = corePrimitives.RefSetCache; -var isName = corePrimitives.isName; -var isCmd = corePrimitives.isCmd; -var isDict = corePrimitives.isDict; -var isRef = corePrimitives.isRef; -var isStream = corePrimitives.isStream; -var CipherTransformFactory = coreCrypto.CipherTransformFactory; -var Lexer = coreParser.Lexer; -var Parser = coreParser.Parser; -var ChunkedStream = coreChunkedStream.ChunkedStream; - -var Catalog = (function CatalogClosure() { - function Catalog(pdfManager, xref, pageFactory) { - this.pdfManager = pdfManager; - this.xref = xref; - this.catDict = xref.getCatalogObj(); - this.fontCache = new RefSetCache(); - assert(isDict(this.catDict), - 'catalog object is not a dictionary'); - - // TODO refactor to move getPage() to the PDFDocument. - this.pageFactory = pageFactory; - this.pagePromises = []; - } - - Catalog.prototype = { - get metadata() { - var streamRef = this.catDict.getRaw('Metadata'); - if (!isRef(streamRef)) { - return shadow(this, 'metadata', null); - } - - var encryptMetadata = (!this.xref.encrypt ? false : - this.xref.encrypt.encryptMetadata); - - var stream = this.xref.fetch(streamRef, !encryptMetadata); - var metadata; - if (stream && isDict(stream.dict)) { - var type = stream.dict.get('Type'); - var subtype = stream.dict.get('Subtype'); - - if (isName(type) && isName(subtype) && - type.name === 'Metadata' && subtype.name === 'XML') { - // XXX: This should examine the charset the XML document defines, - // however since there are currently no real means to decode - // arbitrary charsets, let's just hope that the author of the PDF - // was reasonable enough to stick with the XML default charset, - // which is UTF-8. - try { - metadata = stringToUTF8String(bytesToString(stream.getBytes())); - } catch (e) { - info('Skipping invalid metadata.'); - } - } - } - - return shadow(this, 'metadata', metadata); - }, - get toplevelPagesDict() { - var pagesObj = this.catDict.get('Pages'); - assert(isDict(pagesObj), 'invalid top-level pages dictionary'); - // shadow the prototype getter - return shadow(this, 'toplevelPagesDict', pagesObj); - }, - get documentOutline() { - var obj = null; - try { - obj = this.readDocumentOutline(); - } catch (ex) { - if (ex instanceof MissingDataException) { - throw ex; - } - warn('Unable to read document outline'); - } - return shadow(this, 'documentOutline', obj); - }, - readDocumentOutline: function Catalog_readDocumentOutline() { - var xref = this.xref; - var obj = this.catDict.get('Outlines'); - var root = { items: [] }; - if (isDict(obj)) { - obj = obj.getRaw('First'); - var processed = new RefSet(); - if (isRef(obj)) { - var queue = [{obj: obj, parent: root}]; - // to avoid recursion keeping track of the items - // in the processed dictionary - processed.put(obj); - while (queue.length > 0) { - var i = queue.shift(); - var outlineDict = xref.fetchIfRef(i.obj); - if (outlineDict === null) { - continue; - } - if (!outlineDict.has('Title')) { - error('Invalid outline item'); - } - var dest = outlineDict.get('A'); - if (dest) { - dest = dest.get('D'); - } else if (outlineDict.has('Dest')) { - dest = outlineDict.getRaw('Dest'); - if (isName(dest)) { - dest = dest.name; - } - } - var title = outlineDict.get('Title'); - var outlineItem = { - dest: dest, - title: stringToPDFString(title), - color: outlineDict.get('C') || [0, 0, 0], - count: outlineDict.get('Count'), - bold: !!(outlineDict.get('F') & 2), - italic: !!(outlineDict.get('F') & 1), - items: [] - }; - i.parent.items.push(outlineItem); - obj = outlineDict.getRaw('First'); - if (isRef(obj) && !processed.has(obj)) { - queue.push({obj: obj, parent: outlineItem}); - processed.put(obj); - } - obj = outlineDict.getRaw('Next'); - if (isRef(obj) && !processed.has(obj)) { - queue.push({obj: obj, parent: i.parent}); - processed.put(obj); - } - } - } - } - return (root.items.length > 0 ? root.items : null); - }, - get numPages() { - var obj = this.toplevelPagesDict.get('Count'); - assert( - isInt(obj), - 'page count in top level pages object is not an integer' - ); - // shadow the prototype getter - return shadow(this, 'num', obj); - }, - get destinations() { - function fetchDestination(dest) { - return isDict(dest) ? dest.get('D') : dest; - } - - var xref = this.xref; - var dests = {}, nameTreeRef, nameDictionaryRef; - var obj = this.catDict.get('Names'); - if (obj && obj.has('Dests')) { - nameTreeRef = obj.getRaw('Dests'); - } else if (this.catDict.has('Dests')) { - nameDictionaryRef = this.catDict.get('Dests'); - } - - if (nameDictionaryRef) { - // reading simple destination dictionary - obj = nameDictionaryRef; - obj.forEach(function catalogForEach(key, value) { - if (!value) { - return; - } - dests[key] = fetchDestination(value); - }); - } - if (nameTreeRef) { - var nameTree = new NameTree(nameTreeRef, xref); - var names = nameTree.getAll(); - for (var name in names) { - if (!names.hasOwnProperty(name)) { - continue; - } - dests[name] = fetchDestination(names[name]); - } - } - return shadow(this, 'destinations', dests); - }, - getDestination: function Catalog_getDestination(destinationId) { - function fetchDestination(dest) { - return isDict(dest) ? dest.get('D') : dest; - } - - var xref = this.xref; - var dest = null, nameTreeRef, nameDictionaryRef; - var obj = this.catDict.get('Names'); - if (obj && obj.has('Dests')) { - nameTreeRef = obj.getRaw('Dests'); - } else if (this.catDict.has('Dests')) { - nameDictionaryRef = this.catDict.get('Dests'); - } - - if (nameDictionaryRef) { // Simple destination dictionary. - var value = nameDictionaryRef.get(destinationId); - if (value) { - dest = fetchDestination(value); - } - } - if (nameTreeRef) { - var nameTree = new NameTree(nameTreeRef, xref); - dest = fetchDestination(nameTree.get(destinationId)); - } - return dest; - }, - get attachments() { - var xref = this.xref; - var attachments = null, nameTreeRef; - var obj = this.catDict.get('Names'); - if (obj) { - nameTreeRef = obj.getRaw('EmbeddedFiles'); - } - - if (nameTreeRef) { - var nameTree = new NameTree(nameTreeRef, xref); - var names = nameTree.getAll(); - for (var name in names) { - if (!names.hasOwnProperty(name)) { - continue; - } - var fs = new FileSpec(names[name], xref); - if (!attachments) { - attachments = {}; - } - attachments[stringToPDFString(name)] = fs.serializable; - } - } - return shadow(this, 'attachments', attachments); - }, - get javaScript() { - var xref = this.xref; - var obj = this.catDict.get('Names'); - - var javaScript = []; - function appendIfJavaScriptDict(jsDict) { - var type = jsDict.get('S'); - if (!isName(type) || type.name !== 'JavaScript') { - return; - } - var js = jsDict.get('JS'); - if (isStream(js)) { - js = bytesToString(js.getBytes()); - } else if (!isString(js)) { - return; - } - javaScript.push(stringToPDFString(js)); - } - if (obj && obj.has('JavaScript')) { - var nameTree = new NameTree(obj.getRaw('JavaScript'), xref); - var names = nameTree.getAll(); - for (var name in names) { - if (!names.hasOwnProperty(name)) { - continue; - } - // We don't really use the JavaScript right now. This code is - // defensive so we don't cause errors on document load. - var jsDict = names[name]; - if (isDict(jsDict)) { - appendIfJavaScriptDict(jsDict); - } - } - } - - // Append OpenAction actions to javaScript array - var openactionDict = this.catDict.get('OpenAction'); - if (isDict(openactionDict, 'Action')) { - var actionType = openactionDict.get('S'); - if (isName(actionType) && actionType.name === 'Named') { - // The named Print action is not a part of the PDF 1.7 specification, - // but is supported by many PDF readers/writers (including Adobe's). - var action = openactionDict.get('N'); - if (isName(action) && action.name === 'Print') { - javaScript.push('print({});'); - } - } else { - appendIfJavaScriptDict(openactionDict); - } - } - - return shadow(this, 'javaScript', javaScript); - }, - - cleanup: function Catalog_cleanup() { - var promises = []; - this.fontCache.forEach(function (promise) { - promises.push(promise); - }); - return Promise.all(promises).then(function (translatedFonts) { - for (var i = 0, ii = translatedFonts.length; i < ii; i++) { - var font = translatedFonts[i].dict; - delete font.translated; - } - this.fontCache.clear(); - }.bind(this)); - }, - - getPage: function Catalog_getPage(pageIndex) { - if (!(pageIndex in this.pagePromises)) { - this.pagePromises[pageIndex] = this.getPageDict(pageIndex).then( - function (a) { - var dict = a[0]; - var ref = a[1]; - return this.pageFactory.createPage(pageIndex, dict, ref, - this.fontCache); - }.bind(this) - ); - } - return this.pagePromises[pageIndex]; - }, - - getPageDict: function Catalog_getPageDict(pageIndex) { - var capability = createPromiseCapability(); - var nodesToVisit = [this.catDict.getRaw('Pages')]; - var currentPageIndex = 0; - var xref = this.xref; - var checkAllKids = false; - - function next() { - while (nodesToVisit.length) { - var currentNode = nodesToVisit.pop(); - - if (isRef(currentNode)) { - xref.fetchAsync(currentNode).then(function (obj) { - if (isDict(obj, 'Page') || (isDict(obj) && !obj.has('Kids'))) { - if (pageIndex === currentPageIndex) { - capability.resolve([obj, currentNode]); - } else { - currentPageIndex++; - next(); - } - return; - } - nodesToVisit.push(obj); - next(); - }, capability.reject); - return; - } - - // Must be a child page dictionary. - assert( - isDict(currentNode), - 'page dictionary kid reference points to wrong type of object' - ); - var count = currentNode.get('Count'); - // If the current node doesn't have any children, avoid getting stuck - // in an empty node further down in the tree (see issue5644.pdf). - if (count === 0) { - checkAllKids = true; - } - // Skip nodes where the page can't be. - if (currentPageIndex + count <= pageIndex) { - currentPageIndex += count; - continue; - } - - var kids = currentNode.get('Kids'); - assert(isArray(kids), 'page dictionary kids object is not an array'); - if (!checkAllKids && count === kids.length) { - // Nodes that don't have the page have been skipped and this is the - // bottom of the tree which means the page requested must be a - // descendant of this pages node. Ideally we would just resolve the - // promise with the page ref here, but there is the case where more - // pages nodes could link to single a page (see issue 3666 pdf). To - // handle this push it back on the queue so if it is a pages node it - // will be descended into. - nodesToVisit = [kids[pageIndex - currentPageIndex]]; - currentPageIndex = pageIndex; - continue; - } else { - for (var last = kids.length - 1; last >= 0; last--) { - nodesToVisit.push(kids[last]); - } - } - } - capability.reject('Page index ' + pageIndex + ' not found.'); - } - next(); - return capability.promise; - }, - - getPageIndex: function Catalog_getPageIndex(ref) { - // The page tree nodes have the count of all the leaves below them. To get - // how many pages are before we just have to walk up the tree and keep - // adding the count of siblings to the left of the node. - var xref = this.xref; - function pagesBeforeRef(kidRef) { - var total = 0; - var parentRef; - return xref.fetchAsync(kidRef).then(function (node) { - if (!node) { - return null; - } - parentRef = node.getRaw('Parent'); - return node.getAsync('Parent'); - }).then(function (parent) { - if (!parent) { - return null; - } - return parent.getAsync('Kids'); - }).then(function (kids) { - if (!kids) { - return null; - } - var kidPromises = []; - var found = false; - for (var i = 0; i < kids.length; i++) { - var kid = kids[i]; - assert(isRef(kid), 'kids must be a ref'); - if (kid.num === kidRef.num) { - found = true; - break; - } - kidPromises.push(xref.fetchAsync(kid).then(function (kid) { - if (kid.has('Count')) { - var count = kid.get('Count'); - total += count; - } else { // page leaf node - total++; - } - })); - } - if (!found) { - error('kid ref not found in parents kids'); - } - return Promise.all(kidPromises).then(function () { - return [total, parentRef]; - }); - }); - } - - var total = 0; - function next(ref) { - return pagesBeforeRef(ref).then(function (args) { - if (!args) { - return total; - } - var count = args[0]; - var parentRef = args[1]; - total += count; - return next(parentRef); - }); - } - - return next(ref); - } - }; - - return Catalog; -})(); - -var XRef = (function XRefClosure() { - function XRef(stream, password) { - this.stream = stream; - this.entries = []; - this.xrefstms = {}; - // prepare the XRef cache - this.cache = []; - this.password = password; - this.stats = { - streamTypes: [], - fontTypes: [] - }; - } - - XRef.prototype = { - setStartXRef: function XRef_setStartXRef(startXRef) { - // Store the starting positions of xref tables as we process them - // so we can recover from missing data errors - this.startXRefQueue = [startXRef]; - this.xrefBlocks = [startXRef]; - }, - - parse: function XRef_parse(recoveryMode) { - var trailerDict; - if (!recoveryMode) { - trailerDict = this.readXRef(); - } else { - warn('Indexing all PDF objects'); - trailerDict = this.indexObjects(); - } - trailerDict.assignXref(this); - this.trailer = trailerDict; - var encrypt = trailerDict.get('Encrypt'); - if (encrypt) { - var ids = trailerDict.get('ID'); - var fileId = (ids && ids.length) ? ids[0] : ''; - this.encrypt = new CipherTransformFactory(encrypt, fileId, - this.password); - } - - // get the root dictionary (catalog) object - if (!(this.root = trailerDict.get('Root'))) { - error('Invalid root reference'); - } - }, - - processXRefTable: function XRef_processXRefTable(parser) { - if (!('tableState' in this)) { - // Stores state of the table as we process it so we can resume - // from middle of table in case of missing data error - this.tableState = { - entryNum: 0, - streamPos: parser.lexer.stream.pos, - parserBuf1: parser.buf1, - parserBuf2: parser.buf2 - }; - } - - var obj = this.readXRefTable(parser); - - // Sanity check - if (!isCmd(obj, 'trailer')) { - error('Invalid XRef table: could not find trailer dictionary'); - } - // Read trailer dictionary, e.g. - // trailer - // << /Size 22 - // /Root 20R - // /Info 10R - // /ID [ <81b14aafa313db63dbd6f981e49f94f4> ] - // >> - // The parser goes through the entire stream << ... >> and provides - // a getter interface for the key-value table - var dict = parser.getObj(); - - // The pdflib PDF generator can generate a nested trailer dictionary - if (!isDict(dict) && dict.dict) { - dict = dict.dict; - } - if (!isDict(dict)) { - error('Invalid XRef table: could not parse trailer dictionary'); - } - delete this.tableState; - - return dict; - }, - - readXRefTable: function XRef_readXRefTable(parser) { - // Example of cross-reference table: - // xref - // 0 1 <-- subsection header (first obj #, obj count) - // 0000000000 65535 f <-- actual object (offset, generation #, f/n) - // 23 2 <-- subsection header ... and so on ... - // 0000025518 00002 n - // 0000025635 00000 n - // trailer - // ... - - var stream = parser.lexer.stream; - var tableState = this.tableState; - stream.pos = tableState.streamPos; - parser.buf1 = tableState.parserBuf1; - parser.buf2 = tableState.parserBuf2; - - // Outer loop is over subsection headers - var obj; - - while (true) { - if (!('firstEntryNum' in tableState) || !('entryCount' in tableState)) { - if (isCmd(obj = parser.getObj(), 'trailer')) { - break; - } - tableState.firstEntryNum = obj; - tableState.entryCount = parser.getObj(); - } - - var first = tableState.firstEntryNum; - var count = tableState.entryCount; - if (!isInt(first) || !isInt(count)) { - error('Invalid XRef table: wrong types in subsection header'); - } - // Inner loop is over objects themselves - for (var i = tableState.entryNum; i < count; i++) { - tableState.streamPos = stream.pos; - tableState.entryNum = i; - tableState.parserBuf1 = parser.buf1; - tableState.parserBuf2 = parser.buf2; - - var entry = {}; - entry.offset = parser.getObj(); - entry.gen = parser.getObj(); - var type = parser.getObj(); - - if (isCmd(type, 'f')) { - entry.free = true; - } else if (isCmd(type, 'n')) { - entry.uncompressed = true; - } - - // Validate entry obj - if (!isInt(entry.offset) || !isInt(entry.gen) || - !(entry.free || entry.uncompressed)) { - error('Invalid entry in XRef subsection: ' + first + ', ' + count); - } - - if (!this.entries[i + first]) { - this.entries[i + first] = entry; - } - } - - tableState.entryNum = 0; - tableState.streamPos = stream.pos; - tableState.parserBuf1 = parser.buf1; - tableState.parserBuf2 = parser.buf2; - delete tableState.firstEntryNum; - delete tableState.entryCount; - } - - // Per issue 3248: hp scanners generate bad XRef - if (first === 1 && this.entries[1] && this.entries[1].free) { - // shifting the entries - this.entries.shift(); - } - - // Sanity check: as per spec, first object must be free - if (this.entries[0] && !this.entries[0].free) { - error('Invalid XRef table: unexpected first object'); - } - return obj; - }, - - processXRefStream: function XRef_processXRefStream(stream) { - if (!('streamState' in this)) { - // Stores state of the stream as we process it so we can resume - // from middle of stream in case of missing data error - var streamParameters = stream.dict; - var byteWidths = streamParameters.get('W'); - var range = streamParameters.get('Index'); - if (!range) { - range = [0, streamParameters.get('Size')]; - } - - this.streamState = { - entryRanges: range, - byteWidths: byteWidths, - entryNum: 0, - streamPos: stream.pos - }; - } - this.readXRefStream(stream); - delete this.streamState; - - return stream.dict; - }, - - readXRefStream: function XRef_readXRefStream(stream) { - var i, j; - var streamState = this.streamState; - stream.pos = streamState.streamPos; - - var byteWidths = streamState.byteWidths; - var typeFieldWidth = byteWidths[0]; - var offsetFieldWidth = byteWidths[1]; - var generationFieldWidth = byteWidths[2]; - - var entryRanges = streamState.entryRanges; - while (entryRanges.length > 0) { - var first = entryRanges[0]; - var n = entryRanges[1]; - - if (!isInt(first) || !isInt(n)) { - error('Invalid XRef range fields: ' + first + ', ' + n); - } - if (!isInt(typeFieldWidth) || !isInt(offsetFieldWidth) || - !isInt(generationFieldWidth)) { - error('Invalid XRef entry fields length: ' + first + ', ' + n); - } - for (i = streamState.entryNum; i < n; ++i) { - streamState.entryNum = i; - streamState.streamPos = stream.pos; - - var type = 0, offset = 0, generation = 0; - for (j = 0; j < typeFieldWidth; ++j) { - type = (type << 8) | stream.getByte(); - } - // if type field is absent, its default value is 1 - if (typeFieldWidth === 0) { - type = 1; - } - for (j = 0; j < offsetFieldWidth; ++j) { - offset = (offset << 8) | stream.getByte(); - } - for (j = 0; j < generationFieldWidth; ++j) { - generation = (generation << 8) | stream.getByte(); - } - var entry = {}; - entry.offset = offset; - entry.gen = generation; - switch (type) { - case 0: - entry.free = true; - break; - case 1: - entry.uncompressed = true; - break; - case 2: - break; - default: - error('Invalid XRef entry type: ' + type); - } - if (!this.entries[first + i]) { - this.entries[first + i] = entry; - } - } - - streamState.entryNum = 0; - streamState.streamPos = stream.pos; - entryRanges.splice(0, 2); - } - }, - - indexObjects: function XRef_indexObjects() { - // Simple scan through the PDF content to find objects, - // trailers and XRef streams. - var TAB = 0x9, LF = 0xA, CR = 0xD, SPACE = 0x20; - var PERCENT = 0x25, LT = 0x3C; - - function readToken(data, offset) { - var token = '', ch = data[offset]; - while (ch !== LF && ch !== CR && ch !== LT) { - if (++offset >= data.length) { - break; - } - token += String.fromCharCode(ch); - ch = data[offset]; - } - return token; - } - function skipUntil(data, offset, what) { - var length = what.length, dataLength = data.length; - var skipped = 0; - // finding byte sequence - while (offset < dataLength) { - var i = 0; - while (i < length && data[offset + i] === what[i]) { - ++i; - } - if (i >= length) { - break; // sequence found - } - offset++; - skipped++; - } - return skipped; - } - var objRegExp = /^(\d+)\s+(\d+)\s+obj\b/; - var trailerBytes = new Uint8Array([116, 114, 97, 105, 108, 101, 114]); - var startxrefBytes = new Uint8Array([115, 116, 97, 114, 116, 120, 114, - 101, 102]); - var endobjBytes = new Uint8Array([101, 110, 100, 111, 98, 106]); - var xrefBytes = new Uint8Array([47, 88, 82, 101, 102]); - - // Clear out any existing entries, since they may be bogus. - this.entries.length = 0; - - var stream = this.stream; - stream.pos = 0; - var buffer = stream.getBytes(); - var position = stream.start, length = buffer.length; - var trailers = [], xrefStms = []; - while (position < length) { - var ch = buffer[position]; - if (ch === TAB || ch === LF || ch === CR || ch === SPACE) { - ++position; - continue; - } - if (ch === PERCENT) { // %-comment - do { - ++position; - if (position >= length) { - break; - } - ch = buffer[position]; - } while (ch !== LF && ch !== CR); - continue; - } - var token = readToken(buffer, position); - var m; - if (token.indexOf('xref') === 0 && - (token.length === 4 || /\s/.test(token[4]))) { - position += skipUntil(buffer, position, trailerBytes); - trailers.push(position); - position += skipUntil(buffer, position, startxrefBytes); - } else if ((m = objRegExp.exec(token))) { - if (typeof this.entries[m[1]] === 'undefined') { - this.entries[m[1]] = { - offset: position - stream.start, - gen: m[2] | 0, - uncompressed: true - }; - } - var contentLength = skipUntil(buffer, position, endobjBytes) + 7; - var content = buffer.subarray(position, position + contentLength); - - // checking XRef stream suspect - // (it shall have '/XRef' and next char is not a letter) - var xrefTagOffset = skipUntil(content, 0, xrefBytes); - if (xrefTagOffset < contentLength && - content[xrefTagOffset + 5] < 64) { - xrefStms.push(position - stream.start); - this.xrefstms[position - stream.start] = 1; // Avoid recursion - } - - position += contentLength; - } else if (token.indexOf('trailer') === 0 && - (token.length === 7 || /\s/.test(token[7]))) { - trailers.push(position); - position += skipUntil(buffer, position, startxrefBytes); - } else { - position += token.length + 1; - } - } - // reading XRef streams - var i, ii; - for (i = 0, ii = xrefStms.length; i < ii; ++i) { - this.startXRefQueue.push(xrefStms[i]); - this.xrefBlocks.push(xrefStms[i]); - this.readXRef(/* recoveryMode */ true); - } - // finding main trailer - var dict; - for (i = 0, ii = trailers.length; i < ii; ++i) { - stream.pos = trailers[i]; - var parser = new Parser(new Lexer(stream), true, this); - var obj = parser.getObj(); - if (!isCmd(obj, 'trailer')) { - continue; - } - // read the trailer dictionary - if (!isDict(dict = parser.getObj())) { - continue; - } - // taking the first one with 'ID' - if (dict.has('ID')) { - return dict; - } - } - // no tailer with 'ID', taking last one (if exists) - if (dict) { - return dict; - } - // nothing helps - // calling error() would reject worker with an UnknownErrorException. - throw new InvalidPDFException('Invalid PDF structure'); - }, - - readXRef: function XRef_readXRef(recoveryMode) { - var stream = this.stream; - - try { - while (this.startXRefQueue.length) { - var startXRef = this.startXRefQueue[0]; - - stream.pos = startXRef + stream.start; - - var parser = new Parser(new Lexer(stream), true, this); - var obj = parser.getObj(); - var dict; - - // Get dictionary - if (isCmd(obj, 'xref')) { - // Parse end-of-file XRef - dict = this.processXRefTable(parser); - if (!this.topDict) { - this.topDict = dict; - } - - // Recursively get other XRefs 'XRefStm', if any - obj = dict.get('XRefStm'); - if (isInt(obj)) { - var pos = obj; - // ignore previously loaded xref streams - // (possible infinite recursion) - if (!(pos in this.xrefstms)) { - this.xrefstms[pos] = 1; - this.startXRefQueue.push(pos); - this.xrefBlocks.push(pos); - } - } - } else if (isInt(obj)) { - // Parse in-stream XRef - if (!isInt(parser.getObj()) || - !isCmd(parser.getObj(), 'obj') || - !isStream(obj = parser.getObj())) { - error('Invalid XRef stream'); - } - dict = this.processXRefStream(obj); - if (!this.topDict) { - this.topDict = dict; - } - if (!dict) { - error('Failed to read XRef stream'); - } - } else { - error('Invalid XRef stream header'); - } - - // Recursively get previous dictionary, if any - obj = dict.get('Prev'); - if (isInt(obj)) { - this.startXRefQueue.push(obj); - this.xrefBlocks.push(obj); - } else if (isRef(obj)) { - // The spec says Prev must not be a reference, i.e. "/Prev NNN" - // This is a fallback for non-compliant PDFs, i.e. "/Prev NNN 0 R" - this.startXRefQueue.push(obj.num); - this.xrefBlocks.push(obj.num); - } - this.xrefBlocks.push(stream.pos); - this.startXRefQueue.shift(); - } - - return this.topDict; - } catch (e) { - if (e instanceof MissingDataException) { - throw e; - } - info('(while reading XRef): ' + e); - } - - if (recoveryMode) { - return; - } - throw new XRefParseException(); - }, - - getEntry: function XRef_getEntry(i) { - var xrefEntry = this.entries[i]; - if (xrefEntry && !xrefEntry.free && xrefEntry.offset) { - return xrefEntry; - } - return null; - }, - - fetchIfRef: function XRef_fetchIfRef(obj) { - if (!isRef(obj)) { - return obj; - } - return this.fetch(obj); - }, - - fetch: function XRef_fetch(ref, suppressEncryption) { - assert(isRef(ref), 'ref object is not a reference'); - var num = ref.num; - if (num in this.cache) { - var cacheEntry = this.cache[num]; - return cacheEntry; - } - - var xrefEntry = this.getEntry(num); - - // the referenced entry can be free - if (xrefEntry === null) { - return (this.cache[num] = null); - } - - if (xrefEntry.uncompressed) { - xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption); - } else { - xrefEntry = this.fetchCompressed(xrefEntry, suppressEncryption); - } - if (isDict(xrefEntry)){ - xrefEntry.objId = ref.toString(); - } else if (isStream(xrefEntry)) { - xrefEntry.dict.objId = ref.toString(); - } - return xrefEntry; - }, - - fetchUncompressed: function XRef_fetchUncompressed(ref, xrefEntry, - suppressEncryption) { - var gen = ref.gen; - var num = ref.num; - if (xrefEntry.gen !== gen) { - error('inconsistent generation in XRef'); - } - var stream = this.stream.makeSubStream(xrefEntry.offset + - this.stream.start); - var parser = new Parser(new Lexer(stream), true, this); - var obj1 = parser.getObj(); - var obj2 = parser.getObj(); - var obj3 = parser.getObj(); - if (!isInt(obj1) || parseInt(obj1, 10) !== num || - !isInt(obj2) || parseInt(obj2, 10) !== gen || - !isCmd(obj3)) { - error('bad XRef entry'); - } - if (!isCmd(obj3, 'obj')) { - // some bad PDFs use "obj1234" and really mean 1234 - if (obj3.cmd.indexOf('obj') === 0) { - num = parseInt(obj3.cmd.substring(3), 10); - if (!isNaN(num)) { - return num; - } - } - error('bad XRef entry'); - } - if (this.encrypt && !suppressEncryption) { - xrefEntry = parser.getObj(this.encrypt.createCipherTransform(num, gen)); - } else { - xrefEntry = parser.getObj(); - } - if (!isStream(xrefEntry)) { - this.cache[num] = xrefEntry; - } - return xrefEntry; - }, - - fetchCompressed: function XRef_fetchCompressed(xrefEntry, - suppressEncryption) { - var tableOffset = xrefEntry.offset; - var stream = this.fetch(new Ref(tableOffset, 0)); - if (!isStream(stream)) { - error('bad ObjStm stream'); - } - var first = stream.dict.get('First'); - var n = stream.dict.get('N'); - if (!isInt(first) || !isInt(n)) { - error('invalid first and n parameters for ObjStm stream'); - } - var parser = new Parser(new Lexer(stream), false, this); - parser.allowStreams = true; - var i, entries = [], num, nums = []; - // read the object numbers to populate cache - for (i = 0; i < n; ++i) { - num = parser.getObj(); - if (!isInt(num)) { - error('invalid object number in the ObjStm stream: ' + num); - } - nums.push(num); - var offset = parser.getObj(); - if (!isInt(offset)) { - error('invalid object offset in the ObjStm stream: ' + offset); - } - } - // read stream objects for cache - for (i = 0; i < n; ++i) { - entries.push(parser.getObj()); - num = nums[i]; - var entry = this.entries[num]; - if (entry && entry.offset === tableOffset && entry.gen === i) { - this.cache[num] = entries[i]; - } - } - xrefEntry = entries[xrefEntry.gen]; - if (xrefEntry === undefined) { - error('bad XRef entry for compressed object'); - } - return xrefEntry; - }, - - fetchIfRefAsync: function XRef_fetchIfRefAsync(obj) { - if (!isRef(obj)) { - return Promise.resolve(obj); - } - return this.fetchAsync(obj); - }, - - fetchAsync: function XRef_fetchAsync(ref, suppressEncryption) { - var streamManager = this.stream.manager; - var xref = this; - return new Promise(function tryFetch(resolve, reject) { - try { - resolve(xref.fetch(ref, suppressEncryption)); - } catch (e) { - if (e instanceof MissingDataException) { - streamManager.requestRange(e.begin, e.end).then(function () { - tryFetch(resolve, reject); - }, reject); - return; - } - reject(e); - } - }); - }, - - getCatalogObj: function XRef_getCatalogObj() { - return this.root; - } - }; - - return XRef; -})(); - -/** - * A NameTree is like a Dict but has some advantageous properties, see the - * spec (7.9.6) for more details. - * TODO: implement all the Dict functions and make this more efficent. - */ -var NameTree = (function NameTreeClosure() { - function NameTree(root, xref) { - this.root = root; - this.xref = xref; - } - - NameTree.prototype = { - getAll: function NameTree_getAll() { - var dict = {}; - if (!this.root) { - return dict; - } - var xref = this.xref; - // reading name tree - var processed = new RefSet(); - processed.put(this.root); - var queue = [this.root]; - while (queue.length > 0) { - var i, n; - var obj = xref.fetchIfRef(queue.shift()); - if (!isDict(obj)) { - continue; - } - if (obj.has('Kids')) { - var kids = obj.get('Kids'); - for (i = 0, n = kids.length; i < n; i++) { - var kid = kids[i]; - if (processed.has(kid)) { - error('invalid destinations'); - } - queue.push(kid); - processed.put(kid); - } - continue; - } - var names = obj.get('Names'); - if (names) { - for (i = 0, n = names.length; i < n; i += 2) { - dict[xref.fetchIfRef(names[i])] = xref.fetchIfRef(names[i + 1]); - } - } - } - return dict; - }, - - get: function NameTree_get(destinationId) { - if (!this.root) { - return null; - } - - var xref = this.xref; - var kidsOrNames = xref.fetchIfRef(this.root); - var loopCount = 0; - var MAX_NAMES_LEVELS = 10; - var l, r, m; - - // Perform a binary search to quickly find the entry that - // contains the named destination we are looking for. - while (kidsOrNames.has('Kids')) { - loopCount++; - if (loopCount > MAX_NAMES_LEVELS) { - warn('Search depth limit for named destionations has been reached.'); - return null; - } - - var kids = kidsOrNames.get('Kids'); - if (!isArray(kids)) { - return null; - } - - l = 0; - r = kids.length - 1; - while (l <= r) { - m = (l + r) >> 1; - var kid = xref.fetchIfRef(kids[m]); - var limits = kid.get('Limits'); - - if (destinationId < xref.fetchIfRef(limits[0])) { - r = m - 1; - } else if (destinationId > xref.fetchIfRef(limits[1])) { - l = m + 1; - } else { - kidsOrNames = xref.fetchIfRef(kids[m]); - break; - } - } - if (l > r) { - return null; - } - } - - // If we get here, then we have found the right entry. Now - // go through the named destinations in the Named dictionary - // until we find the exact destination we're looking for. - var names = kidsOrNames.get('Names'); - if (isArray(names)) { - // Perform a binary search to reduce the lookup time. - l = 0; - r = names.length - 2; - while (l <= r) { - // Check only even indices (0, 2, 4, ...) because the - // odd indices contain the actual D array. - m = (l + r) & ~1; - if (destinationId < xref.fetchIfRef(names[m])) { - r = m - 2; - } else if (destinationId > xref.fetchIfRef(names[m])) { - l = m + 2; - } else { - return xref.fetchIfRef(names[m + 1]); - } - } - } - return null; - } - }; - return NameTree; -})(); - -/** - * "A PDF file can refer to the contents of another file by using a File - * Specification (PDF 1.1)", see the spec (7.11) for more details. - * NOTE: Only embedded files are supported (as part of the attachments support) - * TODO: support the 'URL' file system (with caching if !/V), portable - * collections attributes and related files (/RF) - */ -var FileSpec = (function FileSpecClosure() { - function FileSpec(root, xref) { - if (!root || !isDict(root)) { - return; - } - this.xref = xref; - this.root = root; - if (root.has('FS')) { - this.fs = root.get('FS'); - } - this.description = root.has('Desc') ? - stringToPDFString(root.get('Desc')) : - ''; - if (root.has('RF')) { - warn('Related file specifications are not supported'); - } - this.contentAvailable = true; - if (!root.has('EF')) { - this.contentAvailable = false; - warn('Non-embedded file specifications are not supported'); - } - } - - function pickPlatformItem(dict) { - // Look for the filename in this order: - // UF, F, Unix, Mac, DOS - if (dict.has('UF')) { - return dict.get('UF'); - } else if (dict.has('F')) { - return dict.get('F'); - } else if (dict.has('Unix')) { - return dict.get('Unix'); - } else if (dict.has('Mac')) { - return dict.get('Mac'); - } else if (dict.has('DOS')) { - return dict.get('DOS'); - } else { - return null; - } - } - - FileSpec.prototype = { - get filename() { - if (!this._filename && this.root) { - var filename = pickPlatformItem(this.root) || 'unnamed'; - this._filename = stringToPDFString(filename). - replace(/\\\\/g, '\\'). - replace(/\\\//g, '/'). - replace(/\\/g, '/'); - } - return this._filename; - }, - get content() { - if (!this.contentAvailable) { - return null; - } - if (!this.contentRef && this.root) { - this.contentRef = pickPlatformItem(this.root.get('EF')); - } - var content = null; - if (this.contentRef) { - var xref = this.xref; - var fileObj = xref.fetchIfRef(this.contentRef); - if (fileObj && isStream(fileObj)) { - content = fileObj.getBytes(); - } else { - warn('Embedded file specification points to non-existing/invalid ' + - 'content'); - } - } else { - warn('Embedded file specification does not have a content'); - } - return content; - }, - get serializable() { - return { - filename: this.filename, - content: this.content - }; - } - }; - return FileSpec; -})(); - -/** - * A helper for loading missing data in object graphs. It traverses the graph - * depth first and queues up any objects that have missing data. Once it has - * has traversed as many objects that are available it attempts to bundle the - * missing data requests and then resume from the nodes that weren't ready. - * - * NOTE: It provides protection from circular references by keeping track of - * of loaded references. However, you must be careful not to load any graphs - * that have references to the catalog or other pages since that will cause the - * entire PDF document object graph to be traversed. - */ -var ObjectLoader = (function() { - function mayHaveChildren(value) { - return isRef(value) || isDict(value) || isArray(value) || isStream(value); - } - - function addChildren(node, nodesToVisit) { - var value; - if (isDict(node) || isStream(node)) { - var map; - if (isDict(node)) { - map = node.map; - } else { - map = node.dict.map; - } - for (var key in map) { - value = map[key]; - if (mayHaveChildren(value)) { - nodesToVisit.push(value); - } - } - } else if (isArray(node)) { - for (var i = 0, ii = node.length; i < ii; i++) { - value = node[i]; - if (mayHaveChildren(value)) { - nodesToVisit.push(value); - } - } - } - } - - function ObjectLoader(obj, keys, xref) { - this.obj = obj; - this.keys = keys; - this.xref = xref; - this.refSet = null; - this.capability = null; - } - - ObjectLoader.prototype = { - load: function ObjectLoader_load() { - var keys = this.keys; - this.capability = createPromiseCapability(); - // Don't walk the graph if all the data is already loaded. - if (!(this.xref.stream instanceof ChunkedStream) || - this.xref.stream.getMissingChunks().length === 0) { - this.capability.resolve(); - return this.capability.promise; - } - - this.refSet = new RefSet(); - // Setup the initial nodes to visit. - var nodesToVisit = []; - for (var i = 0; i < keys.length; i++) { - nodesToVisit.push(this.obj[keys[i]]); - } - - this._walk(nodesToVisit); - return this.capability.promise; - }, - - _walk: function ObjectLoader_walk(nodesToVisit) { - var nodesToRevisit = []; - var pendingRequests = []; - // DFS walk of the object graph. - while (nodesToVisit.length) { - var currentNode = nodesToVisit.pop(); - - // Only references or chunked streams can cause missing data exceptions. - if (isRef(currentNode)) { - // Skip nodes that have already been visited. - if (this.refSet.has(currentNode)) { - continue; - } - try { - var ref = currentNode; - this.refSet.put(ref); - currentNode = this.xref.fetch(currentNode); - } catch (e) { - if (!(e instanceof MissingDataException)) { - throw e; - } - nodesToRevisit.push(currentNode); - pendingRequests.push({ begin: e.begin, end: e.end }); - } - } - if (currentNode && currentNode.getBaseStreams) { - var baseStreams = currentNode.getBaseStreams(); - var foundMissingData = false; - for (var i = 0; i < baseStreams.length; i++) { - var stream = baseStreams[i]; - if (stream.getMissingChunks && stream.getMissingChunks().length) { - foundMissingData = true; - pendingRequests.push({ - begin: stream.start, - end: stream.end - }); - } - } - if (foundMissingData) { - nodesToRevisit.push(currentNode); - } - } - - addChildren(currentNode, nodesToVisit); - } - - if (pendingRequests.length) { - this.xref.stream.manager.requestRanges(pendingRequests).then( - function pendingRequestCallback() { - nodesToVisit = nodesToRevisit; - for (var i = 0; i < nodesToRevisit.length; i++) { - var node = nodesToRevisit[i]; - // Remove any reference nodes from the currrent refset so they - // aren't skipped when we revist them. - if (isRef(node)) { - this.refSet.remove(node); - } - } - this._walk(nodesToVisit); - }.bind(this), this.capability.reject); - return; - } - // Everything is loaded. - this.refSet = null; - this.capability.resolve(); - } - }; - - return ObjectLoader; -})(); - -exports.Catalog = Catalog; -exports.ObjectLoader = ObjectLoader; -exports.XRef = XRef; -})); - -/* Copyright 2012 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -(function (root, factory) { - //if (typeof define === 'function' && define.amd) { - // define('pdfjs/core/document', ['exports', 'pdfjs/shared/util', - // 'pdfjs/core/primitives', 'pdfjs/core/stream', 'pdfjs/core/obj', - // 'pdfjs/core/parser', 'pdfjs/core/crypto'], factory); - // } else if (typeof exports !== 'undefined') { - // factory(exports, require('../shared/util.js'), require('./primitives.js'), - // require('./stream.js'), require('./obj.js'), require('./parser.js'), - // require('./crypto.js')); - //} else { - factory((root.pdfjsCoreDocument = {}), root.pdfjsSharedUtil, - root.pdfjsCorePrimitives, root.pdfjsCoreStream, - root.pdfjsCoreObj, root.pdfjsCoreParser, root.pdfjsCoreCrypto); - //} -}(window, function (exports, sharedUtil, corePrimitives, coreStream, coreObj, - coreParser, coreCrypto) { - -var MissingDataException = sharedUtil.MissingDataException; -var Util = sharedUtil.Util; -var assert = sharedUtil.assert; -var error = sharedUtil.error; -var info = sharedUtil.info; -var isArray = sharedUtil.isArray; -var isArrayBuffer = sharedUtil.isArrayBuffer; -var isString = sharedUtil.isString; -var shadow = sharedUtil.shadow; -var stringToBytes = sharedUtil.stringToBytes; -var stringToPDFString = sharedUtil.stringToPDFString; -var warn = sharedUtil.warn; -var Dict = corePrimitives.Dict; -var isDict = corePrimitives.isDict; -var isName = corePrimitives.isName; -var isStream = corePrimitives.isStream; -var NullStream = coreStream.NullStream; -var Stream = coreStream.Stream; -var StreamsSequenceStream = coreStream.StreamsSequenceStream; -var Catalog = coreObj.Catalog; -var ObjectLoader = coreObj.ObjectLoader; -var XRef = coreObj.XRef; -var Lexer = coreParser.Lexer; -var Linearization = coreParser.Linearization; -var calculateMD5 = coreCrypto.calculateMD5; - - -/** - * The `PDFDocument` holds all the data of the PDF file. Compared to the - * `PDFDoc`, this one doesn't have any job management code. - * Right now there exists one PDFDocument on the main thread + one object - * for each worker. If there is no worker support enabled, there are two - * `PDFDocument` objects on the main thread created. - */ -var PDFDocument = (function PDFDocumentClosure() { - var FINGERPRINT_FIRST_BYTES = 1024; - var EMPTY_FINGERPRINT = '\x00\x00\x00\x00\x00\x00\x00' + - '\x00\x00\x00\x00\x00\x00\x00\x00\x00'; - - function PDFDocument(pdfManager, arg, password) { - if (isStream(arg)) { - init.call(this, pdfManager, arg, password); - } else if (isArrayBuffer(arg)) { - init.call(this, pdfManager, new Stream(arg), password); - } else { - error('PDFDocument: Unknown argument type'); - } - } - - function init(pdfManager, stream, password) { - assert(stream.length > 0, 'stream must have data'); - this.pdfManager = pdfManager; - this.stream = stream; - var xref = new XRef(this.stream, password, pdfManager); - this.xref = xref; - } - - function find(stream, needle, limit, backwards) { - var pos = stream.pos; - var end = stream.end; - var strBuf = []; - if (pos + limit > end) { - limit = end - pos; - } - for (var n = 0; n < limit; ++n) { - strBuf.push(String.fromCharCode(stream.getByte())); - } - var str = strBuf.join(''); - stream.pos = pos; - var index = backwards ? str.lastIndexOf(needle) : str.indexOf(needle); - if (index === -1) { - return false; /* not found */ - } - stream.pos += index; - return true; /* found */ - } - - var DocumentInfoValidators = { - get entries() { - // Lazily build this since all the validation functions below are not - // defined until after this file loads. - return shadow(this, 'entries', { - Title: isString, - Author: isString, - Subject: isString, - Keywords: isString, - Creator: isString, - Producer: isString, - CreationDate: isString, - ModDate: isString, - Trapped: isName - }); - } - }; - - PDFDocument.prototype = { - parse: function PDFDocument_parse(recoveryMode) { - this.setup(recoveryMode); - var version = this.catalog.catDict.get('Version'); - if (isName(version)) { - this.pdfFormatVersion = version.name; - } - try { - // checking if AcroForm is present - this.acroForm = this.catalog.catDict.get('AcroForm'); - if (this.acroForm) { - this.xfa = this.acroForm.get('XFA'); - var fields = this.acroForm.get('Fields'); - if ((!fields || !isArray(fields) || fields.length === 0) && - !this.xfa) { - // no fields and no XFA -- not a form (?) - this.acroForm = null; - } - } - } catch (ex) { - info('Something wrong with AcroForm entry'); - this.acroForm = null; - } - }, - - get linearization() { - var linearization = null; - if (this.stream.length) { - try { - linearization = Linearization.create(this.stream); - } catch (err) { - if (err instanceof MissingDataException) { - throw err; - } - info(err); - } - } - // shadow the prototype getter with a data property - return shadow(this, 'linearization', linearization); - }, - get startXRef() { - var stream = this.stream; - var startXRef = 0; - var linearization = this.linearization; - if (linearization) { - // Find end of first obj. - stream.reset(); - if (find(stream, 'endobj', 1024)) { - startXRef = stream.pos + 6; - } - } else { - // Find startxref by jumping backward from the end of the file. - var step = 1024; - var found = false, pos = stream.end; - while (!found && pos > 0) { - pos -= step - 'startxref'.length; - if (pos < 0) { - pos = 0; - } - stream.pos = pos; - found = find(stream, 'startxref', step, true); - } - if (found) { - stream.skip(9); - var ch; - do { - ch = stream.getByte(); - } while (Lexer.isSpace(ch)); - var str = ''; - while (ch >= 0x20 && ch <= 0x39) { // < '9' - str += String.fromCharCode(ch); - ch = stream.getByte(); - } - startXRef = parseInt(str, 10); - if (isNaN(startXRef)) { - startXRef = 0; - } - } - } - // shadow the prototype getter with a data property - return shadow(this, 'startXRef', startXRef); - }, - get mainXRefEntriesOffset() { - var mainXRefEntriesOffset = 0; - var linearization = this.linearization; - if (linearization) { - mainXRefEntriesOffset = linearization.mainXRefEntriesOffset; - } - // shadow the prototype getter with a data property - return shadow(this, 'mainXRefEntriesOffset', mainXRefEntriesOffset); - }, - // Find the header, remove leading garbage and setup the stream - // starting from the header. - checkHeader: function PDFDocument_checkHeader() { - var stream = this.stream; - stream.reset(); - if (find(stream, '%PDF-', 1024)) { - // Found the header, trim off any garbage before it. - stream.moveStart(); - // Reading file format version - var MAX_VERSION_LENGTH = 12; - var version = '', ch; - while ((ch = stream.getByte()) > 0x20) { // SPACE - if (version.length >= MAX_VERSION_LENGTH) { - break; - } - version += String.fromCharCode(ch); - } - if (!this.pdfFormatVersion) { - // removing "%PDF-"-prefix - this.pdfFormatVersion = version.substring(5); - } - return; - } - // May not be a PDF file, continue anyway. - }, - parseStartXRef: function PDFDocument_parseStartXRef() { - var startXRef = this.startXRef; - this.xref.setStartXRef(startXRef); - }, - setup: function PDFDocument_setup(recoveryMode) { - this.xref.parse(recoveryMode); - var self = this; - this.catalog = new Catalog(this.pdfManager, this.xref, false); - }, - get numPages() { - var linearization = this.linearization; - var num = linearization ? linearization.numPages : this.catalog.numPages; - // shadow the prototype getter - return shadow(this, 'numPages', num); - }, - get documentInfo() { - var docInfo = { - PDFFormatVersion: this.pdfFormatVersion, - IsAcroFormPresent: !!this.acroForm, - IsXFAPresent: !!this.xfa - }; - var infoDict; - try { - infoDict = this.xref.trailer.get('Info'); - } catch (err) { - info('The document information dictionary is invalid.'); - } - if (infoDict) { - var validEntries = DocumentInfoValidators.entries; - // Only fill the document info with valid entries from the spec. - for (var key in validEntries) { - if (infoDict.has(key)) { - var value = infoDict.get(key); - // Make sure the value conforms to the spec. - if (validEntries[key](value)) { - docInfo[key] = (typeof value !== 'string' ? - value : stringToPDFString(value)); - } else { - info('Bad value in document info for "' + key + '"'); - } - } - } - } - return shadow(this, 'documentInfo', docInfo); - }, - get fingerprint() { - var xref = this.xref, hash, fileID = ''; - var idArray = xref.trailer.get('ID'); - - if (idArray && isArray(idArray) && idArray[0] && isString(idArray[0]) && - idArray[0] !== EMPTY_FINGERPRINT) { - hash = stringToBytes(idArray[0]); - } else { - if (this.stream.ensureRange) { - this.stream.ensureRange(0, - Math.min(FINGERPRINT_FIRST_BYTES, this.stream.end)); - } - hash = calculateMD5(this.stream.bytes.subarray(0, - FINGERPRINT_FIRST_BYTES), 0, FINGERPRINT_FIRST_BYTES); - } - - for (var i = 0, n = hash.length; i < n; i++) { - var hex = hash[i].toString(16); - fileID += hex.length === 1 ? '0' + hex : hex; - } - - return shadow(this, 'fingerprint', fileID); - }, - - getPage: function PDFDocument_getPage(pageIndex) { - return this.catalog.getPage(pageIndex); - }, - - cleanup: function PDFDocument_cleanup() { - return this.catalog.cleanup(); - } - }; - - return PDFDocument; -})(); - -exports.PDFDocument = PDFDocument; -})); +/* Copyright 2015 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* globals global */ + +"use strict"; + +(function(root, factory) { + //if (typeof define === 'function' && define.amd) { + // define('pdfjs/shared/global', ['exports'], factory); + // } else if (typeof exports !== 'undefined') { + // factory(exports); + //} else { + factory((root.pdfjsSharedGlobal = {})); + //} +})(window, function(exports) { + let globalScope = + typeof window !== "undefined" + ? window + : typeof global !== "undefined" + ? global + : typeof self !== "undefined" + ? self + : this; + + let isWorker = typeof window === "undefined"; + + // The global PDFJS object exposes the API + // In production, it will be declared outside a global wrapper + // In development, it will be declared here + if (!globalScope.PDFJS) { + globalScope.PDFJS = {}; + } + + globalScope.PDFJS.pdfBug = false; + + exports.globalScope = globalScope; + exports.isWorker = isWorker; + exports.PDFJS = globalScope.PDFJS; +}); + +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* globals MozBlobBuilder, URL */ + +("use strict"); + +(function(root, factory) { + //if (typeof define === 'function' && define.amd) { + // define('pdfjs/shared/util', ['exports', 'pdfjs/shared/global'], factory); + // } else if (typeof exports !== 'undefined') { + // factory(exports, require('./global.js')); + //} else { + factory((root.pdfjsSharedUtil = {}), root.pdfjsSharedGlobal); + //} +})(window, function(exports, sharedGlobal) { + let PDFJS = sharedGlobal.PDFJS; + let globalScope = sharedGlobal.globalScope; + + let FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; + + let TextRenderingMode = { + FILL: 0, + STROKE: 1, + FILL_STROKE: 2, + INVISIBLE: 3, + FILL_ADD_TO_PATH: 4, + STROKE_ADD_TO_PATH: 5, + FILL_STROKE_ADD_TO_PATH: 6, + ADD_TO_PATH: 7, + FILL_STROKE_MASK: 3, + ADD_TO_PATH_FLAG: 4 + }; + + let ImageKind = { + GRAYSCALE_1BPP: 1, + RGB_24BPP: 2, + RGBA_32BPP: 3 + }; + + let AnnotationType = { + TEXT: 1, + LINK: 2, + FREETEXT: 3, + LINE: 4, + SQUARE: 5, + CIRCLE: 6, + POLYGON: 7, + POLYLINE: 8, + HIGHLIGHT: 9, + UNDERLINE: 10, + SQUIGGLY: 11, + STRIKEOUT: 12, + STAMP: 13, + CARET: 14, + INK: 15, + POPUP: 16, + FILEATTACHMENT: 17, + SOUND: 18, + MOVIE: 19, + WIDGET: 20, + SCREEN: 21, + PRINTERMARK: 22, + TRAPNET: 23, + WATERMARK: 24, + THREED: 25, + REDACT: 26 + }; + + let AnnotationFlag = { + INVISIBLE: 0x01, + HIDDEN: 0x02, + PRINT: 0x04, + NOZOOM: 0x08, + NOROTATE: 0x10, + NOVIEW: 0x20, + READONLY: 0x40, + LOCKED: 0x80, + TOGGLENOVIEW: 0x100, + LOCKEDCONTENTS: 0x200 + }; + + let AnnotationBorderStyleType = { + SOLID: 1, + DASHED: 2, + BEVELED: 3, + INSET: 4, + UNDERLINE: 5 + }; + + let StreamType = { + UNKNOWN: 0, + FLATE: 1, + LZW: 2, + DCT: 3, + JPX: 4, + JBIG: 5, + A85: 6, + AHX: 7, + CCF: 8, + RL: 9 + }; + + let FontType = { + UNKNOWN: 0, + TYPE1: 1, + TYPE1C: 2, + CIDFONTTYPE0: 3, + CIDFONTTYPE0C: 4, + TRUETYPE: 5, + CIDFONTTYPE2: 6, + TYPE3: 7, + OPENTYPE: 8, + TYPE0: 9, + MMTYPE1: 10 + }; + + PDFJS.VERBOSITY_LEVELS = { + errors: 0, + warnings: 1, + infos: 5 + }; + + // All the possible operations for an operator list. + let OPS = (PDFJS.OPS = { + // Intentionally start from 1 so it is easy to spot bad operators that will be + // 0's. + dependency: 1, + setLineWidth: 2, + setLineCap: 3, + setLineJoin: 4, + setMiterLimit: 5, + setDash: 6, + setRenderingIntent: 7, + setFlatness: 8, + setGState: 9, + save: 10, + restore: 11, + transform: 12, + moveTo: 13, + lineTo: 14, + curveTo: 15, + curveTo2: 16, + curveTo3: 17, + closePath: 18, + rectangle: 19, + stroke: 20, + closeStroke: 21, + fill: 22, + eoFill: 23, + fillStroke: 24, + eoFillStroke: 25, + closeFillStroke: 26, + closeEOFillStroke: 27, + endPath: 28, + clip: 29, + eoClip: 30, + beginText: 31, + endText: 32, + setCharSpacing: 33, + setWordSpacing: 34, + setHScale: 35, + setLeading: 36, + setFont: 37, + setTextRenderingMode: 38, + setTextRise: 39, + moveText: 40, + setLeadingMoveText: 41, + setTextMatrix: 42, + nextLine: 43, + showText: 44, + showSpacedText: 45, + nextLineShowText: 46, + nextLineSetSpacingShowText: 47, + setCharWidth: 48, + setCharWidthAndBounds: 49, + setStrokeColorSpace: 50, + setFillColorSpace: 51, + setStrokeColor: 52, + setStrokeColorN: 53, + setFillColor: 54, + setFillColorN: 55, + setStrokeGray: 56, + setFillGray: 57, + setStrokeRGBColor: 58, + setFillRGBColor: 59, + setStrokeCMYKColor: 60, + setFillCMYKColor: 61, + shadingFill: 62, + beginInlineImage: 63, + beginImageData: 64, + endInlineImage: 65, + paintXObject: 66, + markPoint: 67, + markPointProps: 68, + beginMarkedContent: 69, + beginMarkedContentProps: 70, + endMarkedContent: 71, + beginCompat: 72, + endCompat: 73, + paintFormXObjectBegin: 74, + paintFormXObjectEnd: 75, + beginGroup: 76, + endGroup: 77, + beginAnnotations: 78, + endAnnotations: 79, + beginAnnotation: 80, + endAnnotation: 81, + paintJpegXObject: 82, + paintImageMaskXObject: 83, + paintImageMaskXObjectGroup: 84, + paintImageXObject: 85, + paintInlineImageXObject: 86, + paintInlineImageXObjectGroup: 87, + paintImageXObjectRepeat: 88, + paintImageMaskXObjectRepeat: 89, + paintSolidColorImageMask: 90, + constructPath: 91 + }); + + // A notice for devs. These are good for things that are helpful to devs, such + // as warning that Workers were disabled, which is important to devs but not + // end users. + function info(msg) { + if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.infos) { + console.log("Info: " + msg); + } + } + + // Non-fatal warnings. + function warn(msg) { + if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.warnings) { + console.log("Warning: " + msg); + } + } + + // Deprecated API function -- treated as warnings. + function deprecated(details) { + warn("Deprecated API usage: " + details); + } + + // Fatal errors that should trigger the fallback UI and halt execution by + // throwing an exception. + function error(msg) { + if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.errors) { + console.log("Error: " + msg); + console.log(backtrace()); + } + throw new Error(msg); + } + + function backtrace() { + try { + throw new Error(); + } catch (e) { + return e.stack + ? e.stack + .split("\n") + .slice(2) + .join("\n") + : ""; + } + } + + function assert(cond, msg) { + if (!cond) { + error(msg); + } + } + + let UNSUPPORTED_FEATURES = (PDFJS.UNSUPPORTED_FEATURES = { + unknown: "unknown", + forms: "forms", + javaScript: "javaScript", + smask: "smask", + shadingPattern: "shadingPattern", + font: "font" + }); + + // Combines two URLs. The baseUrl shall be absolute URL. If the url is an + // absolute URL, it will be returned as is. + function combineUrl(baseUrl, url) { + if (!url) { + return baseUrl; + } + if (/^[a-z][a-z0-9+\-.]*:/i.test(url)) { + return url; + } + let i; + if (url.charAt(0) === "/") { + // absolute path + i = baseUrl.indexOf("://"); + if (url.charAt(1) === "/") { + ++i; + } else { + i = baseUrl.indexOf("/", i + 3); + } + return baseUrl.substring(0, i) + url; + } else { + // relative path + let pathLength = baseUrl.length; + i = baseUrl.lastIndexOf("#"); + pathLength = i >= 0 ? i : pathLength; + i = baseUrl.lastIndexOf("?", pathLength); + pathLength = i >= 0 ? i : pathLength; + let prefixLength = baseUrl.lastIndexOf("/", pathLength); + return baseUrl.substring(0, prefixLength + 1) + url; + } + } + + // Validates if URL is safe and allowed, e.g. to avoid XSS. + function isValidUrl(url, allowRelative) { + if (!url) { + return false; + } + // RFC 3986 (http://tools.ietf.org/html/rfc3986#section-3.1) + // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + let protocol = /^[a-z][a-z0-9+\-.]*(?=:)/i.exec(url); + if (!protocol) { + return allowRelative; + } + protocol = protocol[0].toLowerCase(); + switch (protocol) { + case "http": + case "https": + case "ftp": + case "mailto": + case "tel": + return true; + default: + return false; + } + } + PDFJS.isValidUrl = isValidUrl; + + function shadow(obj, prop, value) { + Object.defineProperty(obj, prop, { + value: value, + enumerable: true, + configurable: true, + writable: false + }); + return value; + } + PDFJS.shadow = shadow; + + let LinkTarget = (PDFJS.LinkTarget = { + NONE: 0, // Default value. + SELF: 1, + BLANK: 2, + PARENT: 3, + TOP: 4 + }); + let LinkTargetStringMap = ["", "_self", "_blank", "_parent", "_top"]; + + function isExternalLinkTargetSet() { + //#if !MOZCENTRAL + if (PDFJS.openExternalLinksInNewWindow) { + deprecated( + "PDFJS.openExternalLinksInNewWindow, please use " + + '"PDFJS.externalLinkTarget = PDFJS.LinkTarget.BLANK" instead.' + ); + if (PDFJS.externalLinkTarget === LinkTarget.NONE) { + PDFJS.externalLinkTarget = LinkTarget.BLANK; + } + // Reset the deprecated parameter, to suppress further warnings. + PDFJS.openExternalLinksInNewWindow = false; + } + //#endif + switch (PDFJS.externalLinkTarget) { + case LinkTarget.NONE: + return false; + case LinkTarget.SELF: + case LinkTarget.BLANK: + case LinkTarget.PARENT: + case LinkTarget.TOP: + return true; + } + warn("PDFJS.externalLinkTarget is invalid: " + PDFJS.externalLinkTarget); + // Reset the external link target, to suppress further warnings. + PDFJS.externalLinkTarget = LinkTarget.NONE; + return false; + } + PDFJS.isExternalLinkTargetSet = isExternalLinkTargetSet; + + let PasswordResponses = (PDFJS.PasswordResponses = { + NEED_PASSWORD: 1, + INCORRECT_PASSWORD: 2 + }); + + let PasswordException = (function PasswordExceptionClosure() { + function PasswordException(msg, code) { + this.name = "PasswordException"; + this.message = msg; + this.code = code; + } + + PasswordException.prototype = new Error(); + PasswordException.constructor = PasswordException; + + return PasswordException; + })(); + PDFJS.PasswordException = PasswordException; + + let UnknownErrorException = (function UnknownErrorExceptionClosure() { + function UnknownErrorException(msg, details) { + this.name = "UnknownErrorException"; + this.message = msg; + this.details = details; + } + + UnknownErrorException.prototype = new Error(); + UnknownErrorException.constructor = UnknownErrorException; + + return UnknownErrorException; + })(); + PDFJS.UnknownErrorException = UnknownErrorException; + + let InvalidPDFException = (function InvalidPDFExceptionClosure() { + function InvalidPDFException(msg) { + this.name = "InvalidPDFException"; + this.message = msg; + } + + InvalidPDFException.prototype = new Error(); + InvalidPDFException.constructor = InvalidPDFException; + + return InvalidPDFException; + })(); + PDFJS.InvalidPDFException = InvalidPDFException; + + let MissingPDFException = (function MissingPDFExceptionClosure() { + function MissingPDFException(msg) { + this.name = "MissingPDFException"; + this.message = msg; + } + + MissingPDFException.prototype = new Error(); + MissingPDFException.constructor = MissingPDFException; + + return MissingPDFException; + })(); + PDFJS.MissingPDFException = MissingPDFException; + + let UnexpectedResponseException = (function UnexpectedResponseExceptionClosure() { + function UnexpectedResponseException(msg, status) { + this.name = "UnexpectedResponseException"; + this.message = msg; + this.status = status; + } + + UnexpectedResponseException.prototype = new Error(); + UnexpectedResponseException.constructor = UnexpectedResponseException; + + return UnexpectedResponseException; + })(); + PDFJS.UnexpectedResponseException = UnexpectedResponseException; + + let NotImplementedException = (function NotImplementedExceptionClosure() { + function NotImplementedException(msg) { + this.message = msg; + } + + NotImplementedException.prototype = new Error(); + NotImplementedException.prototype.name = "NotImplementedException"; + NotImplementedException.constructor = NotImplementedException; + + return NotImplementedException; + })(); + + let MissingDataException = (function MissingDataExceptionClosure() { + function MissingDataException(begin, end) { + this.begin = begin; + this.end = end; + this.message = "Missing data [" + begin + ", " + end + ")"; + } + + MissingDataException.prototype = new Error(); + MissingDataException.prototype.name = "MissingDataException"; + MissingDataException.constructor = MissingDataException; + + return MissingDataException; + })(); + + let XRefParseException = (function XRefParseExceptionClosure() { + function XRefParseException(msg) { + this.message = msg; + } + + XRefParseException.prototype = new Error(); + XRefParseException.prototype.name = "XRefParseException"; + XRefParseException.constructor = XRefParseException; + + return XRefParseException; + })(); + + function bytesToString(bytes) { + assert( + bytes !== null && typeof bytes === "object" && bytes.length !== undefined, + "Invalid argument for bytesToString" + ); + let length = bytes.length; + let MAX_ARGUMENT_COUNT = 8192; + if (length < MAX_ARGUMENT_COUNT) { + return String.fromCharCode.apply(null, bytes); + } + let strBuf = []; + for (let i = 0; i < length; i += MAX_ARGUMENT_COUNT) { + let chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length); + let chunk = bytes.subarray(i, chunkEnd); + strBuf.push(String.fromCharCode.apply(null, chunk)); + } + return strBuf.join(""); + } + + function stringToBytes(str) { + assert(typeof str === "string", "Invalid argument for stringToBytes"); + let length = str.length; + let bytes = new Uint8Array(length); + for (let i = 0; i < length; ++i) { + bytes[i] = str.charCodeAt(i) & 0xff; + } + return bytes; + } + + function string32(value) { + return String.fromCharCode( + (value >> 24) & 0xff, + (value >> 16) & 0xff, + (value >> 8) & 0xff, + value & 0xff + ); + } + + function log2(x) { + let n = 1, + i = 0; + while (x > n) { + n <<= 1; + i++; + } + return i; + } + + function readInt8(data, start) { + return (data[start] << 24) >> 24; + } + + function readUint16(data, offset) { + return (data[offset] << 8) | data[offset + 1]; + } + + function readUint32(data, offset) { + return ( + ((data[offset] << 24) | + (data[offset + 1] << 16) | + (data[offset + 2] << 8) | + data[offset + 3]) >>> + 0 + ); + } + + // Lazy test the endianness of the platform + // NOTE: This will be 'true' for simulated TypedArrays + function isLittleEndian() { + let buffer8 = new Uint8Array(2); + buffer8[0] = 1; + let buffer16 = new Uint16Array(buffer8.buffer); + return buffer16[0] === 1; + } + + Object.defineProperty(PDFJS, "isLittleEndian", { + configurable: true, + get: function PDFJS_isLittleEndian() { + return shadow(PDFJS, "isLittleEndian", isLittleEndian()); + } + }); + + //#if !(FIREFOX || MOZCENTRAL || CHROME) + //// Lazy test if the userAgent support CanvasTypedArrays + function hasCanvasTypedArrays() { + let canvas = document.createElement("canvas"); + canvas.width = canvas.height = 1; + let ctx = canvas.getContext("2d"); + let imageData = ctx.createImageData(1, 1); + return typeof imageData.data.buffer !== "undefined"; + } + + Object.defineProperty(PDFJS, "hasCanvasTypedArrays", { + configurable: true, + get: function PDFJS_hasCanvasTypedArrays() { + return shadow(PDFJS, "hasCanvasTypedArrays", hasCanvasTypedArrays()); + } + }); + + let Uint32ArrayView = (function Uint32ArrayViewClosure() { + function Uint32ArrayView(buffer, length) { + this.buffer = buffer; + this.byteLength = buffer.length; + this.length = length === undefined ? this.byteLength >> 2 : length; + ensureUint32ArrayViewProps(this.length); + } + Uint32ArrayView.prototype = Object.create(null); + + let uint32ArrayViewSetters = 0; + function createUint32ArrayProp(index) { + return { + get: function() { + let buffer = this.buffer, + offset = index << 2; + return ( + (buffer[offset] | + (buffer[offset + 1] << 8) | + (buffer[offset + 2] << 16) | + (buffer[offset + 3] << 24)) >>> + 0 + ); + }, + set: function(value) { + let buffer = this.buffer, + offset = index << 2; + buffer[offset] = value & 255; + buffer[offset + 1] = (value >> 8) & 255; + buffer[offset + 2] = (value >> 16) & 255; + buffer[offset + 3] = (value >>> 24) & 255; + } + }; + } + + function ensureUint32ArrayViewProps(length) { + while (uint32ArrayViewSetters < length) { + Object.defineProperty( + Uint32ArrayView.prototype, + uint32ArrayViewSetters, + createUint32ArrayProp(uint32ArrayViewSetters) + ); + uint32ArrayViewSetters++; + } + } + + return Uint32ArrayView; + })(); + + exports.Uint32ArrayView = Uint32ArrayView; + //#else + //PDFJS.hasCanvasTypedArrays = true; + //#endif + + let IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; + + let Util = (PDFJS.Util = (function UtilClosure() { + function Util() {} + + var rgbBuf = ["rgb(", 0, ",", 0, ",", 0, ")"]; + + // makeCssRgb() can be called thousands of times. Using |rgbBuf| avoids + // creating many intermediate strings. + Util.makeCssRgb = function Util_makeCssRgb(r, g, b) { + rgbBuf[1] = r; + rgbBuf[3] = g; + rgbBuf[5] = b; + return rgbBuf.join(""); + }; + + // Concatenates two transformation matrices together and returns the result. + Util.transform = function Util_transform(m1, m2) { + return [ + m1[0] * m2[0] + m1[2] * m2[1], + m1[1] * m2[0] + m1[3] * m2[1], + m1[0] * m2[2] + m1[2] * m2[3], + m1[1] * m2[2] + m1[3] * m2[3], + m1[0] * m2[4] + m1[2] * m2[5] + m1[4], + m1[1] * m2[4] + m1[3] * m2[5] + m1[5] + ]; + }; + + // For 2d affine transforms + Util.applyTransform = function Util_applyTransform(p, m) { + var xt = p[0] * m[0] + p[1] * m[2] + m[4]; + var yt = p[0] * m[1] + p[1] * m[3] + m[5]; + return [xt, yt]; + }; + + Util.applyInverseTransform = function Util_applyInverseTransform(p, m) { + var d = m[0] * m[3] - m[1] * m[2]; + var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d; + var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d; + return [xt, yt]; + }; + + // Applies the transform to the rectangle and finds the minimum axially + // aligned bounding box. + Util.getAxialAlignedBoundingBox = function Util_getAxialAlignedBoundingBox( + r, + m + ) { + var p1 = Util.applyTransform(r, m); + var p2 = Util.applyTransform(r.slice(2, 4), m); + var p3 = Util.applyTransform([r[0], r[3]], m); + var p4 = Util.applyTransform([r[2], r[1]], m); + return [ + Math.min(p1[0], p2[0], p3[0], p4[0]), + Math.min(p1[1], p2[1], p3[1], p4[1]), + Math.max(p1[0], p2[0], p3[0], p4[0]), + Math.max(p1[1], p2[1], p3[1], p4[1]) + ]; + }; + + Util.inverseTransform = function Util_inverseTransform(m) { + var d = m[0] * m[3] - m[1] * m[2]; + return [ + m[3] / d, + -m[1] / d, + -m[2] / d, + m[0] / d, + (m[2] * m[5] - m[4] * m[3]) / d, + (m[4] * m[1] - m[5] * m[0]) / d + ]; + }; + + // Apply a generic 3d matrix M on a 3-vector v: + // | a b c | | X | + // | d e f | x | Y | + // | g h i | | Z | + // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i], + // with v as [X,Y,Z] + Util.apply3dTransform = function Util_apply3dTransform(m, v) { + return [ + m[0] * v[0] + m[1] * v[1] + m[2] * v[2], + m[3] * v[0] + m[4] * v[1] + m[5] * v[2], + m[6] * v[0] + m[7] * v[1] + m[8] * v[2] + ]; + }; + + // This calculation uses Singular Value Decomposition. + // The SVD can be represented with formula A = USV. We are interested in the + // matrix S here because it represents the scale values. + Util.singularValueDecompose2dScale = function Util_singularValueDecompose2dScale( + m + ) { + var transpose = [m[0], m[2], m[1], m[3]]; + + // Multiply matrix m with its transpose. + var a = m[0] * transpose[0] + m[1] * transpose[2]; + var b = m[0] * transpose[1] + m[1] * transpose[3]; + var c = m[2] * transpose[0] + m[3] * transpose[2]; + var d = m[2] * transpose[1] + m[3] * transpose[3]; + + // Solve the second degree polynomial to get roots. + var first = (a + d) / 2; + var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2; + var sx = first + second || 1; + var sy = first - second || 1; + + // Scale values are the square roots of the eigenvalues. + return [Math.sqrt(sx), Math.sqrt(sy)]; + }; + + // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2) + // For coordinate systems whose origin lies in the bottom-left, this + // means normalization to (BL,TR) ordering. For systems with origin in the + // top-left, this means (TL,BR) ordering. + Util.normalizeRect = function Util_normalizeRect(rect) { + var r = rect.slice(0); // clone rect + if (rect[0] > rect[2]) { + r[0] = rect[2]; + r[2] = rect[0]; + } + if (rect[1] > rect[3]) { + r[1] = rect[3]; + r[3] = rect[1]; + } + return r; + }; + + // Returns a rectangle [x1, y1, x2, y2] corresponding to the + // intersection of rect1 and rect2. If no intersection, returns 'false' + // The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2] + Util.intersect = function Util_intersect(rect1, rect2) { + function compare(a, b) { + return a - b; + } + + // Order points along the axes + var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare), + orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare), + result = []; + + rect1 = Util.normalizeRect(rect1); + rect2 = Util.normalizeRect(rect2); + + // X: first and second points belong to different rectangles? + if ( + (orderedX[0] === rect1[0] && orderedX[1] === rect2[0]) || + (orderedX[0] === rect2[0] && orderedX[1] === rect1[0]) + ) { + // Intersection must be between second and third points + result[0] = orderedX[1]; + result[2] = orderedX[2]; + } else { + return false; + } + + // Y: first and second points belong to different rectangles? + if ( + (orderedY[0] === rect1[1] && orderedY[1] === rect2[1]) || + (orderedY[0] === rect2[1] && orderedY[1] === rect1[1]) + ) { + // Intersection must be between second and third points + result[1] = orderedY[1]; + result[3] = orderedY[2]; + } else { + return false; + } + + return result; + }; + + Util.sign = function Util_sign(num) { + return num < 0 ? -1 : 1; + }; + + Util.appendToArray = function Util_appendToArray(arr1, arr2) { + Array.prototype.push.apply(arr1, arr2); + }; + + Util.prependToArray = function Util_prependToArray(arr1, arr2) { + Array.prototype.unshift.apply(arr1, arr2); + }; + + Util.extendObj = function extendObj(obj1, obj2) { + for (var key in obj2) { + obj1[key] = obj2[key]; + } + }; + + Util.getInheritableProperty = function Util_getInheritableProperty( + dict, + name + ) { + while (dict && !dict.has(name)) { + dict = dict.get("Parent"); + } + if (!dict) { + return null; + } + return dict.get(name); + }; + + Util.inherit = function Util_inherit(sub, base, prototype) { + sub.prototype = Object.create(base.prototype); + sub.prototype.constructor = sub; + for (var prop in prototype) { + sub.prototype[prop] = prototype[prop]; + } + }; + + Util.loadScript = function Util_loadScript(src, callback) { + var script = document.createElement("script"); + var loaded = false; + script.setAttribute("src", src); + if (callback) { + script.onload = function() { + if (!loaded) { + callback(); + } + loaded = true; + }; + } + document.getElementsByTagName("head")[0].appendChild(script); + }; + + return Util; + })()); + + /** + * PDF page viewport created based on scale, rotation and offset. + * @class + * @alias PDFJS.PageViewport + */ + var PageViewport = (PDFJS.PageViewport = (function PageViewportClosure() { + /** + * @constructor + * @private + * @param viewBox {Array} xMin, yMin, xMax and yMax coordinates. + * @param scale {number} scale of the viewport. + * @param rotation {number} rotations of the viewport in degrees. + * @param offsetX {number} offset X + * @param offsetY {number} offset Y + * @param dontFlip {boolean} if true, axis Y will not be flipped. + */ + function PageViewport( + viewBox, + scale, + rotation, + offsetX, + offsetY, + dontFlip + ) { + this.viewBox = viewBox; + this.scale = scale; + this.rotation = rotation; + this.offsetX = offsetX; + this.offsetY = offsetY; + + // creating transform to convert pdf coordinate system to the normal + // canvas like coordinates taking in account scale and rotation + var centerX = (viewBox[2] + viewBox[0]) / 2; + var centerY = (viewBox[3] + viewBox[1]) / 2; + var rotateA, rotateB, rotateC, rotateD; + rotation = rotation % 360; + rotation = rotation < 0 ? rotation + 360 : rotation; + switch (rotation) { + case 180: + rotateA = -1; + rotateB = 0; + rotateC = 0; + rotateD = 1; + break; + case 90: + rotateA = 0; + rotateB = 1; + rotateC = 1; + rotateD = 0; + break; + case 270: + rotateA = 0; + rotateB = -1; + rotateC = -1; + rotateD = 0; + break; + //case 0: + default: + rotateA = 1; + rotateB = 0; + rotateC = 0; + rotateD = -1; + break; + } + + if (dontFlip) { + rotateC = -rotateC; + rotateD = -rotateD; + } + + var offsetCanvasX, offsetCanvasY; + var width, height; + if (rotateA === 0) { + offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX; + offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY; + width = Math.abs(viewBox[3] - viewBox[1]) * scale; + height = Math.abs(viewBox[2] - viewBox[0]) * scale; + } else { + offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX; + offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY; + width = Math.abs(viewBox[2] - viewBox[0]) * scale; + height = Math.abs(viewBox[3] - viewBox[1]) * scale; + } + // creating transform for the following operations: + // translate(-centerX, -centerY), rotate and flip vertically, + // scale, and translate(offsetCanvasX, offsetCanvasY) + this.transform = [ + rotateA * scale, + rotateB * scale, + rotateC * scale, + rotateD * scale, + offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, + offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY + ]; + + this.width = width; + this.height = height; + this.fontScale = scale; + } + PageViewport.prototype = /** @lends PDFJS.PageViewport.prototype */ { + /** + * Clones viewport with additional properties. + * @param args {Object} (optional) If specified, may contain the 'scale' or + * 'rotation' properties to override the corresponding properties in + * the cloned viewport. + * @returns {PDFJS.PageViewport} Cloned viewport. + */ + clone: function PageViewPort_clone(args) { + args = args || {}; + var scale = "scale" in args ? args.scale : this.scale; + var rotation = "rotation" in args ? args.rotation : this.rotation; + return new PageViewport( + this.viewBox.slice(), + scale, + rotation, + this.offsetX, + this.offsetY, + args.dontFlip + ); + }, + /** + * Converts PDF point to the viewport coordinates. For examples, useful for + * converting PDF location into canvas pixel coordinates. + * @param x {number} X coordinate. + * @param y {number} Y coordinate. + * @returns {Object} Object that contains 'x' and 'y' properties of the + * point in the viewport coordinate space. + * @see {@link convertToPdfPoint} + * @see {@link convertToViewportRectangle} + */ + convertToViewportPoint: function PageViewport_convertToViewportPoint( + x, + y + ) { + return Util.applyTransform([x, y], this.transform); + }, + /** + * Converts PDF rectangle to the viewport coordinates. + * @param rect {Array} xMin, yMin, xMax and yMax coordinates. + * @returns {Array} Contains corresponding coordinates of the rectangle + * in the viewport coordinate space. + * @see {@link convertToViewportPoint} + */ + convertToViewportRectangle: function PageViewport_convertToViewportRectangle( + rect + ) { + var tl = Util.applyTransform([rect[0], rect[1]], this.transform); + var br = Util.applyTransform([rect[2], rect[3]], this.transform); + return [tl[0], tl[1], br[0], br[1]]; + }, + /** + * Converts viewport coordinates to the PDF location. For examples, useful + * for converting canvas pixel location into PDF one. + * @param x {number} X coordinate. + * @param y {number} Y coordinate. + * @returns {Object} Object that contains 'x' and 'y' properties of the + * point in the PDF coordinate space. + * @see {@link convertToViewportPoint} + */ + convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) { + return Util.applyInverseTransform([x, y], this.transform); + } + }; + return PageViewport; + })()); + + var PDFStringTranslateTable = [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0x2d8, + 0x2c7, + 0x2c6, + 0x2d9, + 0x2dd, + 0x2db, + 0x2da, + 0x2dc, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0x2022, + 0x2020, + 0x2021, + 0x2026, + 0x2014, + 0x2013, + 0x192, + 0x2044, + 0x2039, + 0x203a, + 0x2212, + 0x2030, + 0x201e, + 0x201c, + 0x201d, + 0x2018, + 0x2019, + 0x201a, + 0x2122, + 0xfb01, + 0xfb02, + 0x141, + 0x152, + 0x160, + 0x178, + 0x17d, + 0x131, + 0x142, + 0x153, + 0x161, + 0x17e, + 0, + 0x20ac + ]; + + function stringToPDFString(str) { + var i, + n = str.length, + strBuf = []; + if (str[0] === "\xFE" && str[1] === "\xFF") { + // UTF16BE BOM + for (i = 2; i < n; i += 2) { + strBuf.push( + String.fromCharCode((str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)) + ); + } + } else { + for (i = 0; i < n; ++i) { + var code = PDFStringTranslateTable[str.charCodeAt(i)]; + strBuf.push(code ? String.fromCharCode(code) : str.charAt(i)); + } + } + return strBuf.join(""); + } + + function stringToUTF8String(str) { + return decodeURIComponent(escape(str)); + } + + function utf8StringToString(str) { + return unescape(encodeURIComponent(str)); + } + + function isEmptyObj(obj) { + for (var key in obj) { + return false; + } + return true; + } + + function isBool(v) { + return typeof v === "boolean"; + } + + function isInt(v) { + return typeof v === "number" && (v | 0) === v; + } + + function isNum(v) { + return typeof v === "number"; + } + + function isString(v) { + return typeof v === "string"; + } + + function isArray(v) { + return v instanceof Array; + } + + function isArrayBuffer(v) { + return typeof v === "object" && v !== null && v.byteLength !== undefined; + } + + /** + * Promise Capability object. + * + * @typedef {Object} PromiseCapability + * @property {Promise} promise - A promise object. + * @property {function} resolve - Fullfills the promise. + * @property {function} reject - Rejects the promise. + */ + + /** + * Creates a promise capability object. + * @alias PDFJS.createPromiseCapability + * + * @return {PromiseCapability} A capability object contains: + * - a Promise, resolve and reject methods. + */ + function createPromiseCapability() { + var capability = {}; + capability.promise = new Promise(function(resolve, reject) { + capability.resolve = resolve; + capability.reject = reject; + }); + return capability; + } + + PDFJS.createPromiseCapability = createPromiseCapability; + + /** + * Polyfill for Promises: + * The following promise implementation tries to generally implement the + * Promise/A+ spec. Some notable differences from other promise libaries are: + * - There currently isn't a seperate deferred and promise object. + * - Unhandled rejections eventually show an error if they aren't handled. + * + * Based off of the work in: + * https://bugzilla.mozilla.org/show_bug.cgi?id=810490 + */ + (function PromiseClosure() { + if (globalScope.Promise) { + // Promises existing in the DOM/Worker, checking presence of all/resolve + if (typeof globalScope.Promise.all !== "function") { + globalScope.Promise.all = function(iterable) { + var count = 0, + results = [], + resolve, + reject; + var promise = new globalScope.Promise(function(resolve_, reject_) { + resolve = resolve_; + reject = reject_; + }); + iterable.forEach(function(p, i) { + count++; + p.then(function(result) { + results[i] = result; + count--; + if (count === 0) { + resolve(results); + } + }, reject); + }); + if (count === 0) { + resolve(results); + } + return promise; + }; + } + if (typeof globalScope.Promise.resolve !== "function") { + globalScope.Promise.resolve = function(value) { + return new globalScope.Promise(function(resolve) { + resolve(value); + }); + }; + } + if (typeof globalScope.Promise.reject !== "function") { + globalScope.Promise.reject = function(reason) { + return new globalScope.Promise(function(resolve, reject) { + reject(reason); + }); + }; + } + if (typeof globalScope.Promise.prototype.catch !== "function") { + globalScope.Promise.prototype.catch = function(onReject) { + return globalScope.Promise.prototype.then(undefined, onReject); + }; + } + return; + } + //#if !MOZCENTRAL + var STATUS_PENDING = 0; + var STATUS_RESOLVED = 1; + var STATUS_REJECTED = 2; + + // In an attempt to avoid silent exceptions, unhandled rejections are + // tracked and if they aren't handled in a certain amount of time an + // error is logged. + var REJECTION_TIMEOUT = 500; + + var HandlerManager = { + handlers: [], + running: false, + unhandledRejections: [], + pendingRejectionCheck: false, + + scheduleHandlers: function scheduleHandlers(promise) { + if (promise._status === STATUS_PENDING) { + return; + } + + this.handlers = this.handlers.concat(promise._handlers); + promise._handlers = []; + + if (this.running) { + return; + } + this.running = true; + + setTimeout(this.runHandlers.bind(this), 0); + }, + + runHandlers: function runHandlers() { + var RUN_TIMEOUT = 1; // ms + var timeoutAt = Date.now() + RUN_TIMEOUT; + while (this.handlers.length > 0) { + var handler = this.handlers.shift(); + + var nextStatus = handler.thisPromise._status; + var nextValue = handler.thisPromise._value; + + try { + if (nextStatus === STATUS_RESOLVED) { + if (typeof handler.onResolve === "function") { + nextValue = handler.onResolve(nextValue); + } + } else if (typeof handler.onReject === "function") { + nextValue = handler.onReject(nextValue); + nextStatus = STATUS_RESOLVED; + + if (handler.thisPromise._unhandledRejection) { + this.removeUnhandeledRejection(handler.thisPromise); + } + } + } catch (ex) { + nextStatus = STATUS_REJECTED; + nextValue = ex; + } + + handler.nextPromise._updateStatus(nextStatus, nextValue); + if (Date.now() >= timeoutAt) { + break; + } + } + + if (this.handlers.length > 0) { + setTimeout(this.runHandlers.bind(this), 0); + return; + } + + this.running = false; + }, + + addUnhandledRejection: function addUnhandledRejection(promise) { + this.unhandledRejections.push({ + promise: promise, + time: Date.now() + }); + this.scheduleRejectionCheck(); + }, + + removeUnhandeledRejection: function removeUnhandeledRejection(promise) { + promise._unhandledRejection = false; + for (var i = 0; i < this.unhandledRejections.length; i++) { + if (this.unhandledRejections[i].promise === promise) { + this.unhandledRejections.splice(i); + i--; + } + } + }, + + scheduleRejectionCheck: function scheduleRejectionCheck() { + if (this.pendingRejectionCheck) { + return; + } + this.pendingRejectionCheck = true; + setTimeout( + function rejectionCheck() { + this.pendingRejectionCheck = false; + var now = Date.now(); + for (var i = 0; i < this.unhandledRejections.length; i++) { + if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) { + var unhandled = this.unhandledRejections[i].promise._value; + var msg = "Unhandled rejection: " + unhandled; + if (unhandled.stack) { + msg += "\n" + unhandled.stack; + } + warn(msg); + this.unhandledRejections.splice(i); + i--; + } + } + if (this.unhandledRejections.length) { + this.scheduleRejectionCheck(); + } + }.bind(this), + REJECTION_TIMEOUT + ); + } + }; + + function Promise(resolver) { + this._status = STATUS_PENDING; + this._handlers = []; + try { + resolver.call(this, this._resolve.bind(this), this._reject.bind(this)); + } catch (e) { + this._reject(e); + } + } + /** + * Builds a promise that is resolved when all the passed in promises are + * resolved. + * @param {array} array of data and/or promises to wait for. + * @return {Promise} New dependant promise. + */ + Promise.all = function Promise_all(promises) { + var resolveAll, rejectAll; + var deferred = new Promise(function(resolve, reject) { + resolveAll = resolve; + rejectAll = reject; + }); + var unresolved = promises.length; + var results = []; + if (unresolved === 0) { + resolveAll(results); + return deferred; + } + function reject(reason) { + if (deferred._status === STATUS_REJECTED) { + return; + } + results = []; + rejectAll(reason); + } + for (var i = 0, ii = promises.length; i < ii; ++i) { + var promise = promises[i]; + var resolve = (function(i) { + return function(value) { + if (deferred._status === STATUS_REJECTED) { + return; + } + results[i] = value; + unresolved--; + if (unresolved === 0) { + resolveAll(results); + } + }; + })(i); + if (Promise.isPromise(promise)) { + promise.then(resolve, reject); + } else { + resolve(promise); + } + } + return deferred; + }; + + /** + * Checks if the value is likely a promise (has a 'then' function). + * @return {boolean} true if value is thenable + */ + Promise.isPromise = function Promise_isPromise(value) { + return value && typeof value.then === "function"; + }; + + /** + * Creates resolved promise + * @param value resolve value + * @returns {Promise} + */ + Promise.resolve = function Promise_resolve(value) { + return new Promise(function(resolve) { + resolve(value); + }); + }; + + /** + * Creates rejected promise + * @param reason rejection value + * @returns {Promise} + */ + Promise.reject = function Promise_reject(reason) { + return new Promise(function(resolve, reject) { + reject(reason); + }); + }; + + Promise.prototype = { + _status: null, + _value: null, + _handlers: null, + _unhandledRejection: null, + + _updateStatus: function Promise__updateStatus(status, value) { + if ( + this._status === STATUS_RESOLVED || + this._status === STATUS_REJECTED + ) { + return; + } + + if (status === STATUS_RESOLVED && Promise.isPromise(value)) { + value.then( + this._updateStatus.bind(this, STATUS_RESOLVED), + this._updateStatus.bind(this, STATUS_REJECTED) + ); + return; + } + + this._status = status; + this._value = value; + + if (status === STATUS_REJECTED && this._handlers.length === 0) { + this._unhandledRejection = true; + HandlerManager.addUnhandledRejection(this); + } + + HandlerManager.scheduleHandlers(this); + }, + + _resolve: function Promise_resolve(value) { + this._updateStatus(STATUS_RESOLVED, value); + }, + + _reject: function Promise_reject(reason) { + this._updateStatus(STATUS_REJECTED, reason); + }, + + then: function Promise_then(onResolve, onReject) { + var nextPromise = new Promise(function(resolve, reject) { + this.resolve = resolve; + this.reject = reject; + }); + this._handlers.push({ + thisPromise: this, + onResolve: onResolve, + onReject: onReject, + nextPromise: nextPromise + }); + HandlerManager.scheduleHandlers(this); + return nextPromise; + }, + + catch: function Promise_catch(onReject) { + return this.then(undefined, onReject); + } + }; + + globalScope.Promise = Promise; + //#else + //throw new Error('DOM Promise is not present'); + //#endif + })(); + + var StatTimer = (function StatTimerClosure() { + function rpad(str, pad, length) { + while (str.length < length) { + str += pad; + } + return str; + } + function StatTimer() { + this.started = {}; + this.times = []; + this.enabled = true; + } + StatTimer.prototype = { + time: function StatTimer_time(name) { + if (!this.enabled) { + return; + } + if (name in this.started) { + warn("Timer is already running for " + name); + } + this.started[name] = Date.now(); + }, + timeEnd: function StatTimer_timeEnd(name) { + if (!this.enabled) { + return; + } + if (!(name in this.started)) { + warn("Timer has not been started for " + name); + } + this.times.push({ + name: name, + start: this.started[name], + end: Date.now() + }); + // Remove timer from started so it can be called again. + delete this.started[name]; + }, + toString: function StatTimer_toString() { + var i, ii; + var times = this.times; + var out = ""; + // Find the longest name for padding purposes. + var longest = 0; + for (i = 0, ii = times.length; i < ii; ++i) { + var name = times[i]["name"]; + if (name.length > longest) { + longest = name.length; + } + } + for (i = 0, ii = times.length; i < ii; ++i) { + var span = times[i]; + var duration = span.end - span.start; + out += rpad(span["name"], " ", longest) + " " + duration + "ms\n"; + } + return out; + } + }; + return StatTimer; + })(); + + PDFJS.createBlob = function createBlob(data, contentType) { + if (typeof Blob !== "undefined") { + return new Blob([data], { type: contentType }); + } + // Blob builder is deprecated in FF14 and removed in FF18. + var bb = new MozBlobBuilder(); + bb.append(data); + return bb.getBlob(contentType); + }; + + PDFJS.createObjectURL = (function createObjectURLClosure() { + // Blob/createObjectURL is not available, falling back to data schema. + var digits = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + + return function createObjectURL(data, contentType) { + if ( + !PDFJS.disableCreateObjectURL && + typeof URL !== "undefined" && + URL.createObjectURL + ) { + var blob = PDFJS.createBlob(data, contentType); + return URL.createObjectURL(blob); + } + + var buffer = "data:" + contentType + ";base64,"; + for (var i = 0, ii = data.length; i < ii; i += 3) { + var b1 = data[i] & 0xff; + var b2 = data[i + 1] & 0xff; + var b3 = data[i + 2] & 0xff; + var d1 = b1 >> 2, + d2 = ((b1 & 3) << 4) | (b2 >> 4); + var d3 = i + 1 < ii ? ((b2 & 0xf) << 2) | (b3 >> 6) : 64; + var d4 = i + 2 < ii ? b3 & 0x3f : 64; + buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4]; + } + return buffer; + }; + })(); + + function MessageHandler(sourceName, targetName, comObj) { + this.sourceName = sourceName; + this.targetName = targetName; + this.comObj = comObj; + this.callbackIndex = 1; + this.postMessageTransfers = true; + var callbacksCapabilities = (this.callbacksCapabilities = {}); + var ah = (this.actionHandler = {}); + + this._onComObjOnMessage = function messageHandlerComObjOnMessage(event) { + var data = event.data; + if (data.targetName !== this.sourceName) { + return; + } + if (data.isReply) { + var callbackId = data.callbackId; + if (data.callbackId in callbacksCapabilities) { + var callback = callbacksCapabilities[callbackId]; + delete callbacksCapabilities[callbackId]; + if ("error" in data) { + callback.reject(data.error); + } else { + callback.resolve(data.data); + } + } else { + error("Cannot resolve callback " + callbackId); + } + } else if (data.action in ah) { + var action = ah[data.action]; + if (data.callbackId) { + var sourceName = this.sourceName; + var targetName = data.sourceName; + Promise.resolve() + .then(function() { + return action[0].call(action[1], data.data); + }) + .then( + function(result) { + comObj.postMessage({ + sourceName: sourceName, + targetName: targetName, + isReply: true, + callbackId: data.callbackId, + data: result + }); + }, + function(reason) { + if (reason instanceof Error) { + // Serialize error to avoid "DataCloneError" + reason = reason + ""; + } + comObj.postMessage({ + sourceName: sourceName, + targetName: targetName, + isReply: true, + callbackId: data.callbackId, + error: reason + }); + } + ); + } else { + action[0].call(action[1], data.data); + } + } else { + error("Unknown action from worker: " + data.action); + } + }.bind(this); + comObj.addEventListener("message", this._onComObjOnMessage); + } + + MessageHandler.prototype = { + on: function messageHandlerOn(actionName, handler, scope) { + var ah = this.actionHandler; + if (ah[actionName]) { + error('There is already an actionName called "' + actionName + '"'); + } + ah[actionName] = [handler, scope]; + }, + /** + * Sends a message to the comObj to invoke the action with the supplied data. + * @param {String} actionName Action to call. + * @param {JSON} data JSON data to send. + * @param {Array} [transfers] Optional list of transfers/ArrayBuffers + */ + send: function messageHandlerSend(actionName, data, transfers) { + var message = { + sourceName: this.sourceName, + targetName: this.targetName, + action: actionName, + data: data + }; + this.postMessage(message, transfers); + }, + /** + * Sends a message to the comObj to invoke the action with the supplied data. + * Expects that other side will callback with the response. + * @param {String} actionName Action to call. + * @param {JSON} data JSON data to send. + * @param {Array} [transfers] Optional list of transfers/ArrayBuffers. + * @returns {Promise} Promise to be resolved with response data. + */ + sendWithPromise: function messageHandlerSendWithPromise( + actionName, + data, + transfers + ) { + var callbackId = this.callbackIndex++; + var message = { + sourceName: this.sourceName, + targetName: this.targetName, + action: actionName, + data: data, + callbackId: callbackId + }; + var capability = createPromiseCapability(); + this.callbacksCapabilities[callbackId] = capability; + try { + this.postMessage(message, transfers); + } catch (e) { + capability.reject(e); + } + return capability.promise; + }, + /** + * Sends raw message to the comObj. + * @private + * @param message {Object} Raw message. + * @param transfers List of transfers/ArrayBuffers, or undefined. + */ + postMessage: function(message, transfers) { + if (transfers && this.postMessageTransfers) { + this.comObj.postMessage(message, transfers); + } else { + this.comObj.postMessage(message); + } + }, + + destroy: function() { + this.comObj.removeEventListener("message", this._onComObjOnMessage); + } + }; + + function loadJpegStream(id, imageUrl, objs) { + var img = new Image(); + img.onload = function loadJpegStream_onloadClosure() { + objs.resolve(id, img); + }; + img.onerror = function loadJpegStream_onerrorClosure() { + objs.resolve(id, null); + warn("Error during JPEG image loading"); + }; + img.src = imageUrl; + } + + exports.FONT_IDENTITY_MATRIX = FONT_IDENTITY_MATRIX; + exports.IDENTITY_MATRIX = IDENTITY_MATRIX; + exports.OPS = OPS; + exports.UNSUPPORTED_FEATURES = UNSUPPORTED_FEATURES; + exports.AnnotationBorderStyleType = AnnotationBorderStyleType; + exports.AnnotationFlag = AnnotationFlag; + exports.AnnotationType = AnnotationType; + exports.FontType = FontType; + exports.ImageKind = ImageKind; + exports.InvalidPDFException = InvalidPDFException; + exports.LinkTarget = LinkTarget; + exports.LinkTargetStringMap = LinkTargetStringMap; + exports.MessageHandler = MessageHandler; + exports.MissingDataException = MissingDataException; + exports.MissingPDFException = MissingPDFException; + exports.NotImplementedException = NotImplementedException; + exports.PasswordException = PasswordException; + exports.PasswordResponses = PasswordResponses; + exports.StatTimer = StatTimer; + exports.StreamType = StreamType; + exports.TextRenderingMode = TextRenderingMode; + exports.UnexpectedResponseException = UnexpectedResponseException; + exports.UnknownErrorException = UnknownErrorException; + exports.Util = Util; + exports.XRefParseException = XRefParseException; + exports.assert = assert; + exports.bytesToString = bytesToString; + exports.combineUrl = combineUrl; + exports.createPromiseCapability = createPromiseCapability; + exports.deprecated = deprecated; + exports.error = error; + exports.info = info; + exports.isArray = isArray; + exports.isArrayBuffer = isArrayBuffer; + exports.isBool = isBool; + exports.isEmptyObj = isEmptyObj; + exports.isExternalLinkTargetSet = isExternalLinkTargetSet; + exports.isInt = isInt; + exports.isNum = isNum; + exports.isString = isString; + exports.isValidUrl = isValidUrl; + exports.loadJpegStream = loadJpegStream; + exports.log2 = log2; + exports.readInt8 = readInt8; + exports.readUint16 = readUint16; + exports.readUint32 = readUint32; + exports.shadow = shadow; + exports.string32 = string32; + exports.stringToBytes = stringToBytes; + exports.stringToPDFString = stringToPDFString; + exports.stringToUTF8String = stringToUTF8String; + exports.utf8StringToString = utf8StringToString; + exports.warn = warn; +}); + +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* globals NetworkManager */ + +("use strict"); + +(function(root, factory) { + //if (typeof define === 'function' && define.amd) { + // define('pdfjs/core/chunked_stream', ['exports', 'pdfjs/shared/util'], + // factory); + // } else if (typeof exports !== 'undefined') { + // factory(exports, require('../shared/util.js')); + //} else { + factory((root.pdfjsCoreChunkedStream = {}), root.pdfjsSharedUtil); + //} +})(window, function(exports, sharedUtil) { + var MissingDataException = sharedUtil.MissingDataException; + var assert = sharedUtil.assert; + var createPromiseCapability = sharedUtil.createPromiseCapability; + var isInt = sharedUtil.isInt; + var isEmptyObj = sharedUtil.isEmptyObj; + + var ChunkedStream = (function ChunkedStreamClosure() { + function ChunkedStream(length, chunkSize, manager) { + this.bytes = new Uint8Array(length); + this.start = 0; + this.pos = 0; + this.end = length; + this.chunkSize = chunkSize; + this.loadedChunks = []; + this.numChunksLoaded = 0; + this.numChunks = Math.ceil(length / chunkSize); + this.manager = manager; + this.progressiveDataLength = 0; + this.lastSuccessfulEnsureByteChunk = -1; // a single-entry cache + } + + // required methods for a stream. if a particular stream does not + // implement these, an error should be thrown + ChunkedStream.prototype = { + getMissingChunks: function ChunkedStream_getMissingChunks() { + var chunks = []; + for (var chunk = 0, n = this.numChunks; chunk < n; ++chunk) { + if (!this.loadedChunks[chunk]) { + chunks.push(chunk); + } + } + return chunks; + }, + + getBaseStreams: function ChunkedStream_getBaseStreams() { + return [this]; + }, + + allChunksLoaded: function ChunkedStream_allChunksLoaded() { + return this.numChunksLoaded === this.numChunks; + }, + + onReceiveData: function ChunkedStream_onReceiveData(begin, chunk) { + var end = begin + chunk.byteLength; + + assert(begin % this.chunkSize === 0, "Bad begin offset: " + begin); + // Using this.length is inaccurate here since this.start can be moved + // See ChunkedStream.moveStart() + var length = this.bytes.length; + assert( + end % this.chunkSize === 0 || end === length, + "Bad end offset: " + end + ); + + this.bytes.set(new Uint8Array(chunk), begin); + var chunkSize = this.chunkSize; + var beginChunk = Math.floor(begin / chunkSize); + var endChunk = Math.floor((end - 1) / chunkSize) + 1; + var curChunk; + + for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) { + if (!this.loadedChunks[curChunk]) { + this.loadedChunks[curChunk] = true; + ++this.numChunksLoaded; + } + } + }, + + onReceiveProgressiveData: function ChunkedStream_onReceiveProgressiveData( + data + ) { + var position = this.progressiveDataLength; + var beginChunk = Math.floor(position / this.chunkSize); + + this.bytes.set(new Uint8Array(data), position); + position += data.byteLength; + this.progressiveDataLength = position; + var endChunk = + position >= this.end + ? this.numChunks + : Math.floor(position / this.chunkSize); + var curChunk; + for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) { + if (!this.loadedChunks[curChunk]) { + this.loadedChunks[curChunk] = true; + ++this.numChunksLoaded; + } + } + }, + + ensureByte: function ChunkedStream_ensureByte(pos) { + var chunk = Math.floor(pos / this.chunkSize); + if (chunk === this.lastSuccessfulEnsureByteChunk) { + return; + } + + if (!this.loadedChunks[chunk]) { + throw new MissingDataException(pos, pos + 1); + } + this.lastSuccessfulEnsureByteChunk = chunk; + }, + + ensureRange: function ChunkedStream_ensureRange(begin, end) { + if (begin >= end) { + return; + } + + if (end <= this.progressiveDataLength) { + return; + } + + var chunkSize = this.chunkSize; + var beginChunk = Math.floor(begin / chunkSize); + var endChunk = Math.floor((end - 1) / chunkSize) + 1; + for (var chunk = beginChunk; chunk < endChunk; ++chunk) { + if (!this.loadedChunks[chunk]) { + throw new MissingDataException(begin, end); + } + } + }, + + nextEmptyChunk: function ChunkedStream_nextEmptyChunk(beginChunk) { + var chunk, + numChunks = this.numChunks; + for (var i = 0; i < numChunks; ++i) { + chunk = (beginChunk + i) % numChunks; // Wrap around to beginning + if (!this.loadedChunks[chunk]) { + return chunk; + } + } + return null; + }, + + hasChunk: function ChunkedStream_hasChunk(chunk) { + return !!this.loadedChunks[chunk]; + }, + + get length() { + return this.end - this.start; + }, + + get isEmpty() { + return this.length === 0; + }, + + getByte: function ChunkedStream_getByte() { + var pos = this.pos; + if (pos >= this.end) { + return -1; + } + this.ensureByte(pos); + return this.bytes[this.pos++]; + }, + + getUint16: function ChunkedStream_getUint16() { + var b0 = this.getByte(); + var b1 = this.getByte(); + if (b0 === -1 || b1 === -1) { + return -1; + } + return (b0 << 8) + b1; + }, + + getInt32: function ChunkedStream_getInt32() { + var b0 = this.getByte(); + var b1 = this.getByte(); + var b2 = this.getByte(); + var b3 = this.getByte(); + return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3; + }, + + // returns subarray of original buffer + // should only be read + getBytes: function ChunkedStream_getBytes(length) { + var bytes = this.bytes; + var pos = this.pos; + var strEnd = this.end; + + if (!length) { + this.ensureRange(pos, strEnd); + return bytes.subarray(pos, strEnd); + } + + var end = pos + length; + if (end > strEnd) { + end = strEnd; + } + this.ensureRange(pos, end); + + this.pos = end; + return bytes.subarray(pos, end); + }, + + peekByte: function ChunkedStream_peekByte() { + var peekedByte = this.getByte(); + this.pos--; + return peekedByte; + }, + + peekBytes: function ChunkedStream_peekBytes(length) { + var bytes = this.getBytes(length); + this.pos -= bytes.length; + return bytes; + }, + + getByteRange: function ChunkedStream_getBytes(begin, end) { + this.ensureRange(begin, end); + return this.bytes.subarray(begin, end); + }, + + skip: function ChunkedStream_skip(n) { + if (!n) { + n = 1; + } + this.pos += n; + }, + + reset: function ChunkedStream_reset() { + this.pos = this.start; + }, + + moveStart: function ChunkedStream_moveStart() { + this.start = this.pos; + }, + + makeSubStream: function ChunkedStream_makeSubStream(start, length, dict) { + this.ensureRange(start, start + length); + + function ChunkedStreamSubstream() {} + ChunkedStreamSubstream.prototype = Object.create(this); + ChunkedStreamSubstream.prototype.getMissingChunks = function() { + var chunkSize = this.chunkSize; + var beginChunk = Math.floor(this.start / chunkSize); + var endChunk = Math.floor((this.end - 1) / chunkSize) + 1; + var missingChunks = []; + for (var chunk = beginChunk; chunk < endChunk; ++chunk) { + if (!this.loadedChunks[chunk]) { + missingChunks.push(chunk); + } + } + return missingChunks; + }; + var subStream = new ChunkedStreamSubstream(); + subStream.pos = subStream.start = start; + subStream.end = start + length || this.end; + subStream.dict = dict; + return subStream; + }, + + isStream: true + }; + + return ChunkedStream; + })(); + + var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { + function ChunkedStreamManager(length, chunkSize, url, args) { + this.stream = new ChunkedStream(length, chunkSize, this); + this.length = length; + this.chunkSize = chunkSize; + this.url = url; + this.disableAutoFetch = args.disableAutoFetch; + var msgHandler = (this.msgHandler = args.msgHandler); + + if (args.chunkedViewerLoading) { + msgHandler.on("OnDataRange", this.onReceiveData.bind(this)); + msgHandler.on("OnDataProgress", this.onProgress.bind(this)); + this.sendRequest = function ChunkedStreamManager_sendRequest( + begin, + end + ) { + msgHandler.send("RequestDataRange", { begin: begin, end: end }); + }; + } else { + var getXhr = function getXhr() { + return new XMLHttpRequest(); + }; + this.networkManager = new NetworkManager(this.url, { + getXhr: getXhr, + httpHeaders: args.httpHeaders, + withCredentials: args.withCredentials + }); + this.sendRequest = function ChunkedStreamManager_sendRequest( + begin, + end + ) { + this.networkManager.requestRange(begin, end, { + onDone: this.onReceiveData.bind(this), + onProgress: this.onProgress.bind(this) + }); + }; + } + + this.currRequestId = 0; + + this.chunksNeededByRequest = {}; + this.requestsByChunk = {}; + this.promisesByRequest = {}; + this.progressiveDataLength = 0; + + this._loadedStreamCapability = createPromiseCapability(); + + if (args.initialData) { + this.onReceiveData({ chunk: args.initialData }); + } + } + + ChunkedStreamManager.prototype = { + onLoadedStream: function ChunkedStreamManager_getLoadedStream() { + return this._loadedStreamCapability.promise; + }, + + // Get all the chunks that are not yet loaded and groups them into + // contiguous ranges to load in as few requests as possible + requestAllChunks: function ChunkedStreamManager_requestAllChunks() { + var missingChunks = this.stream.getMissingChunks(); + this._requestChunks(missingChunks); + return this._loadedStreamCapability.promise; + }, + + _requestChunks: function ChunkedStreamManager_requestChunks(chunks) { + var requestId = this.currRequestId++; + + var chunksNeeded; + var i, ii; + this.chunksNeededByRequest[requestId] = chunksNeeded = {}; + for (i = 0, ii = chunks.length; i < ii; i++) { + if (!this.stream.hasChunk(chunks[i])) { + chunksNeeded[chunks[i]] = true; + } + } + + if (isEmptyObj(chunksNeeded)) { + return Promise.resolve(); + } + + var capability = createPromiseCapability(); + this.promisesByRequest[requestId] = capability; + + var chunksToRequest = []; + for (var chunk in chunksNeeded) { + chunk = chunk | 0; + if (!(chunk in this.requestsByChunk)) { + this.requestsByChunk[chunk] = []; + chunksToRequest.push(chunk); + } + this.requestsByChunk[chunk].push(requestId); + } + + if (!chunksToRequest.length) { + return capability.promise; + } + + var groupedChunksToRequest = this.groupChunks(chunksToRequest); + + for (i = 0; i < groupedChunksToRequest.length; ++i) { + var groupedChunk = groupedChunksToRequest[i]; + var begin = groupedChunk.beginChunk * this.chunkSize; + var end = Math.min( + groupedChunk.endChunk * this.chunkSize, + this.length + ); + this.sendRequest(begin, end); + } + + return capability.promise; + }, + + getStream: function ChunkedStreamManager_getStream() { + return this.stream; + }, + + // Loads any chunks in the requested range that are not yet loaded + requestRange: function ChunkedStreamManager_requestRange(begin, end) { + end = Math.min(end, this.length); + + var beginChunk = this.getBeginChunk(begin); + var endChunk = this.getEndChunk(end); + + var chunks = []; + for (var chunk = beginChunk; chunk < endChunk; ++chunk) { + chunks.push(chunk); + } + + return this._requestChunks(chunks); + }, + + requestRanges: function ChunkedStreamManager_requestRanges(ranges) { + ranges = ranges || []; + var chunksToRequest = []; + + for (var i = 0; i < ranges.length; i++) { + var beginChunk = this.getBeginChunk(ranges[i].begin); + var endChunk = this.getEndChunk(ranges[i].end); + for (var chunk = beginChunk; chunk < endChunk; ++chunk) { + if (chunksToRequest.indexOf(chunk) < 0) { + chunksToRequest.push(chunk); + } + } + } + + chunksToRequest.sort(function(a, b) { + return a - b; + }); + return this._requestChunks(chunksToRequest); + }, + + // Groups a sorted array of chunks into as few contiguous larger + // chunks as possible + groupChunks: function ChunkedStreamManager_groupChunks(chunks) { + var groupedChunks = []; + var beginChunk = -1; + var prevChunk = -1; + for (var i = 0; i < chunks.length; ++i) { + var chunk = chunks[i]; + + if (beginChunk < 0) { + beginChunk = chunk; + } + + if (prevChunk >= 0 && prevChunk + 1 !== chunk) { + groupedChunks.push({ + beginChunk: beginChunk, + endChunk: prevChunk + 1 + }); + beginChunk = chunk; + } + if (i + 1 === chunks.length) { + groupedChunks.push({ beginChunk: beginChunk, endChunk: chunk + 1 }); + } + + prevChunk = chunk; + } + return groupedChunks; + }, + + onProgress: function ChunkedStreamManager_onProgress(args) { + var bytesLoaded = + this.stream.numChunksLoaded * this.chunkSize + args.loaded; + this.msgHandler.send("DocProgress", { + loaded: bytesLoaded, + total: this.length + }); + }, + + onReceiveData: function ChunkedStreamManager_onReceiveData(args) { + var chunk = args.chunk; + var isProgressive = args.begin === undefined; + var begin = isProgressive ? this.progressiveDataLength : args.begin; + var end = begin + chunk.byteLength; + + var beginChunk = Math.floor(begin / this.chunkSize); + var endChunk = + end < this.length + ? Math.floor(end / this.chunkSize) + : Math.ceil(end / this.chunkSize); + + if (isProgressive) { + this.stream.onReceiveProgressiveData(chunk); + this.progressiveDataLength = end; + } else { + this.stream.onReceiveData(begin, chunk); + } + + if (this.stream.allChunksLoaded()) { + this._loadedStreamCapability.resolve(this.stream); + } + + var loadedRequests = []; + var i, requestId; + for (chunk = beginChunk; chunk < endChunk; ++chunk) { + // The server might return more chunks than requested + var requestIds = this.requestsByChunk[chunk] || []; + delete this.requestsByChunk[chunk]; + + for (i = 0; i < requestIds.length; ++i) { + requestId = requestIds[i]; + var chunksNeeded = this.chunksNeededByRequest[requestId]; + if (chunk in chunksNeeded) { + delete chunksNeeded[chunk]; + } + + if (!isEmptyObj(chunksNeeded)) { + continue; + } + + loadedRequests.push(requestId); + } + } + + // If there are no pending requests, automatically fetch the next + // unfetched chunk of the PDF + if (!this.disableAutoFetch && isEmptyObj(this.requestsByChunk)) { + var nextEmptyChunk; + if (this.stream.numChunksLoaded === 1) { + // This is a special optimization so that after fetching the first + // chunk, rather than fetching the second chunk, we fetch the last + // chunk. + var lastChunk = this.stream.numChunks - 1; + if (!this.stream.hasChunk(lastChunk)) { + nextEmptyChunk = lastChunk; + } + } else { + nextEmptyChunk = this.stream.nextEmptyChunk(endChunk); + } + if (isInt(nextEmptyChunk)) { + this._requestChunks([nextEmptyChunk]); + } + } + + for (i = 0; i < loadedRequests.length; ++i) { + requestId = loadedRequests[i]; + var capability = this.promisesByRequest[requestId]; + delete this.promisesByRequest[requestId]; + capability.resolve(); + } + + this.msgHandler.send("DocProgress", { + loaded: this.stream.numChunksLoaded * this.chunkSize, + total: this.length + }); + }, + + onError: function ChunkedStreamManager_onError(err) { + this._loadedStreamCapability.reject(err); + }, + + getBeginChunk: function ChunkedStreamManager_getBeginChunk(begin) { + var chunk = Math.floor(begin / this.chunkSize); + return chunk; + }, + + getEndChunk: function ChunkedStreamManager_getEndChunk(end) { + var chunk = Math.floor((end - 1) / this.chunkSize) + 1; + return chunk; + }, + + abort: function ChunkedStreamManager_abort() { + if (this.networkManager) { + this.networkManager.abortAllRequests(); + } + for (var requestId in this.promisesByRequest) { + var capability = this.promisesByRequest[requestId]; + capability.reject(new Error("Request was aborted")); + } + } + }; + + return ChunkedStreamManager; + })(); + + exports.ChunkedStream = ChunkedStream; + exports.ChunkedStreamManager = ChunkedStreamManager; +}); + +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* uses XRef */ + +("use strict"); + +(function(root, factory) { + //if (typeof define === 'function' && define.amd) { + // define('pdfjs/core/primitives', ['exports', 'pdfjs/shared/util'], factory); + // } else if (typeof exports !== 'undefined') { + // factory(exports, require('../shared/util.js')); + //} else { + factory((root.pdfjsCorePrimitives = {}), root.pdfjsSharedUtil); + //} +})(window, function(exports, sharedUtil) { + var isArray = sharedUtil.isArray; + + var Name = (function NameClosure() { + function Name(name) { + this.name = name; + } + + Name.prototype = {}; + + var nameCache = {}; + + Name.get = function Name_get(name) { + var nameValue = nameCache[name]; + return nameValue ? nameValue : (nameCache[name] = new Name(name)); + }; + + return Name; + })(); + + var Cmd = (function CmdClosure() { + function Cmd(cmd) { + this.cmd = cmd; + } + + Cmd.prototype = {}; + + var cmdCache = {}; + + Cmd.get = function Cmd_get(cmd) { + var cmdValue = cmdCache[cmd]; + return cmdValue ? cmdValue : (cmdCache[cmd] = new Cmd(cmd)); + }; + + return Cmd; + })(); + + var Dict = (function DictClosure() { + var nonSerializable = function nonSerializableClosure() { + return nonSerializable; // creating closure on some variable + }; + + var GETALL_DICTIONARY_TYPES_WHITELIST = { + Background: true, + ExtGState: true, + Halftone: true, + Layout: true, + Mask: true, + Pagination: true, + Printing: true + }; + + function isRecursionAllowedFor(dict) { + if (!isName(dict.Type)) { + return true; + } + var dictType = dict.Type.name; + return GETALL_DICTIONARY_TYPES_WHITELIST[dictType] === true; + } + + // xref is optional + function Dict(xref) { + // Map should only be used internally, use functions below to access. + this.map = Object.create(null); + this.xref = xref; + this.objId = null; + this.__nonSerializable__ = nonSerializable; // disable cloning of the Dict + } + + Dict.prototype = { + assignXref: function Dict_assignXref(newXref) { + this.xref = newXref; + }, + + // automatically dereferences Ref objects + get: function Dict_get(key1, key2, key3) { + var value; + var xref = this.xref; + if ( + typeof (value = this.map[key1]) !== "undefined" || + key1 in this.map || + typeof key2 === "undefined" + ) { + return xref ? xref.fetchIfRef(value) : value; + } + if ( + typeof (value = this.map[key2]) !== "undefined" || + key2 in this.map || + typeof key3 === "undefined" + ) { + return xref ? xref.fetchIfRef(value) : value; + } + value = this.map[key3] || null; + return xref ? xref.fetchIfRef(value) : value; + }, + + // Same as get(), but returns a promise and uses fetchIfRefAsync(). + getAsync: function Dict_getAsync(key1, key2, key3) { + var value; + var xref = this.xref; + if ( + typeof (value = this.map[key1]) !== "undefined" || + key1 in this.map || + typeof key2 === "undefined" + ) { + if (xref) { + return xref.fetchIfRefAsync(value); + } + return Promise.resolve(value); + } + if ( + typeof (value = this.map[key2]) !== "undefined" || + key2 in this.map || + typeof key3 === "undefined" + ) { + if (xref) { + return xref.fetchIfRefAsync(value); + } + return Promise.resolve(value); + } + value = this.map[key3] || null; + if (xref) { + return xref.fetchIfRefAsync(value); + } + return Promise.resolve(value); + }, + + // Same as get(), but dereferences all elements if the result is an Array. + getArray: function Dict_getArray(key1, key2, key3) { + var value = this.get(key1, key2, key3); + var xref = this.xref; + if (!isArray(value) || !xref) { + return value; + } + value = value.slice(); // Ensure that we don't modify the Dict data. + for (var i = 0, ii = value.length; i < ii; i++) { + if (!isRef(value[i])) { + continue; + } + value[i] = xref.fetch(value[i]); + } + return value; + }, + + // no dereferencing + getRaw: function Dict_getRaw(key) { + return this.map[key]; + }, + + // creates new map and dereferences all Refs + getAll: function Dict_getAll() { + var all = Object.create(null); + var queue = null; + var key, obj; + for (key in this.map) { + obj = this.get(key); + if (obj instanceof Dict) { + if (isRecursionAllowedFor(obj)) { + (queue || (queue = [])).push({ target: all, key: key, obj: obj }); + } else { + all[key] = this.getRaw(key); + } + } else { + all[key] = obj; + } + } + if (!queue) { + return all; + } + + // trying to take cyclic references into the account + var processed = Object.create(null); + while (queue.length > 0) { + var item = queue.shift(); + var itemObj = item.obj; + var objId = itemObj.objId; + if (objId && objId in processed) { + item.target[item.key] = processed[objId]; + continue; + } + var dereferenced = Object.create(null); + for (key in itemObj.map) { + obj = itemObj.get(key); + if (obj instanceof Dict) { + if (isRecursionAllowedFor(obj)) { + queue.push({ target: dereferenced, key: key, obj: obj }); + } else { + dereferenced[key] = itemObj.getRaw(key); + } + } else { + dereferenced[key] = obj; + } + } + if (objId) { + processed[objId] = dereferenced; + } + item.target[item.key] = dereferenced; + } + return all; + }, + + getKeys: function Dict_getKeys() { + return Object.keys(this.map); + }, + + set: function Dict_set(key, value) { + this.map[key] = value; + }, + + has: function Dict_has(key) { + return key in this.map; + }, + + forEach: function Dict_forEach(callback) { + for (var key in this.map) { + callback(key, this.get(key)); + } + } + }; + + Dict.empty = new Dict(null); + + Dict.merge = function Dict_merge(xref, dictArray) { + var mergedDict = new Dict(xref); + + for (var i = 0, ii = dictArray.length; i < ii; i++) { + var dict = dictArray[i]; + if (!isDict(dict)) { + continue; + } + for (var keyName in dict.map) { + if (mergedDict.map[keyName]) { + continue; + } + mergedDict.map[keyName] = dict.map[keyName]; + } + } + return mergedDict; + }; + + return Dict; + })(); + + var Ref = (function RefClosure() { + function Ref(num, gen) { + this.num = num; + this.gen = gen; + } + + Ref.prototype = { + toString: function Ref_toString() { + // This function is hot, so we make the string as compact as possible. + // |this.gen| is almost always zero, so we treat that case specially. + var str = this.num + "R"; + if (this.gen !== 0) { + str += this.gen; + } + return str; + } + }; + + return Ref; + })(); + + // The reference is identified by number and generation. + // This structure stores only one instance of the reference. + var RefSet = (function RefSetClosure() { + function RefSet() { + this.dict = {}; + } + + RefSet.prototype = { + has: function RefSet_has(ref) { + return ref.toString() in this.dict; + }, + + put: function RefSet_put(ref) { + this.dict[ref.toString()] = true; + }, + + remove: function RefSet_remove(ref) { + delete this.dict[ref.toString()]; + } + }; + + return RefSet; + })(); + + var RefSetCache = (function RefSetCacheClosure() { + function RefSetCache() { + this.dict = Object.create(null); + } + + RefSetCache.prototype = { + get: function RefSetCache_get(ref) { + return this.dict[ref.toString()]; + }, + + has: function RefSetCache_has(ref) { + return ref.toString() in this.dict; + }, + + put: function RefSetCache_put(ref, obj) { + this.dict[ref.toString()] = obj; + }, + + putAlias: function RefSetCache_putAlias(ref, aliasRef) { + this.dict[ref.toString()] = this.get(aliasRef); + }, + + forEach: function RefSetCache_forEach(fn, thisArg) { + for (var i in this.dict) { + fn.call(thisArg, this.dict[i]); + } + }, + + clear: function RefSetCache_clear() { + this.dict = Object.create(null); + } + }; + + return RefSetCache; + })(); + + function isName(v) { + return v instanceof Name; + } + + function isCmd(v, cmd) { + return v instanceof Cmd && (cmd === undefined || v.cmd === cmd); + } + + function isDict(v, type) { + if (!(v instanceof Dict)) { + return false; + } + if (!type) { + return true; + } + var dictType = v.get("Type"); + return isName(dictType) && dictType.name === type; + } + + function isRef(v) { + return v instanceof Ref; + } + + function isStream(v) { + return typeof v === "object" && v !== null && v.getBytes !== undefined; + } + + exports.Cmd = Cmd; + exports.Dict = Dict; + exports.Name = Name; + exports.Ref = Ref; + exports.RefSet = RefSet; + exports.RefSetCache = RefSetCache; + exports.isCmd = isCmd; + exports.isDict = isDict; + exports.isName = isName; + exports.isRef = isRef; + exports.isStream = isStream; +}); + +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* globals PDFJS */ + +("use strict"); + +(function(root, factory) { + //if (typeof define === 'function' && define.amd) { + // define('pdfjs/core/stream', ['exports', 'pdfjs/shared/util', + // 'pdfjs/core/primitives'], factory); + // } else if (typeof exports !== 'undefined') { + // factory(exports, require('../shared/util.js'), require('./primitives.js')); + //} else { + factory( + (root.pdfjsCoreStream = {}), + root.pdfjsSharedUtil, + root.pdfjsCorePrimitives + ); + //} +})(window, function(exports, sharedUtil, corePrimitives) { + var Util = sharedUtil.Util; + var error = sharedUtil.error; + var info = sharedUtil.info; + var isArray = sharedUtil.isArray; + var shadow = sharedUtil.shadow; + var warn = sharedUtil.warn; + var Dict = corePrimitives.Dict; + + var coreParser; // see _setCoreParser below + var EOF; // = coreParser.EOF; + var Lexer; // = coreParser.Lexer; + + var coreColorSpace; // see _setCoreColorSpace below + var ColorSpace; // = coreColorSpace.ColorSpace; + + var Stream = (function StreamClosure() { + function Stream(arrayBuffer, start, length, dict) { + this.bytes = + arrayBuffer instanceof Uint8Array + ? arrayBuffer + : new Uint8Array(arrayBuffer); + this.start = start || 0; + this.pos = this.start; + this.end = start + length || this.bytes.length; + this.dict = dict; + } + + // required methods for a stream. if a particular stream does not + // implement these, an error should be thrown + Stream.prototype = { + get length() { + return this.end - this.start; + }, + get isEmpty() { + return this.length === 0; + }, + getByte: function Stream_getByte() { + if (this.pos >= this.end) { + return -1; + } + return this.bytes[this.pos++]; + }, + getUint16: function Stream_getUint16() { + var b0 = this.getByte(); + var b1 = this.getByte(); + if (b0 === -1 || b1 === -1) { + return -1; + } + return (b0 << 8) + b1; + }, + getInt32: function Stream_getInt32() { + var b0 = this.getByte(); + var b1 = this.getByte(); + var b2 = this.getByte(); + var b3 = this.getByte(); + return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3; + }, + // returns subarray of original buffer + // should only be read + getBytes: function Stream_getBytes(length) { + var bytes = this.bytes; + var pos = this.pos; + var strEnd = this.end; + + if (!length) { + return bytes.subarray(pos, strEnd); + } + var end = pos + length; + if (end > strEnd) { + end = strEnd; + } + this.pos = end; + return bytes.subarray(pos, end); + }, + peekByte: function Stream_peekByte() { + var peekedByte = this.getByte(); + this.pos--; + return peekedByte; + }, + peekBytes: function Stream_peekBytes(length) { + var bytes = this.getBytes(length); + this.pos -= bytes.length; + return bytes; + }, + skip: function Stream_skip(n) { + if (!n) { + n = 1; + } + this.pos += n; + }, + reset: function Stream_reset() { + this.pos = this.start; + }, + moveStart: function Stream_moveStart() { + this.start = this.pos; + }, + makeSubStream: function Stream_makeSubStream(start, length, dict) { + return new Stream(this.bytes.buffer, start, length, dict); + }, + isStream: true + }; + + return Stream; + })(); + + var StringStream = (function StringStreamClosure() { + function StringStream(str) { + var length = str.length; + var bytes = new Uint8Array(length); + for (var n = 0; n < length; ++n) { + bytes[n] = str.charCodeAt(n); + } + Stream.call(this, bytes); + } + + StringStream.prototype = Stream.prototype; + + return StringStream; + })(); + + // super class for the decoding streams + var DecodeStream = (function DecodeStreamClosure() { + // Lots of DecodeStreams are created whose buffers are never used. For these + // we share a single empty buffer. This is (a) space-efficient and (b) avoids + // having special cases that would be required if we used |null| for an empty + // buffer. + var emptyBuffer = new Uint8Array(0); + + function DecodeStream(maybeMinBufferLength) { + this.pos = 0; + this.bufferLength = 0; + this.eof = false; + this.buffer = emptyBuffer; + this.minBufferLength = 512; + if (maybeMinBufferLength) { + // Compute the first power of two that is as big as maybeMinBufferLength. + while (this.minBufferLength < maybeMinBufferLength) { + this.minBufferLength *= 2; + } + } + } + + DecodeStream.prototype = { + get isEmpty() { + while (!this.eof && this.bufferLength === 0) { + this.readBlock(); + } + return this.bufferLength === 0; + }, + ensureBuffer: function DecodeStream_ensureBuffer(requested) { + var buffer = this.buffer; + if (requested <= buffer.byteLength) { + return buffer; + } + var size = this.minBufferLength; + while (size < requested) { + size *= 2; + } + var buffer2 = new Uint8Array(size); + buffer2.set(buffer); + return (this.buffer = buffer2); + }, + getByte: function DecodeStream_getByte() { + var pos = this.pos; + while (this.bufferLength <= pos) { + if (this.eof) { + return -1; + } + this.readBlock(); + } + return this.buffer[this.pos++]; + }, + getUint16: function DecodeStream_getUint16() { + var b0 = this.getByte(); + var b1 = this.getByte(); + if (b0 === -1 || b1 === -1) { + return -1; + } + return (b0 << 8) + b1; + }, + getInt32: function DecodeStream_getInt32() { + var b0 = this.getByte(); + var b1 = this.getByte(); + var b2 = this.getByte(); + var b3 = this.getByte(); + return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3; + }, + getBytes: function DecodeStream_getBytes(length) { + var end, + pos = this.pos; + + if (length) { + this.ensureBuffer(pos + length); + end = pos + length; + + while (!this.eof && this.bufferLength < end) { + this.readBlock(); + } + var bufEnd = this.bufferLength; + if (end > bufEnd) { + end = bufEnd; + } + } else { + while (!this.eof) { + this.readBlock(); + } + end = this.bufferLength; + } + + this.pos = end; + return this.buffer.subarray(pos, end); + }, + peekByte: function DecodeStream_peekByte() { + var peekedByte = this.getByte(); + this.pos--; + return peekedByte; + }, + peekBytes: function DecodeStream_peekBytes(length) { + var bytes = this.getBytes(length); + this.pos -= bytes.length; + return bytes; + }, + makeSubStream: function DecodeStream_makeSubStream(start, length, dict) { + var end = start + length; + while (this.bufferLength <= end && !this.eof) { + this.readBlock(); + } + return new Stream(this.buffer, start, length, dict); + }, + skip: function DecodeStream_skip(n) { + if (!n) { + n = 1; + } + this.pos += n; + }, + reset: function DecodeStream_reset() { + this.pos = 0; + }, + getBaseStreams: function DecodeStream_getBaseStreams() { + if (this.str && this.str.getBaseStreams) { + return this.str.getBaseStreams(); + } + return []; + } + }; + + return DecodeStream; + })(); + + var StreamsSequenceStream = (function StreamsSequenceStreamClosure() { + function StreamsSequenceStream(streams) { + this.streams = streams; + DecodeStream.call(this, /* maybeLength = */ null); + } + + StreamsSequenceStream.prototype = Object.create(DecodeStream.prototype); + + StreamsSequenceStream.prototype.readBlock = function streamSequenceStreamReadBlock() { + var streams = this.streams; + if (streams.length === 0) { + this.eof = true; + return; + } + var stream = streams.shift(); + var chunk = stream.getBytes(); + var bufferLength = this.bufferLength; + var newLength = bufferLength + chunk.length; + var buffer = this.ensureBuffer(newLength); + buffer.set(chunk, bufferLength); + this.bufferLength = newLength; + }; + + StreamsSequenceStream.prototype.getBaseStreams = function StreamsSequenceStream_getBaseStreams() { + var baseStreams = []; + for (var i = 0, ii = this.streams.length; i < ii; i++) { + var stream = this.streams[i]; + if (stream.getBaseStreams) { + Util.appendToArray(baseStreams, stream.getBaseStreams()); + } + } + return baseStreams; + }; + + return StreamsSequenceStream; + })(); + + var FlateStream = (function FlateStreamClosure() { + var codeLenCodeMap = new Int32Array([ + 16, + 17, + 18, + 0, + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15 + ]); + + var lengthDecode = new Int32Array([ + 0x00003, + 0x00004, + 0x00005, + 0x00006, + 0x00007, + 0x00008, + 0x00009, + 0x0000a, + 0x1000b, + 0x1000d, + 0x1000f, + 0x10011, + 0x20013, + 0x20017, + 0x2001b, + 0x2001f, + 0x30023, + 0x3002b, + 0x30033, + 0x3003b, + 0x40043, + 0x40053, + 0x40063, + 0x40073, + 0x50083, + 0x500a3, + 0x500c3, + 0x500e3, + 0x00102, + 0x00102, + 0x00102 + ]); + + var distDecode = new Int32Array([ + 0x00001, + 0x00002, + 0x00003, + 0x00004, + 0x10005, + 0x10007, + 0x20009, + 0x2000d, + 0x30011, + 0x30019, + 0x40021, + 0x40031, + 0x50041, + 0x50061, + 0x60081, + 0x600c1, + 0x70101, + 0x70181, + 0x80201, + 0x80301, + 0x90401, + 0x90601, + 0xa0801, + 0xa0c01, + 0xb1001, + 0xb1801, + 0xc2001, + 0xc3001, + 0xd4001, + 0xd6001 + ]); + + var fixedLitCodeTab = [ + new Int32Array([ + 0x70100, + 0x80050, + 0x80010, + 0x80118, + 0x70110, + 0x80070, + 0x80030, + 0x900c0, + 0x70108, + 0x80060, + 0x80020, + 0x900a0, + 0x80000, + 0x80080, + 0x80040, + 0x900e0, + 0x70104, + 0x80058, + 0x80018, + 0x90090, + 0x70114, + 0x80078, + 0x80038, + 0x900d0, + 0x7010c, + 0x80068, + 0x80028, + 0x900b0, + 0x80008, + 0x80088, + 0x80048, + 0x900f0, + 0x70102, + 0x80054, + 0x80014, + 0x8011c, + 0x70112, + 0x80074, + 0x80034, + 0x900c8, + 0x7010a, + 0x80064, + 0x80024, + 0x900a8, + 0x80004, + 0x80084, + 0x80044, + 0x900e8, + 0x70106, + 0x8005c, + 0x8001c, + 0x90098, + 0x70116, + 0x8007c, + 0x8003c, + 0x900d8, + 0x7010e, + 0x8006c, + 0x8002c, + 0x900b8, + 0x8000c, + 0x8008c, + 0x8004c, + 0x900f8, + 0x70101, + 0x80052, + 0x80012, + 0x8011a, + 0x70111, + 0x80072, + 0x80032, + 0x900c4, + 0x70109, + 0x80062, + 0x80022, + 0x900a4, + 0x80002, + 0x80082, + 0x80042, + 0x900e4, + 0x70105, + 0x8005a, + 0x8001a, + 0x90094, + 0x70115, + 0x8007a, + 0x8003a, + 0x900d4, + 0x7010d, + 0x8006a, + 0x8002a, + 0x900b4, + 0x8000a, + 0x8008a, + 0x8004a, + 0x900f4, + 0x70103, + 0x80056, + 0x80016, + 0x8011e, + 0x70113, + 0x80076, + 0x80036, + 0x900cc, + 0x7010b, + 0x80066, + 0x80026, + 0x900ac, + 0x80006, + 0x80086, + 0x80046, + 0x900ec, + 0x70107, + 0x8005e, + 0x8001e, + 0x9009c, + 0x70117, + 0x8007e, + 0x8003e, + 0x900dc, + 0x7010f, + 0x8006e, + 0x8002e, + 0x900bc, + 0x8000e, + 0x8008e, + 0x8004e, + 0x900fc, + 0x70100, + 0x80051, + 0x80011, + 0x80119, + 0x70110, + 0x80071, + 0x80031, + 0x900c2, + 0x70108, + 0x80061, + 0x80021, + 0x900a2, + 0x80001, + 0x80081, + 0x80041, + 0x900e2, + 0x70104, + 0x80059, + 0x80019, + 0x90092, + 0x70114, + 0x80079, + 0x80039, + 0x900d2, + 0x7010c, + 0x80069, + 0x80029, + 0x900b2, + 0x80009, + 0x80089, + 0x80049, + 0x900f2, + 0x70102, + 0x80055, + 0x80015, + 0x8011d, + 0x70112, + 0x80075, + 0x80035, + 0x900ca, + 0x7010a, + 0x80065, + 0x80025, + 0x900aa, + 0x80005, + 0x80085, + 0x80045, + 0x900ea, + 0x70106, + 0x8005d, + 0x8001d, + 0x9009a, + 0x70116, + 0x8007d, + 0x8003d, + 0x900da, + 0x7010e, + 0x8006d, + 0x8002d, + 0x900ba, + 0x8000d, + 0x8008d, + 0x8004d, + 0x900fa, + 0x70101, + 0x80053, + 0x80013, + 0x8011b, + 0x70111, + 0x80073, + 0x80033, + 0x900c6, + 0x70109, + 0x80063, + 0x80023, + 0x900a6, + 0x80003, + 0x80083, + 0x80043, + 0x900e6, + 0x70105, + 0x8005b, + 0x8001b, + 0x90096, + 0x70115, + 0x8007b, + 0x8003b, + 0x900d6, + 0x7010d, + 0x8006b, + 0x8002b, + 0x900b6, + 0x8000b, + 0x8008b, + 0x8004b, + 0x900f6, + 0x70103, + 0x80057, + 0x80017, + 0x8011f, + 0x70113, + 0x80077, + 0x80037, + 0x900ce, + 0x7010b, + 0x80067, + 0x80027, + 0x900ae, + 0x80007, + 0x80087, + 0x80047, + 0x900ee, + 0x70107, + 0x8005f, + 0x8001f, + 0x9009e, + 0x70117, + 0x8007f, + 0x8003f, + 0x900de, + 0x7010f, + 0x8006f, + 0x8002f, + 0x900be, + 0x8000f, + 0x8008f, + 0x8004f, + 0x900fe, + 0x70100, + 0x80050, + 0x80010, + 0x80118, + 0x70110, + 0x80070, + 0x80030, + 0x900c1, + 0x70108, + 0x80060, + 0x80020, + 0x900a1, + 0x80000, + 0x80080, + 0x80040, + 0x900e1, + 0x70104, + 0x80058, + 0x80018, + 0x90091, + 0x70114, + 0x80078, + 0x80038, + 0x900d1, + 0x7010c, + 0x80068, + 0x80028, + 0x900b1, + 0x80008, + 0x80088, + 0x80048, + 0x900f1, + 0x70102, + 0x80054, + 0x80014, + 0x8011c, + 0x70112, + 0x80074, + 0x80034, + 0x900c9, + 0x7010a, + 0x80064, + 0x80024, + 0x900a9, + 0x80004, + 0x80084, + 0x80044, + 0x900e9, + 0x70106, + 0x8005c, + 0x8001c, + 0x90099, + 0x70116, + 0x8007c, + 0x8003c, + 0x900d9, + 0x7010e, + 0x8006c, + 0x8002c, + 0x900b9, + 0x8000c, + 0x8008c, + 0x8004c, + 0x900f9, + 0x70101, + 0x80052, + 0x80012, + 0x8011a, + 0x70111, + 0x80072, + 0x80032, + 0x900c5, + 0x70109, + 0x80062, + 0x80022, + 0x900a5, + 0x80002, + 0x80082, + 0x80042, + 0x900e5, + 0x70105, + 0x8005a, + 0x8001a, + 0x90095, + 0x70115, + 0x8007a, + 0x8003a, + 0x900d5, + 0x7010d, + 0x8006a, + 0x8002a, + 0x900b5, + 0x8000a, + 0x8008a, + 0x8004a, + 0x900f5, + 0x70103, + 0x80056, + 0x80016, + 0x8011e, + 0x70113, + 0x80076, + 0x80036, + 0x900cd, + 0x7010b, + 0x80066, + 0x80026, + 0x900ad, + 0x80006, + 0x80086, + 0x80046, + 0x900ed, + 0x70107, + 0x8005e, + 0x8001e, + 0x9009d, + 0x70117, + 0x8007e, + 0x8003e, + 0x900dd, + 0x7010f, + 0x8006e, + 0x8002e, + 0x900bd, + 0x8000e, + 0x8008e, + 0x8004e, + 0x900fd, + 0x70100, + 0x80051, + 0x80011, + 0x80119, + 0x70110, + 0x80071, + 0x80031, + 0x900c3, + 0x70108, + 0x80061, + 0x80021, + 0x900a3, + 0x80001, + 0x80081, + 0x80041, + 0x900e3, + 0x70104, + 0x80059, + 0x80019, + 0x90093, + 0x70114, + 0x80079, + 0x80039, + 0x900d3, + 0x7010c, + 0x80069, + 0x80029, + 0x900b3, + 0x80009, + 0x80089, + 0x80049, + 0x900f3, + 0x70102, + 0x80055, + 0x80015, + 0x8011d, + 0x70112, + 0x80075, + 0x80035, + 0x900cb, + 0x7010a, + 0x80065, + 0x80025, + 0x900ab, + 0x80005, + 0x80085, + 0x80045, + 0x900eb, + 0x70106, + 0x8005d, + 0x8001d, + 0x9009b, + 0x70116, + 0x8007d, + 0x8003d, + 0x900db, + 0x7010e, + 0x8006d, + 0x8002d, + 0x900bb, + 0x8000d, + 0x8008d, + 0x8004d, + 0x900fb, + 0x70101, + 0x80053, + 0x80013, + 0x8011b, + 0x70111, + 0x80073, + 0x80033, + 0x900c7, + 0x70109, + 0x80063, + 0x80023, + 0x900a7, + 0x80003, + 0x80083, + 0x80043, + 0x900e7, + 0x70105, + 0x8005b, + 0x8001b, + 0x90097, + 0x70115, + 0x8007b, + 0x8003b, + 0x900d7, + 0x7010d, + 0x8006b, + 0x8002b, + 0x900b7, + 0x8000b, + 0x8008b, + 0x8004b, + 0x900f7, + 0x70103, + 0x80057, + 0x80017, + 0x8011f, + 0x70113, + 0x80077, + 0x80037, + 0x900cf, + 0x7010b, + 0x80067, + 0x80027, + 0x900af, + 0x80007, + 0x80087, + 0x80047, + 0x900ef, + 0x70107, + 0x8005f, + 0x8001f, + 0x9009f, + 0x70117, + 0x8007f, + 0x8003f, + 0x900df, + 0x7010f, + 0x8006f, + 0x8002f, + 0x900bf, + 0x8000f, + 0x8008f, + 0x8004f, + 0x900ff + ]), + 9 + ]; + + var fixedDistCodeTab = [ + new Int32Array([ + 0x50000, + 0x50010, + 0x50008, + 0x50018, + 0x50004, + 0x50014, + 0x5000c, + 0x5001c, + 0x50002, + 0x50012, + 0x5000a, + 0x5001a, + 0x50006, + 0x50016, + 0x5000e, + 0x00000, + 0x50001, + 0x50011, + 0x50009, + 0x50019, + 0x50005, + 0x50015, + 0x5000d, + 0x5001d, + 0x50003, + 0x50013, + 0x5000b, + 0x5001b, + 0x50007, + 0x50017, + 0x5000f, + 0x00000 + ]), + 5 + ]; + + function FlateStream(str, maybeLength) { + this.str = str; + this.dict = str.dict; + + var cmf = str.getByte(); + var flg = str.getByte(); + if (cmf === -1 || flg === -1) { + error("Invalid header in flate stream: " + cmf + ", " + flg); + } + if ((cmf & 0x0f) !== 0x08) { + error( + "Unknown compression method in flate stream: " + cmf + ", " + flg + ); + } + if (((cmf << 8) + flg) % 31 !== 0) { + error("Bad FCHECK in flate stream: " + cmf + ", " + flg); + } + if (flg & 0x20) { + error("FDICT bit set in flate stream: " + cmf + ", " + flg); + } + + this.codeSize = 0; + this.codeBuf = 0; + + DecodeStream.call(this, maybeLength); + } + + FlateStream.prototype = Object.create(DecodeStream.prototype); + + FlateStream.prototype.getBits = function FlateStream_getBits(bits) { + var str = this.str; + var codeSize = this.codeSize; + var codeBuf = this.codeBuf; + + var b; + while (codeSize < bits) { + if ((b = str.getByte()) === -1) { + error("Bad encoding in flate stream"); + } + codeBuf |= b << codeSize; + codeSize += 8; + } + b = codeBuf & ((1 << bits) - 1); + this.codeBuf = codeBuf >> bits; + this.codeSize = codeSize -= bits; + + return b; + }; + + FlateStream.prototype.getCode = function FlateStream_getCode(table) { + var str = this.str; + var codes = table[0]; + var maxLen = table[1]; + var codeSize = this.codeSize; + var codeBuf = this.codeBuf; + + var b; + while (codeSize < maxLen) { + if ((b = str.getByte()) === -1) { + // premature end of stream. code might however still be valid. + // codeSize < codeLen check below guards against incomplete codeVal. + break; + } + codeBuf |= b << codeSize; + codeSize += 8; + } + var code = codes[codeBuf & ((1 << maxLen) - 1)]; + var codeLen = code >> 16; + var codeVal = code & 0xffff; + if (codeLen < 1 || codeSize < codeLen) { + error("Bad encoding in flate stream"); + } + this.codeBuf = codeBuf >> codeLen; + this.codeSize = codeSize - codeLen; + return codeVal; + }; + + FlateStream.prototype.generateHuffmanTable = function flateStreamGenerateHuffmanTable( + lengths + ) { + var n = lengths.length; + + // find max code length + var maxLen = 0; + var i; + for (i = 0; i < n; ++i) { + if (lengths[i] > maxLen) { + maxLen = lengths[i]; + } + } + + // build the table + var size = 1 << maxLen; + var codes = new Int32Array(size); + for ( + var len = 1, code = 0, skip = 2; + len <= maxLen; + ++len, code <<= 1, skip <<= 1 + ) { + for (var val = 0; val < n; ++val) { + if (lengths[val] === len) { + // bit-reverse the code + var code2 = 0; + var t = code; + for (i = 0; i < len; ++i) { + code2 = (code2 << 1) | (t & 1); + t >>= 1; + } + + // fill the table entries + for (i = code2; i < size; i += skip) { + codes[i] = (len << 16) | val; + } + ++code; + } + } + } + + return [codes, maxLen]; + }; + + FlateStream.prototype.readBlock = function FlateStream_readBlock() { + var buffer, len; + var str = this.str; + // read block header + var hdr = this.getBits(3); + if (hdr & 1) { + this.eof = true; + } + hdr >>= 1; + + if (hdr === 0) { + // uncompressed block + var b; + + if ((b = str.getByte()) === -1) { + error("Bad block header in flate stream"); + } + var blockLen = b; + if ((b = str.getByte()) === -1) { + error("Bad block header in flate stream"); + } + blockLen |= b << 8; + if ((b = str.getByte()) === -1) { + error("Bad block header in flate stream"); + } + var check = b; + if ((b = str.getByte()) === -1) { + error("Bad block header in flate stream"); + } + check |= b << 8; + if (check !== (~blockLen & 0xffff) && (blockLen !== 0 || check !== 0)) { + // Ignoring error for bad "empty" block (see issue 1277) + error("Bad uncompressed block length in flate stream"); + } + + this.codeBuf = 0; + this.codeSize = 0; + + var bufferLength = this.bufferLength; + buffer = this.ensureBuffer(bufferLength + blockLen); + var end = bufferLength + blockLen; + this.bufferLength = end; + if (blockLen === 0) { + if (str.peekByte() === -1) { + this.eof = true; + } + } else { + for (var n = bufferLength; n < end; ++n) { + if ((b = str.getByte()) === -1) { + this.eof = true; + break; + } + buffer[n] = b; + } + } + return; + } + + var litCodeTable; + var distCodeTable; + if (hdr === 1) { + // compressed block, fixed codes + litCodeTable = fixedLitCodeTab; + distCodeTable = fixedDistCodeTab; + } else if (hdr === 2) { + // compressed block, dynamic codes + var numLitCodes = this.getBits(5) + 257; + var numDistCodes = this.getBits(5) + 1; + var numCodeLenCodes = this.getBits(4) + 4; + + // build the code lengths code table + var codeLenCodeLengths = new Uint8Array(codeLenCodeMap.length); + + var i; + for (i = 0; i < numCodeLenCodes; ++i) { + codeLenCodeLengths[codeLenCodeMap[i]] = this.getBits(3); + } + var codeLenCodeTab = this.generateHuffmanTable(codeLenCodeLengths); + + // build the literal and distance code tables + len = 0; + i = 0; + var codes = numLitCodes + numDistCodes; + var codeLengths = new Uint8Array(codes); + var bitsLength, bitsOffset, what; + while (i < codes) { + var code = this.getCode(codeLenCodeTab); + if (code === 16) { + bitsLength = 2; + bitsOffset = 3; + what = len; + } else if (code === 17) { + bitsLength = 3; + bitsOffset = 3; + what = len = 0; + } else if (code === 18) { + bitsLength = 7; + bitsOffset = 11; + what = len = 0; + } else { + codeLengths[i++] = len = code; + continue; + } + + var repeatLength = this.getBits(bitsLength) + bitsOffset; + while (repeatLength-- > 0) { + codeLengths[i++] = what; + } + } + + litCodeTable = this.generateHuffmanTable( + codeLengths.subarray(0, numLitCodes) + ); + distCodeTable = this.generateHuffmanTable( + codeLengths.subarray(numLitCodes, codes) + ); + } else { + error("Unknown block type in flate stream"); + } + + buffer = this.buffer; + var limit = buffer ? buffer.length : 0; + var pos = this.bufferLength; + while (true) { + var code1 = this.getCode(litCodeTable); + if (code1 < 256) { + if (pos + 1 >= limit) { + buffer = this.ensureBuffer(pos + 1); + limit = buffer.length; + } + buffer[pos++] = code1; + continue; + } + if (code1 === 256) { + this.bufferLength = pos; + return; + } + code1 -= 257; + code1 = lengthDecode[code1]; + var code2 = code1 >> 16; + if (code2 > 0) { + code2 = this.getBits(code2); + } + len = (code1 & 0xffff) + code2; + code1 = this.getCode(distCodeTable); + code1 = distDecode[code1]; + code2 = code1 >> 16; + if (code2 > 0) { + code2 = this.getBits(code2); + } + var dist = (code1 & 0xffff) + code2; + if (pos + len >= limit) { + buffer = this.ensureBuffer(pos + len); + limit = buffer.length; + } + for (var k = 0; k < len; ++k, ++pos) { + buffer[pos] = buffer[pos - dist]; + } + } + }; + + return FlateStream; + })(); + + var PredictorStream = (function PredictorStreamClosure() { + function PredictorStream(str, maybeLength, params) { + var predictor = (this.predictor = params.get("Predictor") || 1); + + if (predictor <= 1) { + return str; // no prediction + } + if (predictor !== 2 && (predictor < 10 || predictor > 15)) { + error("Unsupported predictor: " + predictor); + } + + if (predictor === 2) { + this.readBlock = this.readBlockTiff; + } else { + this.readBlock = this.readBlockPng; + } + + this.str = str; + this.dict = str.dict; + + var colors = (this.colors = params.get("Colors") || 1); + var bits = (this.bits = params.get("BitsPerComponent") || 8); + var columns = (this.columns = params.get("Columns") || 1); + + this.pixBytes = (colors * bits + 7) >> 3; + this.rowBytes = (columns * colors * bits + 7) >> 3; + + DecodeStream.call(this, maybeLength); + return this; + } + + PredictorStream.prototype = Object.create(DecodeStream.prototype); + + PredictorStream.prototype.readBlockTiff = function predictorStreamReadBlockTiff() { + var rowBytes = this.rowBytes; + + var bufferLength = this.bufferLength; + var buffer = this.ensureBuffer(bufferLength + rowBytes); + + var bits = this.bits; + var colors = this.colors; + + var rawBytes = this.str.getBytes(rowBytes); + this.eof = !rawBytes.length; + if (this.eof) { + return; + } + + var inbuf = 0, + outbuf = 0; + var inbits = 0, + outbits = 0; + var pos = bufferLength; + var i; + + if (bits === 1) { + for (i = 0; i < rowBytes; ++i) { + var c = rawBytes[i]; + inbuf = (inbuf << 8) | c; + // bitwise addition is exclusive or + // first shift inbuf and then add + buffer[pos++] = (c ^ (inbuf >> colors)) & 0xff; + // truncate inbuf (assumes colors < 16) + inbuf &= 0xffff; + } + } else if (bits === 8) { + for (i = 0; i < colors; ++i) { + buffer[pos++] = rawBytes[i]; + } + for (; i < rowBytes; ++i) { + buffer[pos] = buffer[pos - colors] + rawBytes[i]; + pos++; + } + } else { + var compArray = new Uint8Array(colors + 1); + var bitMask = (1 << bits) - 1; + var j = 0, + k = bufferLength; + var columns = this.columns; + for (i = 0; i < columns; ++i) { + for (var kk = 0; kk < colors; ++kk) { + if (inbits < bits) { + inbuf = (inbuf << 8) | (rawBytes[j++] & 0xff); + inbits += 8; + } + compArray[kk] = + (compArray[kk] + (inbuf >> (inbits - bits))) & bitMask; + inbits -= bits; + outbuf = (outbuf << bits) | compArray[kk]; + outbits += bits; + if (outbits >= 8) { + buffer[k++] = (outbuf >> (outbits - 8)) & 0xff; + outbits -= 8; + } + } + } + if (outbits > 0) { + buffer[k++] = + (outbuf << (8 - outbits)) + (inbuf & ((1 << (8 - outbits)) - 1)); + } + } + this.bufferLength += rowBytes; + }; + + PredictorStream.prototype.readBlockPng = function predictorStreamReadBlockPng() { + var rowBytes = this.rowBytes; + var pixBytes = this.pixBytes; + + var predictor = this.str.getByte(); + var rawBytes = this.str.getBytes(rowBytes); + this.eof = !rawBytes.length; + if (this.eof) { + return; + } + + var bufferLength = this.bufferLength; + var buffer = this.ensureBuffer(bufferLength + rowBytes); + + var prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength); + if (prevRow.length === 0) { + prevRow = new Uint8Array(rowBytes); + } + + var i, + j = bufferLength, + up, + c; + switch (predictor) { + case 0: + for (i = 0; i < rowBytes; ++i) { + buffer[j++] = rawBytes[i]; + } + break; + case 1: + for (i = 0; i < pixBytes; ++i) { + buffer[j++] = rawBytes[i]; + } + for (; i < rowBytes; ++i) { + buffer[j] = (buffer[j - pixBytes] + rawBytes[i]) & 0xff; + j++; + } + break; + case 2: + for (i = 0; i < rowBytes; ++i) { + buffer[j++] = (prevRow[i] + rawBytes[i]) & 0xff; + } + break; + case 3: + for (i = 0; i < pixBytes; ++i) { + buffer[j++] = (prevRow[i] >> 1) + rawBytes[i]; + } + for (; i < rowBytes; ++i) { + buffer[j] = + (((prevRow[i] + buffer[j - pixBytes]) >> 1) + rawBytes[i]) & 0xff; + j++; + } + break; + case 4: + // we need to save the up left pixels values. the simplest way + // is to create a new buffer + for (i = 0; i < pixBytes; ++i) { + up = prevRow[i]; + c = rawBytes[i]; + buffer[j++] = up + c; + } + for (; i < rowBytes; ++i) { + up = prevRow[i]; + var upLeft = prevRow[i - pixBytes]; + var left = buffer[j - pixBytes]; + var p = left + up - upLeft; + + var pa = p - left; + if (pa < 0) { + pa = -pa; + } + var pb = p - up; + if (pb < 0) { + pb = -pb; + } + var pc = p - upLeft; + if (pc < 0) { + pc = -pc; + } + + c = rawBytes[i]; + if (pa <= pb && pa <= pc) { + buffer[j++] = left + c; + } else if (pb <= pc) { + buffer[j++] = up + c; + } else { + buffer[j++] = upLeft + c; + } + } + break; + default: + error("Unsupported predictor: " + predictor); + } + this.bufferLength += rowBytes; + }; + + return PredictorStream; + })(); + + /** + * Depending on the type of JPEG a JpegStream is handled in different ways. For + * JPEG's that are supported natively such as DeviceGray and DeviceRGB the image + * data is stored and then loaded by the browser. For unsupported JPEG's we use + * a library to decode these images and the stream behaves like all the other + * DecodeStreams. + */ + var JpegStream = (function JpegStreamClosure() { + function JpegStream(stream, maybeLength, dict, xref) { + // Some images may contain 'junk' before the SOI (start-of-image) marker. + // Note: this seems to mainly affect inline images. + var ch; + while ((ch = stream.getByte()) !== -1) { + if (ch === 0xff) { + // Find the first byte of the SOI marker (0xFFD8). + stream.skip(-1); // Reset the stream position to the SOI. + break; + } + } + this.stream = stream; + this.maybeLength = maybeLength; + this.dict = dict; + + DecodeStream.call(this, maybeLength); + } + + JpegStream.prototype = Object.create(DecodeStream.prototype); + + Object.defineProperty(JpegStream.prototype, "bytes", { + get: function JpegStream_bytes() { + // If this.maybeLength is null, we'll get the entire stream. + return shadow(this, "bytes", this.stream.getBytes(this.maybeLength)); + }, + configurable: true + }); + + JpegStream.prototype.ensureBuffer = function JpegStream_ensureBuffer(req) { + if (this.bufferLength) { + return; + } + try { + var jpegImage = new JpegImage(); + + // checking if values needs to be transformed before conversion + if (this.forceRGB && this.dict && isArray(this.dict.get("Decode"))) { + var decodeArr = this.dict.get("Decode"); + var bitsPerComponent = this.dict.get("BitsPerComponent") || 8; + var decodeArrLength = decodeArr.length; + var transform = new Int32Array(decodeArrLength); + var transformNeeded = false; + var maxValue = (1 << bitsPerComponent) - 1; + for (var i = 0; i < decodeArrLength; i += 2) { + transform[i] = ((decodeArr[i + 1] - decodeArr[i]) * 256) | 0; + transform[i + 1] = (decodeArr[i] * maxValue) | 0; + if (transform[i] !== 256 || transform[i + 1] !== 0) { + transformNeeded = true; + } + } + if (transformNeeded) { + jpegImage.decodeTransform = transform; + } + } + + jpegImage.parse(this.bytes); + var data = jpegImage.getData( + this.drawWidth, + this.drawHeight, + this.forceRGB + ); + this.buffer = data; + this.bufferLength = data.length; + this.eof = true; + } catch (e) { + error("JPEG error: " + e); + } + }; + + JpegStream.prototype.getBytes = function JpegStream_getBytes(length) { + this.ensureBuffer(); + return this.buffer; + }; + + JpegStream.prototype.getIR = function JpegStream_getIR() { + return PDFJS.createObjectURL(this.bytes, "image/jpeg"); + }; + /** + * Checks if the image can be decoded and displayed by the browser without any + * further processing such as color space conversions. + */ + JpegStream.prototype.isNativelySupported = function JpegStream_isNativelySupported( + xref, + res + ) { + var cs = ColorSpace.parse(this.dict.get("ColorSpace", "CS"), xref, res); + return ( + (cs.name === "DeviceGray" || cs.name === "DeviceRGB") && + cs.isDefaultDecode(this.dict.get("Decode", "D")) + ); + }; + /** + * Checks if the image can be decoded by the browser. + */ + JpegStream.prototype.isNativelyDecodable = function JpegStream_isNativelyDecodable( + xref, + res + ) { + var cs = ColorSpace.parse(this.dict.get("ColorSpace", "CS"), xref, res); + return ( + (cs.numComps === 1 || cs.numComps === 3) && + cs.isDefaultDecode(this.dict.get("Decode", "D")) + ); + }; + + return JpegStream; + })(); + + /** + * For JPEG 2000's we use a library to decode these images and + * the stream behaves like all the other DecodeStreams. + */ + var JpxStream = (function JpxStreamClosure() { + function JpxStream(stream, maybeLength, dict) { + this.stream = stream; + this.maybeLength = maybeLength; + this.dict = dict; + + DecodeStream.call(this, maybeLength); + } + + JpxStream.prototype = Object.create(DecodeStream.prototype); + + Object.defineProperty(JpxStream.prototype, "bytes", { + get: function JpxStream_bytes() { + // If this.maybeLength is null, we'll get the entire stream. + return shadow(this, "bytes", this.stream.getBytes(this.maybeLength)); + }, + configurable: true + }); + + JpxStream.prototype.ensureBuffer = function JpxStream_ensureBuffer(req) { + if (this.bufferLength) { + return; + } + + var jpxImage = new JpxImage(); + jpxImage.parse(this.bytes); + + var width = jpxImage.width; + var height = jpxImage.height; + var componentsCount = jpxImage.componentsCount; + var tileCount = jpxImage.tiles.length; + if (tileCount === 1) { + this.buffer = jpxImage.tiles[0].items; + } else { + var data = new Uint8Array(width * height * componentsCount); + + for (var k = 0; k < tileCount; k++) { + var tileComponents = jpxImage.tiles[k]; + var tileWidth = tileComponents.width; + var tileHeight = tileComponents.height; + var tileLeft = tileComponents.left; + var tileTop = tileComponents.top; + + var src = tileComponents.items; + var srcPosition = 0; + var dataPosition = (width * tileTop + tileLeft) * componentsCount; + var imgRowSize = width * componentsCount; + var tileRowSize = tileWidth * componentsCount; + + for (var j = 0; j < tileHeight; j++) { + var rowBytes = src.subarray(srcPosition, srcPosition + tileRowSize); + data.set(rowBytes, dataPosition); + srcPosition += tileRowSize; + dataPosition += imgRowSize; + } + } + this.buffer = data; + } + this.bufferLength = this.buffer.length; + this.eof = true; + }; + + return JpxStream; + })(); + + /** + * For JBIG2's we use a library to decode these images and + * the stream behaves like all the other DecodeStreams. + */ + var Jbig2Stream = (function Jbig2StreamClosure() { + function Jbig2Stream(stream, maybeLength, dict) { + this.stream = stream; + this.maybeLength = maybeLength; + this.dict = dict; + + DecodeStream.call(this, maybeLength); + } + + Jbig2Stream.prototype = Object.create(DecodeStream.prototype); + + Object.defineProperty(Jbig2Stream.prototype, "bytes", { + get: function Jbig2Stream_bytes() { + // If this.maybeLength is null, we'll get the entire stream. + return shadow(this, "bytes", this.stream.getBytes(this.maybeLength)); + }, + configurable: true + }); + + Jbig2Stream.prototype.ensureBuffer = function Jbig2Stream_ensureBuffer( + req + ) { + if (this.bufferLength) { + return; + } + + var jbig2Image = new Jbig2Image(); + + var chunks = [], + xref = this.dict.xref; + var decodeParams = xref.fetchIfRef(this.dict.get("DecodeParms")); + + // According to the PDF specification, DecodeParms can be either + // a dictionary, or an array whose elements are dictionaries. + if (isArray(decodeParams)) { + if (decodeParams.length > 1) { + warn( + "JBIG2 - 'DecodeParms' array with multiple elements " + + "not supported." + ); + } + decodeParams = xref.fetchIfRef(decodeParams[0]); + } + if (decodeParams && decodeParams.has("JBIG2Globals")) { + var globalsStream = decodeParams.get("JBIG2Globals"); + var globals = globalsStream.getBytes(); + chunks.push({ data: globals, start: 0, end: globals.length }); + } + chunks.push({ data: this.bytes, start: 0, end: this.bytes.length }); + var data = jbig2Image.parseChunks(chunks); + var dataLength = data.length; + + // JBIG2 had black as 1 and white as 0, inverting the colors + for (var i = 0; i < dataLength; i++) { + data[i] ^= 0xff; + } + + this.buffer = data; + this.bufferLength = dataLength; + this.eof = true; + }; + + return Jbig2Stream; + })(); + + var DecryptStream = (function DecryptStreamClosure() { + function DecryptStream(str, maybeLength, decrypt) { + this.str = str; + this.dict = str.dict; + this.decrypt = decrypt; + this.nextChunk = null; + this.initialized = false; + + DecodeStream.call(this, maybeLength); + } + + var chunkSize = 512; + + DecryptStream.prototype = Object.create(DecodeStream.prototype); + + DecryptStream.prototype.readBlock = function DecryptStream_readBlock() { + var chunk; + if (this.initialized) { + chunk = this.nextChunk; + } else { + chunk = this.str.getBytes(chunkSize); + this.initialized = true; + } + if (!chunk || chunk.length === 0) { + this.eof = true; + return; + } + this.nextChunk = this.str.getBytes(chunkSize); + var hasMoreData = this.nextChunk && this.nextChunk.length > 0; + + var decrypt = this.decrypt; + chunk = decrypt(chunk, !hasMoreData); + + 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; + }; + + return DecryptStream; + })(); + + var Ascii85Stream = (function Ascii85StreamClosure() { + function Ascii85Stream(str, maybeLength) { + this.str = str; + this.dict = str.dict; + this.input = new Uint8Array(5); + + // Most streams increase in size when decoded, but Ascii85 streams + // typically shrink by ~20%. + if (maybeLength) { + maybeLength = 0.8 * maybeLength; + } + DecodeStream.call(this, maybeLength); + } + + Ascii85Stream.prototype = Object.create(DecodeStream.prototype); + + Ascii85Stream.prototype.readBlock = function Ascii85Stream_readBlock() { + var TILDA_CHAR = 0x7e; // '~' + var Z_LOWER_CHAR = 0x7a; // 'z' + var EOF = -1; + + var str = this.str; + + var c = str.getByte(); + while (Lexer.isSpace(c)) { + c = str.getByte(); + } + + if (c === EOF || c === TILDA_CHAR) { + this.eof = true; + return; + } + + var bufferLength = this.bufferLength, + buffer; + var i; + + // special code for z + if (c === Z_LOWER_CHAR) { + buffer = this.ensureBuffer(bufferLength + 4); + for (i = 0; i < 4; ++i) { + buffer[bufferLength + i] = 0; + } + this.bufferLength += 4; + } else { + var input = this.input; + input[0] = c; + for (i = 1; i < 5; ++i) { + c = str.getByte(); + while (Lexer.isSpace(c)) { + c = str.getByte(); + } + + input[i] = c; + + if (c === EOF || c === TILDA_CHAR) { + break; + } + } + buffer = this.ensureBuffer(bufferLength + i - 1); + this.bufferLength += i - 1; + + // partial ending; + if (i < 5) { + for (; i < 5; ++i) { + input[i] = 0x21 + 84; + } + this.eof = true; + } + var t = 0; + for (i = 0; i < 5; ++i) { + t = t * 85 + (input[i] - 0x21); + } + + for (i = 3; i >= 0; --i) { + buffer[bufferLength + i] = t & 0xff; + t >>= 8; + } + } + }; + + return Ascii85Stream; + })(); + + var AsciiHexStream = (function AsciiHexStreamClosure() { + function AsciiHexStream(str, maybeLength) { + this.str = str; + this.dict = str.dict; + + this.firstDigit = -1; + + // Most streams increase in size when decoded, but AsciiHex streams shrink + // by 50%. + if (maybeLength) { + maybeLength = 0.5 * maybeLength; + } + DecodeStream.call(this, maybeLength); + } + + AsciiHexStream.prototype = Object.create(DecodeStream.prototype); + + AsciiHexStream.prototype.readBlock = function AsciiHexStream_readBlock() { + var UPSTREAM_BLOCK_SIZE = 8000; + var bytes = this.str.getBytes(UPSTREAM_BLOCK_SIZE); + if (!bytes.length) { + this.eof = true; + return; + } + + var maxDecodeLength = (bytes.length + 1) >> 1; + var buffer = this.ensureBuffer(this.bufferLength + maxDecodeLength); + var bufferLength = this.bufferLength; + + var firstDigit = this.firstDigit; + for (var i = 0, ii = bytes.length; i < ii; i++) { + var ch = bytes[i], + digit; + if (ch >= 0x30 && ch <= 0x39) { + // '0'-'9' + digit = ch & 0x0f; + } else if ((ch >= 0x41 && ch <= 0x46) || (ch >= 0x61 && ch <= 0x66)) { + // 'A'-'Z', 'a'-'z' + digit = (ch & 0x0f) + 9; + } else if (ch === 0x3e) { + // '>' + this.eof = true; + break; + } else { + // probably whitespace + continue; // ignoring + } + if (firstDigit < 0) { + firstDigit = digit; + } else { + buffer[bufferLength++] = (firstDigit << 4) | digit; + firstDigit = -1; + } + } + if (firstDigit >= 0 && this.eof) { + // incomplete byte + buffer[bufferLength++] = firstDigit << 4; + firstDigit = -1; + } + this.firstDigit = firstDigit; + this.bufferLength = bufferLength; + }; + + return AsciiHexStream; + })(); + + var RunLengthStream = (function RunLengthStreamClosure() { + function RunLengthStream(str, maybeLength) { + this.str = str; + this.dict = str.dict; + + DecodeStream.call(this, maybeLength); + } + + RunLengthStream.prototype = Object.create(DecodeStream.prototype); + + RunLengthStream.prototype.readBlock = function RunLengthStream_readBlock() { + // The repeatHeader has following format. The first byte defines type of run + // and amount of bytes to repeat/copy: n = 0 through 127 - copy next n bytes + // (in addition to the second byte from the header), n = 129 through 255 - + // duplicate the second byte from the header (257 - n) times, n = 128 - end. + var repeatHeader = this.str.getBytes(2); + if (!repeatHeader || repeatHeader.length < 2 || repeatHeader[0] === 128) { + this.eof = true; + return; + } + + var buffer; + var bufferLength = this.bufferLength; + var n = repeatHeader[0]; + if (n < 128) { + // copy n bytes + buffer = this.ensureBuffer(bufferLength + n + 1); + buffer[bufferLength++] = repeatHeader[1]; + if (n > 0) { + var source = this.str.getBytes(n); + buffer.set(source, bufferLength); + bufferLength += n; + } + } else { + n = 257 - n; + var b = repeatHeader[1]; + buffer = this.ensureBuffer(bufferLength + n + 1); + for (var i = 0; i < n; i++) { + buffer[bufferLength++] = b; + } + } + this.bufferLength = bufferLength; + }; + + return RunLengthStream; + })(); + + var CCITTFaxStream = (function CCITTFaxStreamClosure() { + var ccittEOL = -2; + var twoDimPass = 0; + var twoDimHoriz = 1; + var twoDimVert0 = 2; + var twoDimVertR1 = 3; + var twoDimVertL1 = 4; + var twoDimVertR2 = 5; + var twoDimVertL2 = 6; + var twoDimVertR3 = 7; + var twoDimVertL3 = 8; + + var 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] + ]; + + var 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 + ]; + + var 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] + ]; + + var 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] + ]; + + var 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] + ]; + + var 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 CCITTFaxStream(str, maybeLength, params) { + this.str = str; + this.dict = str.dict; + + params = params || Dict.empty; + + 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 === undefined) { + 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 = 0; + this.outputBits = 0; + + 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, maybeLength); + } + + CCITTFaxStream.prototype = Object.create(DecodeStream.prototype); + + CCITTFaxStream.prototype.readBlock = function CCITTFaxStream_readBlock() { + while (!this.eof) { + var c = this.lookChar(); + this.ensureBuffer(this.bufferLength + 1); + this.buffer[this.bufferLength++] = c; + } + }; + + CCITTFaxStream.prototype.addPixels = function ccittFaxStreamAddPixels( + a1, + blackPixels + ) { + var codingLine = this.codingLine; + var codingPos = this.codingPos; + + if (a1 > codingLine[codingPos]) { + if (a1 > this.columns) { + info("row is wrong length"); + this.err = true; + a1 = this.columns; + } + if ((codingPos & 1) ^ blackPixels) { + ++codingPos; + } + + codingLine[codingPos] = a1; + } + this.codingPos = codingPos; + }; + + CCITTFaxStream.prototype.addPixelsNeg = function ccittFaxStreamAddPixelsNeg( + a1, + blackPixels + ) { + var codingLine = this.codingLine; + var codingPos = this.codingPos; + + if (a1 > codingLine[codingPos]) { + if (a1 > this.columns) { + info("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) { + info("invalid code"); + this.err = true; + a1 = 0; + } + while (codingPos > 0 && a1 < codingLine[codingPos - 1]) { + --codingPos; + } + codingLine[codingPos] = a1; + } + + this.codingPos = codingPos; + }; + + CCITTFaxStream.prototype.lookChar = function CCITTFaxStream_lookChar() { + var refLine = this.refLine; + var codingLine = this.codingLine; + var columns = this.columns; + + var refPos, blackPixels, bits, i; + + if (this.outputBits === 0) { + if (this.eof) { + return null; + } + this.err = false; + + var code1, code2, code3; + if (this.nextLine2D) { + for (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) { + code1 = this.getTwoDimCode(); + switch (code1) { + case twoDimPass: + this.addPixels(refLine[refPos + 1], blackPixels); + if (refLine[refPos + 1] < columns) { + refPos += 2; + } + break; + case twoDimHoriz: + code1 = code2 = 0; + if (blackPixels) { + do { + code1 += code3 = this.getBlackCode(); + } while (code3 >= 64); + do { + code2 += code3 = this.getWhiteCode(); + } while (code3 >= 64); + } else { + 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: + info("bad 2d code"); + this.addPixels(columns, 0); + this.err = true; + } + } + } 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; + } + } + + var gotEOL = false; + + if (this.byteAlign) { + this.inputBits &= ~7; + } + + if (!this.eoblock && this.row === this.rows - 1) { + this.eof = true; + } else { + code1 = this.lookBits(12); + if (this.eoline) { + while (code1 !== EOF && code1 !== 1) { + this.eatBits(1); + code1 = this.lookBits(12); + } + } else { + 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 && this.byteAlign) { + 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 (i = 0; i < 4; ++i) { + code1 = this.lookBits(12); + if (code1 !== 1) { + info("bad rtc code: " + code1); + } + this.eatBits(12); + if (this.encoding > 0) { + this.lookBits(1); + this.eatBits(1); + } + } + } + this.eof = true; + } + } else if (this.err && this.eoline) { + while (true) { + code1 = this.lookBits(13); + if (code1 === EOF) { + this.eof = true; + return null; + } + 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++; + } + + var c; + if (this.outputBits >= 8) { + c = 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 { + bits = 8; + c = 0; + do { + if (this.outputBits > bits) { + c <<= bits; + if (!(this.codingPos & 1)) { + c |= 0xff >> (8 - bits); + } + this.outputBits -= bits; + bits = 0; + } else { + c <<= this.outputBits; + if (!(this.codingPos & 1)) { + c |= 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) { + c <<= bits; + bits = 0; + } + } + } while (bits); + } + if (this.black) { + c ^= 0xff; + } + return c; + }; + + // This functions returns the code found from the table. + // The start and end parameters set the boundaries for searching the table. + // The limit parameter is optional. Function returns an array with three + // values. The first array element indicates whether a valid code is being + // returned. The second array element is the actual code. The third array + // element indicates whether EOF was reached. + CCITTFaxStream.prototype.findTableCode = function ccittFaxStreamFindTableCode( + start, + end, + table, + limit + ) { + var limitValue = limit || 0; + for (var i = start; i <= end; ++i) { + var code = this.lookBits(i); + if (code === EOF) { + return [true, 1, false]; + } + if (i < end) { + code <<= end - i; + } + if (!limitValue || code >= limitValue) { + var p = table[code - limitValue]; + if (p[0] === i) { + this.eatBits(i); + return [true, p[1], true]; + } + } + } + return [false, 0, false]; + }; + + CCITTFaxStream.prototype.getTwoDimCode = function ccittFaxStreamGetTwoDimCode() { + var code = 0; + var p; + if (this.eoblock) { + code = this.lookBits(7); + p = twoDimTable[code]; + if (p && p[0] > 0) { + this.eatBits(p[0]); + return p[1]; + } + } else { + var result = this.findTableCode(1, 7, twoDimTable); + if (result[0] && result[2]) { + return result[1]; + } + } + info("Bad two dim code"); + return EOF; + }; + + CCITTFaxStream.prototype.getWhiteCode = function ccittFaxStreamGetWhiteCode() { + var code = 0; + var p; + 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 { + var result = this.findTableCode(1, 9, whiteTable2); + if (result[0]) { + return result[1]; + } + + result = this.findTableCode(11, 12, whiteTable1); + if (result[0]) { + return result[1]; + } + } + info("bad white code"); + this.eatBits(1); + return 1; + }; + + CCITTFaxStream.prototype.getBlackCode = function ccittFaxStreamGetBlackCode() { + var code, p; + 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 { + var result = this.findTableCode(2, 6, blackTable3); + if (result[0]) { + return result[1]; + } + + result = this.findTableCode(7, 12, blackTable2, 64); + if (result[0]) { + return result[1]; + } + + result = this.findTableCode(10, 13, blackTable1); + if (result[0]) { + return result[1]; + } + } + info("bad black code"); + this.eatBits(1); + return 1; + }; + + CCITTFaxStream.prototype.lookBits = function CCITTFaxStream_lookBits(n) { + var c; + while (this.inputBits < n) { + if ((c = this.str.getByte()) === -1) { + 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)); + }; + + CCITTFaxStream.prototype.eatBits = function CCITTFaxStream_eatBits(n) { + if ((this.inputBits -= n) < 0) { + this.inputBits = 0; + } + }; + + return CCITTFaxStream; + })(); + + var LZWStream = (function LZWStreamClosure() { + function LZWStream(str, maybeLength, earlyChange) { + this.str = str; + this.dict = str.dict; + this.cachedData = 0; + this.bitsCached = 0; + + var maxLzwDictionarySize = 4096; + var lzwState = { + earlyChange: earlyChange, + codeLength: 9, + nextCode: 258, + dictionaryValues: new Uint8Array(maxLzwDictionarySize), + dictionaryLengths: new Uint16Array(maxLzwDictionarySize), + dictionaryPrevCodes: new Uint16Array(maxLzwDictionarySize), + currentSequence: new Uint8Array(maxLzwDictionarySize), + currentSequenceLength: 0 + }; + for (var i = 0; i < 256; ++i) { + lzwState.dictionaryValues[i] = i; + lzwState.dictionaryLengths[i] = 1; + } + this.lzwState = lzwState; + + DecodeStream.call(this, maybeLength); + } + + LZWStream.prototype = Object.create(DecodeStream.prototype); + + LZWStream.prototype.readBits = function LZWStream_readBits(n) { + var bitsCached = this.bitsCached; + var cachedData = this.cachedData; + while (bitsCached < n) { + var c = this.str.getByte(); + if (c === -1) { + this.eof = true; + return null; + } + cachedData = (cachedData << 8) | c; + bitsCached += 8; + } + this.bitsCached = bitsCached -= n; + this.cachedData = cachedData; + this.lastCode = null; + return (cachedData >>> bitsCached) & ((1 << n) - 1); + }; + + LZWStream.prototype.readBlock = function LZWStream_readBlock() { + var blockSize = 512; + var estimatedDecodedSize = blockSize * 2, + decodedSizeDelta = blockSize; + var i, j, q; + + var lzwState = this.lzwState; + if (!lzwState) { + return; // eof was found + } + + var earlyChange = lzwState.earlyChange; + var nextCode = lzwState.nextCode; + var dictionaryValues = lzwState.dictionaryValues; + var dictionaryLengths = lzwState.dictionaryLengths; + var dictionaryPrevCodes = lzwState.dictionaryPrevCodes; + var codeLength = lzwState.codeLength; + var prevCode = lzwState.prevCode; + var currentSequence = lzwState.currentSequence; + var currentSequenceLength = lzwState.currentSequenceLength; + + var decodedLength = 0; + var currentBufferLength = this.bufferLength; + var buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize); + + for (i = 0; i < blockSize; i++) { + var code = this.readBits(codeLength); + var hasPrev = currentSequenceLength > 0; + if (code < 256) { + currentSequence[0] = code; + currentSequenceLength = 1; + } else if (code >= 258) { + if (code < nextCode) { + currentSequenceLength = dictionaryLengths[code]; + for (j = currentSequenceLength - 1, q = code; j >= 0; j--) { + currentSequence[j] = dictionaryValues[q]; + q = dictionaryPrevCodes[q]; + } + } else { + currentSequence[currentSequenceLength++] = currentSequence[0]; + } + } else if (code === 256) { + codeLength = 9; + nextCode = 258; + currentSequenceLength = 0; + continue; + } else { + this.eof = true; + delete this.lzwState; + break; + } + + if (hasPrev) { + dictionaryPrevCodes[nextCode] = prevCode; + dictionaryLengths[nextCode] = dictionaryLengths[prevCode] + 1; + dictionaryValues[nextCode] = currentSequence[0]; + nextCode++; + codeLength = + (nextCode + earlyChange) & (nextCode + earlyChange - 1) + ? codeLength + : Math.min( + Math.log(nextCode + earlyChange) / 0.6931471805599453 + 1, + 12 + ) | 0; + } + prevCode = code; + + decodedLength += currentSequenceLength; + if (estimatedDecodedSize < decodedLength) { + do { + estimatedDecodedSize += decodedSizeDelta; + } while (estimatedDecodedSize < decodedLength); + buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize); + } + for (j = 0; j < currentSequenceLength; j++) { + buffer[currentBufferLength++] = currentSequence[j]; + } + } + lzwState.nextCode = nextCode; + lzwState.codeLength = codeLength; + lzwState.prevCode = prevCode; + lzwState.currentSequenceLength = currentSequenceLength; + + this.bufferLength = currentBufferLength; + }; + + return LZWStream; + })(); + + var NullStream = (function NullStreamClosure() { + function NullStream() { + Stream.call(this, new Uint8Array(0)); + } + + NullStream.prototype = Stream.prototype; + + return NullStream; + })(); + + // TODO refactor to remove dependency on parser.js + function _setCoreParser(coreParser_) { + coreParser = coreParser_; + EOF = coreParser_.EOF; + Lexer = coreParser_.Lexer; + } + exports._setCoreParser = _setCoreParser; + + // TODO refactor to remove dependency on colorspace.js + function _setCoreColorSpace(coreColorSpace_) { + coreColorSpace = coreColorSpace_; + ColorSpace = coreColorSpace_.ColorSpace; + } + exports._setCoreColorSpace = _setCoreColorSpace; + + exports.Ascii85Stream = Ascii85Stream; + exports.AsciiHexStream = AsciiHexStream; + exports.CCITTFaxStream = CCITTFaxStream; + exports.DecryptStream = DecryptStream; + exports.DecodeStream = DecodeStream; + exports.FlateStream = FlateStream; + exports.Jbig2Stream = Jbig2Stream; + exports.JpegStream = JpegStream; + exports.JpxStream = JpxStream; + exports.NullStream = NullStream; + exports.PredictorStream = PredictorStream; + exports.RunLengthStream = RunLengthStream; + exports.Stream = Stream; + exports.StreamsSequenceStream = StreamsSequenceStream; + exports.StringStream = StringStream; + exports.LZWStream = LZWStream; +}); + +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +("use strict"); + +(function(root, factory) { + //if (typeof define === 'function' && define.amd) { + // define('pdfjs/core/parser', ['exports', 'pdfjs/shared/util', + // 'pdfjs/core/primitives', 'pdfjs/core/stream'], factory); + // } else if (typeof exports !== 'undefined') { + // factory(exports, require('../shared/util.js'), require('./primitives.js'), + // require('./stream.js')); + //} else { + factory( + (root.pdfjsCoreParser = {}), + root.pdfjsSharedUtil, + root.pdfjsCorePrimitives, + root.pdfjsCoreStream + ); + //} +})(window, function(exports, sharedUtil, corePrimitives, coreStream) { + var MissingDataException = sharedUtil.MissingDataException; + var StreamType = sharedUtil.StreamType; + var assert = sharedUtil.assert; + var error = sharedUtil.error; + var info = sharedUtil.info; + var isArray = sharedUtil.isArray; + var isInt = sharedUtil.isInt; + var isNum = sharedUtil.isNum; + var isString = sharedUtil.isString; + var warn = sharedUtil.warn; + var Cmd = corePrimitives.Cmd; + var Dict = corePrimitives.Dict; + var Name = corePrimitives.Name; + var Ref = corePrimitives.Ref; + var isCmd = corePrimitives.isCmd; + var isDict = corePrimitives.isDict; + var isName = corePrimitives.isName; + var Ascii85Stream = coreStream.Ascii85Stream; + var AsciiHexStream = coreStream.AsciiHexStream; + var CCITTFaxStream = coreStream.CCITTFaxStream; + var FlateStream = coreStream.FlateStream; + var Jbig2Stream = coreStream.Jbig2Stream; + var JpegStream = coreStream.JpegStream; + var JpxStream = coreStream.JpxStream; + var LZWStream = coreStream.LZWStream; + var NullStream = coreStream.NullStream; + var PredictorStream = coreStream.PredictorStream; + var RunLengthStream = coreStream.RunLengthStream; + + var EOF = {}; + + function isEOF(v) { + return v === EOF; + } + + var MAX_LENGTH_TO_CACHE = 1000; + + var Parser = (function ParserClosure() { + function Parser(lexer, allowStreams, xref) { + this.lexer = lexer; + this.allowStreams = allowStreams; + this.xref = xref; + this.imageCache = {}; + this.refill(); + } + + Parser.prototype = { + refill: function Parser_refill() { + this.buf1 = this.lexer.getObj(); + this.buf2 = this.lexer.getObj(); + }, + shift: function Parser_shift() { + if (isCmd(this.buf2, "ID")) { + this.buf1 = this.buf2; + this.buf2 = null; + } else { + this.buf1 = this.buf2; + this.buf2 = this.lexer.getObj(); + } + }, + tryShift: function Parser_tryShift() { + try { + this.shift(); + return true; + } catch (e) { + if (e instanceof MissingDataException) { + throw e; + } + // Upon failure, the caller should reset this.lexer.pos to a known good + // state and call this.shift() twice to reset the buffers. + return false; + } + }, + getObj: function Parser_getObj(cipherTransform) { + var buf1 = this.buf1; + this.shift(); + + if (buf1 instanceof Cmd) { + switch (buf1.cmd) { + case "BI": // inline image + return this.makeInlineImage(cipherTransform); + case "[": // array + var array = []; + while (!isCmd(this.buf1, "]") && !isEOF(this.buf1)) { + array.push(this.getObj(cipherTransform)); + } + if (isEOF(this.buf1)) { + error("End of file inside array"); + } + this.shift(); + return array; + case "<<": // dictionary or stream + var dict = new Dict(this.xref); + while (!isCmd(this.buf1, ">>") && !isEOF(this.buf1)) { + if (!isName(this.buf1)) { + info("Malformed dictionary: key must be a name object"); + this.shift(); + continue; + } + var pos = this.lexer.stream.pos; + var key = this.buf1.name; + dict.set("#" + key + "_offset", pos); + this.shift(); + if (isEOF(this.buf1)) { + break; + } + dict.set(key, this.getObj(cipherTransform)); + } + if (isEOF(this.buf1)) { + error("End of file inside dictionary"); + } + + // Stream objects are not allowed inside content streams or + // object streams. + if (isCmd(this.buf2, "stream")) { + return this.allowStreams + ? this.makeStream(dict, cipherTransform) + : dict; + } + this.shift(); + return dict; + default: + // simple object + return buf1; + } + } + + if (isInt(buf1)) { + // indirect reference or integer + var num = buf1; + if (isInt(this.buf1) && isCmd(this.buf2, "R")) { + var ref = new Ref(num, this.buf1); + this.shift(); + this.shift(); + return ref; + } + return num; + } + + if (isString(buf1)) { + // string + var str = buf1; + if (cipherTransform) { + str = cipherTransform.decryptString(str); + } + return str; + } + + // simple object + return buf1; + }, + /** + * Find the end of the stream by searching for the /EI\s/. + * @returns {number} The inline stream length. + */ + findDefaultInlineStreamEnd: function Parser_findDefaultInlineStreamEnd( + stream + ) { + var E = 0x45, + I = 0x49, + SPACE = 0x20, + LF = 0xa, + CR = 0xd; + var startPos = stream.pos, + state = 0, + ch, + i, + n, + followingBytes; + while ((ch = stream.getByte()) !== -1) { + if (state === 0) { + state = ch === E ? 1 : 0; + } else if (state === 1) { + state = ch === I ? 2 : 0; + } else { + assert(state === 2); + if (ch === SPACE || ch === LF || ch === CR) { + // Let's check the next five bytes are ASCII... just be sure. + n = 5; + followingBytes = stream.peekBytes(n); + for (i = 0; i < n; i++) { + ch = followingBytes[i]; + if (ch !== LF && ch !== CR && (ch < SPACE || ch > 0x7f)) { + // Not a LF, CR, SPACE or any visible ASCII character, i.e. + // it's binary stuff. Resetting the state. + state = 0; + break; + } + } + if (state === 2) { + break; // Finished! + } + } else { + state = 0; + } + } + } + return stream.pos - 4 - startPos; + }, + /** + * Find the EOI (end-of-image) marker 0xFFD9 of the stream. + * @returns {number} The inline stream length. + */ + findDCTDecodeInlineStreamEnd: function Parser_findDCTDecodeInlineStreamEnd( + stream + ) { + var startPos = stream.pos, + foundEOI = false, + b, + markerLength, + length; + while ((b = stream.getByte()) !== -1) { + if (b !== 0xff) { + // Not a valid marker. + continue; + } + switch (stream.getByte()) { + case 0x00: // Byte stuffing. + // 0xFF00 appears to be a very common byte sequence in JPEG images. + break; + + case 0xff: // Fill byte. + // Avoid skipping a valid marker, resetting the stream position. + stream.skip(-1); + break; + + case 0xd9: // EOI + foundEOI = true; + break; + + case 0xc0: // SOF0 + case 0xc1: // SOF1 + case 0xc2: // SOF2 + case 0xc3: // SOF3 + + case 0xc5: // SOF5 + case 0xc6: // SOF6 + case 0xc7: // SOF7 + + case 0xc9: // SOF9 + case 0xca: // SOF10 + case 0xcb: // SOF11 + + case 0xcd: // SOF13 + case 0xce: // SOF14 + case 0xcf: // SOF15 + + case 0xc4: // DHT + case 0xcc: // DAC + + case 0xda: // SOS + case 0xdb: // DQT + case 0xdc: // DNL + case 0xdd: // DRI + case 0xde: // DHP + case 0xdf: // EXP + + case 0xe0: // APP0 + case 0xe1: // APP1 + case 0xe2: // APP2 + case 0xe3: // APP3 + case 0xe4: // APP4 + case 0xe5: // APP5 + case 0xe6: // APP6 + case 0xe7: // APP7 + case 0xe8: // APP8 + case 0xe9: // APP9 + case 0xea: // APP10 + case 0xeb: // APP11 + case 0xec: // APP12 + case 0xed: // APP13 + case 0xee: // APP14 + case 0xef: // APP15 + + case 0xfe: // COM + // The marker should be followed by the length of the segment. + markerLength = stream.getUint16(); + if (markerLength > 2) { + // |markerLength| contains the byte length of the marker segment, + // including its own length (2 bytes) and excluding the marker. + stream.skip(markerLength - 2); // Jump to the next marker. + } else { + // The marker length is invalid, resetting the stream position. + stream.skip(-2); + } + break; + } + if (foundEOI) { + break; + } + } + length = stream.pos - startPos; + if (b === -1) { + warn( + "Inline DCTDecode image stream: " + + "EOI marker not found, searching for /EI/ instead." + ); + stream.skip(-length); // Reset the stream position. + return this.findDefaultInlineStreamEnd(stream); + } + this.inlineStreamSkipEI(stream); + return length; + }, + /** + * Find the EOD (end-of-data) marker '~>' (i.e. TILDE + GT) of the stream. + * @returns {number} The inline stream length. + */ + findASCII85DecodeInlineStreamEnd: function Parser_findASCII85DecodeInlineStreamEnd( + stream + ) { + var TILDE = 0x7e, + GT = 0x3e; + var startPos = stream.pos, + ch, + length; + while ((ch = stream.getByte()) !== -1) { + if (ch === TILDE && stream.peekByte() === GT) { + stream.skip(); + break; + } + } + length = stream.pos - startPos; + if (ch === -1) { + warn( + "Inline ASCII85Decode image stream: " + + "EOD marker not found, searching for /EI/ instead." + ); + stream.skip(-length); // Reset the stream position. + return this.findDefaultInlineStreamEnd(stream); + } + this.inlineStreamSkipEI(stream); + return length; + }, + /** + * Find the EOD (end-of-data) marker '>' (i.e. GT) of the stream. + * @returns {number} The inline stream length. + */ + findASCIIHexDecodeInlineStreamEnd: function Parser_findASCIIHexDecodeInlineStreamEnd( + stream + ) { + var GT = 0x3e; + var startPos = stream.pos, + ch, + length; + while ((ch = stream.getByte()) !== -1) { + if (ch === GT) { + break; + } + } + length = stream.pos - startPos; + if (ch === -1) { + warn( + "Inline ASCIIHexDecode image stream: " + + "EOD marker not found, searching for /EI/ instead." + ); + stream.skip(-length); // Reset the stream position. + return this.findDefaultInlineStreamEnd(stream); + } + this.inlineStreamSkipEI(stream); + return length; + }, + /** + * Skip over the /EI/ for streams where we search for an EOD marker. + */ + inlineStreamSkipEI: function Parser_inlineStreamSkipEI(stream) { + var E = 0x45, + I = 0x49; + var state = 0, + ch; + while ((ch = stream.getByte()) !== -1) { + if (state === 0) { + state = ch === E ? 1 : 0; + } else if (state === 1) { + state = ch === I ? 2 : 0; + } else if (state === 2) { + break; + } + } + }, + makeInlineImage: function Parser_makeInlineImage(cipherTransform) { + var lexer = this.lexer; + var stream = lexer.stream; + + // Parse dictionary. + var dict = new Dict(this.xref); + while (!isCmd(this.buf1, "ID") && !isEOF(this.buf1)) { + if (!isName(this.buf1)) { + error("Dictionary key must be a name object"); + } + var key = this.buf1.name; + this.shift(); + if (isEOF(this.buf1)) { + break; + } + dict.set(key, this.getObj(cipherTransform)); + } + + // Extract the name of the first (i.e. the current) image filter. + var filter = dict.get("Filter", "F"), + filterName; + if (isName(filter)) { + filterName = filter.name; + } else if (isArray(filter) && isName(filter[0])) { + filterName = filter[0].name; + } + + // Parse image stream. + var startPos = stream.pos, + length, + i, + ii; + if (filterName === "DCTDecode" || filterName === "DCT") { + length = this.findDCTDecodeInlineStreamEnd(stream); + } else if (filterName === "ASCII85Decide" || filterName === "A85") { + length = this.findASCII85DecodeInlineStreamEnd(stream); + } else if (filterName === "ASCIIHexDecode" || filterName === "AHx") { + length = this.findASCIIHexDecodeInlineStreamEnd(stream); + } else { + length = this.findDefaultInlineStreamEnd(stream); + } + var imageStream = stream.makeSubStream(startPos, length, dict); + + // Cache all images below the MAX_LENGTH_TO_CACHE threshold by their + // adler32 checksum. + var adler32; + if (length < MAX_LENGTH_TO_CACHE) { + var imageBytes = imageStream.getBytes(); + imageStream.reset(); + + var a = 1; + var b = 0; + for (i = 0, ii = imageBytes.length; i < ii; ++i) { + // No modulo required in the loop if imageBytes.length < 5552. + a += imageBytes[i] & 0xff; + b += a; + } + adler32 = (b % 65521 << 16) | a % 65521; + + if (this.imageCache.adler32 === adler32) { + this.buf2 = Cmd.get("EI"); + this.shift(); + + this.imageCache[adler32].reset(); + return this.imageCache[adler32]; + } + } + + if (cipherTransform) { + imageStream = cipherTransform.createStream(imageStream, length); + } + + imageStream = this.filter(imageStream, dict, length); + imageStream.dict = dict; + if (adler32 !== undefined) { + imageStream.cacheKey = "inline_" + length + "_" + adler32; + this.imageCache[adler32] = imageStream; + } + + this.buf2 = Cmd.get("EI"); + this.shift(); + + return imageStream; + }, + makeStream: function Parser_makeStream(dict, cipherTransform) { + var lexer = this.lexer; + var stream = lexer.stream; + + // get stream start position + lexer.skipToNextLine(); + var pos = stream.pos - 1; + + // get length + var length = dict.get("Length"); + if (!isInt(length)) { + info("Bad " + length + " attribute in stream"); + length = 0; + } + + // skip over the stream data + stream.pos = pos + length; + lexer.nextChar(); + + // Shift '>>' and check whether the new object marks the end of the stream + if (this.tryShift() && isCmd(this.buf2, "endstream")) { + this.shift(); // 'stream' + } else { + // bad stream length, scanning for endstream + stream.pos = pos; + var SCAN_BLOCK_SIZE = 2048; + var ENDSTREAM_SIGNATURE_LENGTH = 9; + var ENDSTREAM_SIGNATURE = [ + 0x65, + 0x6e, + 0x64, + 0x73, + 0x74, + 0x72, + 0x65, + 0x61, + 0x6d + ]; + var skipped = 0, + found = false, + i, + j; + while (stream.pos < stream.end) { + var scanBytes = stream.peekBytes(SCAN_BLOCK_SIZE); + var scanLength = scanBytes.length - ENDSTREAM_SIGNATURE_LENGTH; + if (scanLength <= 0) { + break; + } + found = false; + for (i = 0, j = 0; i < scanLength; i++) { + var b = scanBytes[i]; + if (b !== ENDSTREAM_SIGNATURE[j]) { + i -= j; + j = 0; + } else { + j++; + if (j >= ENDSTREAM_SIGNATURE_LENGTH) { + i++; + found = true; + break; + } + } + } + if (found) { + skipped += i - ENDSTREAM_SIGNATURE_LENGTH; + stream.pos += i - ENDSTREAM_SIGNATURE_LENGTH; + break; + } + skipped += scanLength; + stream.pos += scanLength; + } + if (!found) { + error("Missing endstream"); + } + length = skipped; + + lexer.nextChar(); + this.shift(); + this.shift(); + } + this.shift(); // 'endstream' + + stream = stream.makeSubStream(pos, length, dict); + if (cipherTransform) { + stream = cipherTransform.createStream(stream, length); + } + stream = this.filter(stream, dict, length); + stream.dict = dict; + return stream; + }, + filter: function Parser_filter(stream, dict, length) { + var filter = dict.get("Filter", "F"); + var params = dict.get("DecodeParms", "DP"); + if (isName(filter)) { + return this.makeFilter(stream, filter.name, length, params); + } + + var maybeLength = length; + if (isArray(filter)) { + var filterArray = filter; + var paramsArray = params; + for (var i = 0, ii = filterArray.length; i < ii; ++i) { + filter = filterArray[i]; + if (!isName(filter)) { + error("Bad filter name: " + filter); + } + + params = null; + if (isArray(paramsArray) && i in paramsArray) { + params = paramsArray[i]; + } + stream = this.makeFilter(stream, filter.name, maybeLength, params); + // after the first stream the length variable is invalid + maybeLength = null; + } + } + return stream; + }, + makeFilter: function Parser_makeFilter( + stream, + name, + maybeLength, + params + ) { + if (stream.dict.get("Length") === 0 && !maybeLength) { + warn('Empty "' + name + '" stream.'); + return new NullStream(stream); + } + try { + if (params && this.xref) { + params = this.xref.fetchIfRef(params); + } + var xrefStreamStats = this.xref.stats.streamTypes; + if (name === "FlateDecode" || name === "Fl") { + xrefStreamStats[StreamType.FLATE] = true; + if (params) { + return new PredictorStream( + new FlateStream(stream, maybeLength), + maybeLength, + params + ); + } + return new FlateStream(stream, maybeLength); + } + if (name === "LZWDecode" || name === "LZW") { + xrefStreamStats[StreamType.LZW] = true; + var earlyChange = 1; + if (params) { + if (params.has("EarlyChange")) { + earlyChange = params.get("EarlyChange"); + } + return new PredictorStream( + new LZWStream(stream, maybeLength, earlyChange), + maybeLength, + params + ); + } + return new LZWStream(stream, maybeLength, earlyChange); + } + if (name === "DCTDecode" || name === "DCT") { + xrefStreamStats[StreamType.DCT] = true; + return new JpegStream(stream, maybeLength, stream.dict, this.xref); + } + if (name === "JPXDecode" || name === "JPX") { + xrefStreamStats[StreamType.JPX] = true; + return new JpxStream(stream, maybeLength, stream.dict); + } + if (name === "ASCII85Decode" || name === "A85") { + xrefStreamStats[StreamType.A85] = true; + return new Ascii85Stream(stream, maybeLength); + } + if (name === "ASCIIHexDecode" || name === "AHx") { + xrefStreamStats[StreamType.AHX] = true; + return new AsciiHexStream(stream, maybeLength); + } + if (name === "CCITTFaxDecode" || name === "CCF") { + xrefStreamStats[StreamType.CCF] = true; + return new CCITTFaxStream(stream, maybeLength, params); + } + if (name === "RunLengthDecode" || name === "RL") { + xrefStreamStats[StreamType.RL] = true; + return new RunLengthStream(stream, maybeLength); + } + if (name === "JBIG2Decode") { + xrefStreamStats[StreamType.JBIG] = true; + return new Jbig2Stream(stream, maybeLength, stream.dict); + } + warn('filter "' + name + '" not supported yet'); + return stream; + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn('Invalid stream: "' + ex + '"'); + return new NullStream(stream); + } + } + }; + + return Parser; + })(); + + var Lexer = (function LexerClosure() { + function Lexer(stream, knownCommands) { + this.stream = stream; + this.nextChar(); + + // While lexing, we build up many strings one char at a time. Using += for + // this can result in lots of garbage strings. It's better to build an + // array of single-char strings and then join() them together at the end. + // And reusing a single array (i.e. |this.strBuf|) over and over for this + // purpose uses less memory than using a new array for each string. + this.strBuf = []; + + // The PDFs might have "glued" commands with other commands, operands or + // literals, e.g. "q1". The knownCommands is a dictionary of the valid + // commands and their prefixes. The prefixes are built the following way: + // if there a command that is a prefix of the other valid command or + // literal (e.g. 'f' and 'false') the following prefixes must be included, + // 'fa', 'fal', 'fals'. The prefixes are not needed, if the command has no + // other commands or literals as a prefix. The knowCommands is optional. + this.knownCommands = knownCommands; + } + + Lexer.isSpace = function Lexer_isSpace(ch) { + // Space is one of the following characters: SPACE, TAB, CR or LF. + return ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a; + }; + + // A '1' in this array means the character is white space. A '1' or + // '2' means the character ends a name or command. + var specialChars = [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, // 0x + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // 1x + 1, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 2, + 2, + 0, + 0, + 0, + 0, + 0, + 2, // 2x + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 2, + 0, // 3x + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // 4x + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 2, + 0, + 0, // 5x + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // 6x + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 2, + 0, + 0, // 7x + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // 8x + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // 9x + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // ax + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // bx + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // cx + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // dx + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // ex + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 // fx + ]; + + function toHexDigit(ch) { + if (ch >= 0x30 && ch <= 0x39) { + // '0'-'9' + return ch & 0x0f; + } + if ((ch >= 0x41 && ch <= 0x46) || (ch >= 0x61 && ch <= 0x66)) { + // 'A'-'F', 'a'-'f' + return (ch & 0x0f) + 9; + } + return -1; + } + + Lexer.prototype = { + nextChar: function Lexer_nextChar() { + return (this.currentChar = this.stream.getByte()); + }, + peekChar: function Lexer_peekChar() { + return this.stream.peekByte(); + }, + getNumber: function Lexer_getNumber() { + var ch = this.currentChar; + var eNotation = false; + var divideBy = 0; // different from 0 if it's a floating point value + var sign = 1; + + if (ch === 0x2d) { + // '-' + sign = -1; + ch = this.nextChar(); + + if (ch === 0x2d) { + // '-' + // Ignore double negative (this is consistent with Adobe Reader). + ch = this.nextChar(); + } + } else if (ch === 0x2b) { + // '+' + ch = this.nextChar(); + } + if (ch === 0x2e) { + // '.' + divideBy = 10; + ch = this.nextChar(); + } + if (ch < 0x30 || ch > 0x39) { + // '0' - '9' + error("Invalid number: " + String.fromCharCode(ch)); + return 0; + } + + var baseValue = ch - 0x30; // '0' + var powerValue = 0; + var powerValueSign = 1; + + while ((ch = this.nextChar()) >= 0) { + if (0x30 <= ch && ch <= 0x39) { + // '0' - '9' + var currentDigit = ch - 0x30; // '0' + if (eNotation) { + // We are after an 'e' or 'E' + powerValue = powerValue * 10 + currentDigit; + } else { + if (divideBy !== 0) { + // We are after a point + divideBy *= 10; + } + baseValue = baseValue * 10 + currentDigit; + } + } else if (ch === 0x2e) { + // '.' + if (divideBy === 0) { + divideBy = 1; + } else { + // A number can have only one '.' + break; + } + } else if (ch === 0x2d) { + // '-' + // ignore minus signs in the middle of numbers to match + // Adobe's behavior + warn("Badly formated number"); + } else if (ch === 0x45 || ch === 0x65) { + // 'E', 'e' + // 'E' can be either a scientific notation or the beginning of a new + // operator + ch = this.peekChar(); + if (ch === 0x2b || ch === 0x2d) { + // '+', '-' + powerValueSign = ch === 0x2d ? -1 : 1; + this.nextChar(); // Consume the sign character + } else if (ch < 0x30 || ch > 0x39) { + // '0' - '9' + // The 'E' must be the beginning of a new operator + break; + } + eNotation = true; + } else { + // the last character doesn't belong to us + break; + } + } + + if (divideBy !== 0) { + baseValue /= divideBy; + } + if (eNotation) { + baseValue *= Math.pow(10, powerValueSign * powerValue); + } + return sign * baseValue; + }, + getString: function Lexer_getString() { + var numParen = 1; + var done = false; + var strBuf = this.strBuf; + strBuf.length = 0; + + var ch = this.nextChar(); + while (true) { + var charBuffered = false; + switch (ch | 0) { + case -1: + warn("Unterminated string"); + done = true; + break; + case 0x28: // '(' + ++numParen; + strBuf.push("("); + break; + case 0x29: // ')' + if (--numParen === 0) { + this.nextChar(); // consume strings ')' + done = true; + } else { + strBuf.push(")"); + } + break; + case 0x5c: // '\\' + ch = this.nextChar(); + switch (ch) { + case -1: + warn("Unterminated string"); + done = true; + break; + case 0x6e: // 'n' + strBuf.push("\n"); + break; + case 0x72: // 'r' + strBuf.push("\r"); + break; + case 0x74: // 't' + strBuf.push("\t"); + break; + case 0x62: // 'b' + strBuf.push("\b"); + break; + case 0x66: // 'f' + strBuf.push("\f"); + break; + case 0x5c: // '\' + case 0x28: // '(' + case 0x29: // ')' + strBuf.push(String.fromCharCode(ch)); + break; + case 0x30: + case 0x31: + case 0x32: + case 0x33: // '0'-'3' + case 0x34: + case 0x35: + case 0x36: + case 0x37: // '4'-'7' + var x = ch & 0x0f; + ch = this.nextChar(); + charBuffered = true; + if (ch >= 0x30 && ch <= 0x37) { + // '0'-'7' + x = (x << 3) + (ch & 0x0f); + ch = this.nextChar(); + if (ch >= 0x30 && ch <= 0x37) { + // '0'-'7' + charBuffered = false; + x = (x << 3) + (ch & 0x0f); + } + } + strBuf.push(String.fromCharCode(x)); + break; + case 0x0d: // CR + if (this.peekChar() === 0x0a) { + // LF + this.nextChar(); + } + break; + case 0x0a: // LF + break; + default: + strBuf.push(String.fromCharCode(ch)); + break; + } + break; + default: + strBuf.push(String.fromCharCode(ch)); + break; + } + if (done) { + break; + } + if (!charBuffered) { + ch = this.nextChar(); + } + } + return strBuf.join(""); + }, + getName: function Lexer_getName() { + var ch, previousCh; + var strBuf = this.strBuf; + strBuf.length = 0; + while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) { + if (ch === 0x23) { + // '#' + ch = this.nextChar(); + if (specialChars[ch]) { + warn( + "Lexer_getName: " + + "NUMBER SIGN (#) should be followed by a hexadecimal number." + ); + strBuf.push("#"); + break; + } + var x = toHexDigit(ch); + if (x !== -1) { + previousCh = ch; + ch = this.nextChar(); + var x2 = toHexDigit(ch); + if (x2 === -1) { + warn( + "Lexer_getName: Illegal digit (" + + String.fromCharCode(ch) + + ") in hexadecimal number." + ); + strBuf.push("#", String.fromCharCode(previousCh)); + if (specialChars[ch]) { + break; + } + strBuf.push(String.fromCharCode(ch)); + continue; + } + strBuf.push(String.fromCharCode((x << 4) | x2)); + } else { + strBuf.push("#", String.fromCharCode(ch)); + } + } else { + strBuf.push(String.fromCharCode(ch)); + } + } + if (strBuf.length > 127) { + warn( + "name token is longer than allowed by the spec: " + strBuf.length + ); + } + return Name.get(strBuf.join("")); + }, + getHexString: function Lexer_getHexString() { + var strBuf = this.strBuf; + strBuf.length = 0; + var ch = this.currentChar; + var isFirstHex = true; + var firstDigit; + var secondDigit; + while (true) { + if (ch < 0) { + warn("Unterminated hex string"); + break; + } else if (ch === 0x3e) { + // '>' + this.nextChar(); + break; + } else if (specialChars[ch] === 1) { + ch = this.nextChar(); + continue; + } else { + if (isFirstHex) { + firstDigit = toHexDigit(ch); + if (firstDigit === -1) { + warn('Ignoring invalid character "' + ch + '" in hex string'); + ch = this.nextChar(); + continue; + } + } else { + secondDigit = toHexDigit(ch); + if (secondDigit === -1) { + warn('Ignoring invalid character "' + ch + '" in hex string'); + ch = this.nextChar(); + continue; + } + strBuf.push(String.fromCharCode((firstDigit << 4) | secondDigit)); + } + isFirstHex = !isFirstHex; + ch = this.nextChar(); + } + } + return strBuf.join(""); + }, + getObj: function Lexer_getObj() { + // skip whitespace and comments + var comment = false; + var ch = this.currentChar; + while (true) { + if (ch < 0) { + return EOF; + } + if (comment) { + if (ch === 0x0a || ch === 0x0d) { + // LF, CR + comment = false; + } + } else if (ch === 0x25) { + // '%' + comment = true; + } else if (specialChars[ch] !== 1) { + break; + } + ch = this.nextChar(); + } + + // start reading token + switch (ch | 0) { + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: // '0'-'4' + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: // '5'-'9' + case 0x2b: + case 0x2d: + case 0x2e: // '+', '-', '.' + return this.getNumber(); + case 0x28: // '(' + return this.getString(); + case 0x2f: // '/' + return this.getName(); + // array punctuation + case 0x5b: // '[' + this.nextChar(); + return Cmd.get("["); + case 0x5d: // ']' + this.nextChar(); + return Cmd.get("]"); + // hex string or dict punctuation + case 0x3c: // '<' + ch = this.nextChar(); + if (ch === 0x3c) { + // dict punctuation + this.nextChar(); + return Cmd.get("<<"); + } + return this.getHexString(); + // dict punctuation + case 0x3e: // '>' + ch = this.nextChar(); + if (ch === 0x3e) { + this.nextChar(); + return Cmd.get(">>"); + } + return Cmd.get(">"); + case 0x7b: // '{' + this.nextChar(); + return Cmd.get("{"); + case 0x7d: // '}' + this.nextChar(); + return Cmd.get("}"); + case 0x29: // ')' + error("Illegal character: " + ch); + break; + } + + // command + var str = String.fromCharCode(ch); + var knownCommands = this.knownCommands; + var knownCommandFound = + knownCommands && knownCommands[str] !== undefined; + while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) { + // stop if known command is found and next character does not make + // the str a command + var possibleCommand = str + String.fromCharCode(ch); + if ( + knownCommandFound && + knownCommands[possibleCommand] === undefined + ) { + break; + } + if (str.length === 128) { + error("Command token too long: " + str.length); + } + str = possibleCommand; + knownCommandFound = knownCommands && knownCommands[str] !== undefined; + } + if (str === "true") { + return true; + } + if (str === "false") { + return false; + } + if (str === "null") { + return null; + } + return Cmd.get(str); + }, + skipToNextLine: function Lexer_skipToNextLine() { + var ch = this.currentChar; + while (ch >= 0) { + if (ch === 0x0d) { + // CR + ch = this.nextChar(); + if (ch === 0x0a) { + // LF + this.nextChar(); + } + break; + } else if (ch === 0x0a) { + // LF + this.nextChar(); + break; + } + ch = this.nextChar(); + } + } + }; + + return Lexer; + })(); + + var Linearization = { + create: function LinearizationCreate(stream) { + function getInt(name, allowZeroValue) { + var obj = linDict.get(name); + if (isInt(obj) && (allowZeroValue ? obj >= 0 : obj > 0)) { + return obj; + } + throw new Error( + 'The "' + + name + + '" parameter in the linearization ' + + "dictionary is invalid." + ); + } + function getHints() { + var hints = linDict.get("H"), + hintsLength, + item; + if ( + isArray(hints) && + ((hintsLength = hints.length) === 2 || hintsLength === 4) + ) { + for (var index = 0; index < hintsLength; index++) { + if (!(isInt((item = hints[index])) && item > 0)) { + throw new Error( + "Hint (" + + index + + ") in the linearization dictionary is invalid." + ); + } + } + return hints; + } + throw new Error( + "Hint array in the linearization dictionary is invalid." + ); + } + var parser = new Parser(new Lexer(stream), false, null); + var obj1 = parser.getObj(); + var obj2 = parser.getObj(); + var obj3 = parser.getObj(); + var linDict = parser.getObj(); + var obj, length; + if ( + !( + isInt(obj1) && + isInt(obj2) && + isCmd(obj3, "obj") && + isDict(linDict) && + isNum((obj = linDict.get("Linearized"))) && + obj > 0 + ) + ) { + return null; // No valid linearization dictionary found. + } else if ((length = getInt("L")) !== stream.length) { + throw new Error( + 'The "L" parameter in the linearization dictionary ' + + "does not equal the stream length." + ); + } + return { + length: length, + hints: getHints(), + objectNumberFirst: getInt("O"), + endFirst: getInt("E"), + numPages: getInt("N"), + mainXRefEntriesOffset: getInt("T"), + pageFirst: linDict.has("P") ? getInt("P", true) : 0 + }; + } + }; + + exports.EOF = EOF; + exports.Lexer = Lexer; + exports.Linearization = Linearization; + exports.Parser = Parser; + exports.isEOF = isEOF; + + // TODO refactor to remove dependency on stream.js + coreStream._setCoreParser(exports); +}); + +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +("use strict"); + +(function(root, factory) { + //if (typeof define === 'function' && define.amd) { + // define('pdfjs/core/crypto', ['exports', 'pdfjs/shared/util', + // 'pdfjs/core/primitives', 'pdfjs/core/stream'], factory); + // } else if (typeof exports !== 'undefined') { + // factory(exports, require('../shared/util.js'), require('./primitives.js'), + // require('./stream.js')); + //} else { + factory( + (root.pdfjsCoreCrypto = {}), + root.pdfjsSharedUtil, + root.pdfjsCorePrimitives, + root.pdfjsCoreStream + ); + //} +})(window, function(exports, sharedUtil, corePrimitives, coreStream) { + var PasswordException = sharedUtil.PasswordException; + var PasswordResponses = sharedUtil.PasswordResponses; + var bytesToString = sharedUtil.bytesToString; + var error = sharedUtil.error; + var isInt = sharedUtil.isInt; + var stringToBytes = sharedUtil.stringToBytes; + var utf8StringToString = sharedUtil.utf8StringToString; + var warn = sharedUtil.warn; + var Name = corePrimitives.Name; + var isName = corePrimitives.isName; + var DecryptStream = coreStream.DecryptStream; + + var ARCFourCipher = (function ARCFourCipherClosure() { + function ARCFourCipher(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; + } + + ARCFourCipher.prototype = { + encryptBlock: function ARCFourCipher_encryptBlock(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) { + 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; + } + }; + ARCFourCipher.prototype.decryptBlock = ARCFourCipher.prototype.encryptBlock; + + return ARCFourCipher; + })(); + + var calculateMD5 = (function calculateMD5Closure() { + var 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 + ]); + + var 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; + while (i < n) { + 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; + 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 Word64 = (function Word64Closure() { + function Word64(highInteger, lowInteger) { + this.high = highInteger | 0; + this.low = lowInteger | 0; + } + Word64.prototype = { + and: function Word64_and(word) { + this.high &= word.high; + this.low &= word.low; + }, + xor: function Word64_xor(word) { + this.high ^= word.high; + this.low ^= word.low; + }, + + or: function Word64_or(word) { + this.high |= word.high; + this.low |= word.low; + }, + + shiftRight: function Word64_shiftRight(places) { + if (places >= 32) { + this.low = (this.high >>> (places - 32)) | 0; + this.high = 0; + } else { + this.low = (this.low >>> places) | (this.high << (32 - places)); + this.high = (this.high >>> places) | 0; + } + }, + + shiftLeft: function Word64_shiftLeft(places) { + if (places >= 32) { + this.high = this.low << (places - 32); + this.low = 0; + } else { + this.high = (this.high << places) | (this.low >>> (32 - places)); + this.low = this.low << places; + } + }, + + rotateRight: function Word64_rotateRight(places) { + var low, high; + if (places & 32) { + high = this.low; + low = this.high; + } else { + low = this.low; + high = this.high; + } + places &= 31; + this.low = (low >>> places) | (high << (32 - places)); + this.high = (high >>> places) | (low << (32 - places)); + }, + + not: function Word64_not() { + this.high = ~this.high; + this.low = ~this.low; + }, + + add: function Word64_add(word) { + var lowAdd = (this.low >>> 0) + (word.low >>> 0); + var highAdd = (this.high >>> 0) + (word.high >>> 0); + if (lowAdd > 0xffffffff) { + highAdd += 1; + } + this.low = lowAdd | 0; + this.high = highAdd | 0; + }, + + copyTo: function Word64_copyTo(bytes, offset) { + bytes[offset] = (this.high >>> 24) & 0xff; + bytes[offset + 1] = (this.high >> 16) & 0xff; + bytes[offset + 2] = (this.high >> 8) & 0xff; + bytes[offset + 3] = this.high & 0xff; + bytes[offset + 4] = (this.low >>> 24) & 0xff; + bytes[offset + 5] = (this.low >> 16) & 0xff; + bytes[offset + 6] = (this.low >> 8) & 0xff; + bytes[offset + 7] = this.low & 0xff; + }, + + assign: function Word64_assign(word) { + this.high = word.high; + this.low = word.low; + } + }; + return Word64; + })(); + + var calculateSHA256 = (function calculateSHA256Closure() { + function rotr(x, n) { + return (x >>> n) | (x << (32 - n)); + } + + function ch(x, y, z) { + return (x & y) ^ (~x & z); + } + + function maj(x, y, z) { + return (x & y) ^ (x & z) ^ (y & z); + } + + function sigma(x) { + return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22); + } + + function sigmaPrime(x) { + return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25); + } + + function littleSigma(x) { + return rotr(x, 7) ^ rotr(x, 18) ^ (x >>> 3); + } + + function littleSigmaPrime(x) { + return rotr(x, 17) ^ rotr(x, 19) ^ (x >>> 10); + } + + var k = [ + 0x428a2f98, + 0x71374491, + 0xb5c0fbcf, + 0xe9b5dba5, + 0x3956c25b, + 0x59f111f1, + 0x923f82a4, + 0xab1c5ed5, + 0xd807aa98, + 0x12835b01, + 0x243185be, + 0x550c7dc3, + 0x72be5d74, + 0x80deb1fe, + 0x9bdc06a7, + 0xc19bf174, + 0xe49b69c1, + 0xefbe4786, + 0x0fc19dc6, + 0x240ca1cc, + 0x2de92c6f, + 0x4a7484aa, + 0x5cb0a9dc, + 0x76f988da, + 0x983e5152, + 0xa831c66d, + 0xb00327c8, + 0xbf597fc7, + 0xc6e00bf3, + 0xd5a79147, + 0x06ca6351, + 0x14292967, + 0x27b70a85, + 0x2e1b2138, + 0x4d2c6dfc, + 0x53380d13, + 0x650a7354, + 0x766a0abb, + 0x81c2c92e, + 0x92722c85, + 0xa2bfe8a1, + 0xa81a664b, + 0xc24b8b70, + 0xc76c51a3, + 0xd192e819, + 0xd6990624, + 0xf40e3585, + 0x106aa070, + 0x19a4c116, + 0x1e376c08, + 0x2748774c, + 0x34b0bcb5, + 0x391c0cb3, + 0x4ed8aa4a, + 0x5b9cca4f, + 0x682e6ff3, + 0x748f82ee, + 0x78a5636f, + 0x84c87814, + 0x8cc70208, + 0x90befffa, + 0xa4506ceb, + 0xbef9a3f7, + 0xc67178f2 + ]; + + function hash(data, offset, length) { + // initial hash values + var h0 = 0x6a09e667, + h1 = 0xbb67ae85, + h2 = 0x3c6ef372, + h3 = 0xa54ff53a, + h4 = 0x510e527f, + h5 = 0x9b05688c, + h6 = 0x1f83d9ab, + h7 = 0x5be0cd19; + // pre-processing + var paddedLength = Math.ceil((length + 9) / 64) * 64; + 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; + while (i < n) { + padded[i++] = 0; + } + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = (length >>> 29) & 0xff; + padded[i++] = (length >> 21) & 0xff; + padded[i++] = (length >> 13) & 0xff; + padded[i++] = (length >> 5) & 0xff; + padded[i++] = (length << 3) & 0xff; + var w = new Uint32Array(64); + // for each 512 bit block + for (i = 0; i < paddedLength; ) { + for (j = 0; j < 16; ++j) { + w[j] = + (padded[i] << 24) | + (padded[i + 1] << 16) | + (padded[i + 2] << 8) | + padded[i + 3]; + i += 4; + } + + for (j = 16; j < 64; ++j) { + w[j] = + (littleSigmaPrime(w[j - 2]) + + w[j - 7] + + littleSigma(w[j - 15]) + + w[j - 16]) | + 0; + } + var a = h0, + b = h1, + c = h2, + d = h3, + e = h4, + f = h5, + g = h6, + h = h7, + t1, + t2; + for (j = 0; j < 64; ++j) { + t1 = h + sigmaPrime(e) + ch(e, f, g) + k[j] + w[j]; + t2 = sigma(a) + maj(a, b, c); + h = g; + g = f; + f = e; + e = (d + t1) | 0; + d = c; + c = b; + b = a; + a = (t1 + t2) | 0; + } + h0 = (h0 + a) | 0; + h1 = (h1 + b) | 0; + h2 = (h2 + c) | 0; + h3 = (h3 + d) | 0; + h4 = (h4 + e) | 0; + h5 = (h5 + f) | 0; + h6 = (h6 + g) | 0; + h7 = (h7 + h) | 0; + } + return new Uint8Array([ + (h0 >> 24) & 0xff, + (h0 >> 16) & 0xff, + (h0 >> 8) & 0xff, + h0 & 0xff, + (h1 >> 24) & 0xff, + (h1 >> 16) & 0xff, + (h1 >> 8) & 0xff, + h1 & 0xff, + (h2 >> 24) & 0xff, + (h2 >> 16) & 0xff, + (h2 >> 8) & 0xff, + h2 & 0xff, + (h3 >> 24) & 0xff, + (h3 >> 16) & 0xff, + (h3 >> 8) & 0xff, + h3 & 0xff, + (h4 >> 24) & 0xff, + (h4 >> 16) & 0xff, + (h4 >> 8) & 0xff, + h4 & 0xff, + (h5 >> 24) & 0xff, + (h5 >> 16) & 0xff, + (h5 >> 8) & 0xff, + h5 & 0xff, + (h6 >> 24) & 0xff, + (h6 >> 16) & 0xff, + (h6 >> 8) & 0xff, + h6 & 0xff, + (h7 >> 24) & 0xff, + (h7 >> 16) & 0xff, + (h7 >> 8) & 0xff, + h7 & 0xff + ]); + } + + return hash; + })(); + + var calculateSHA512 = (function calculateSHA512Closure() { + function ch(result, x, y, z, tmp) { + result.assign(x); + result.and(y); + tmp.assign(x); + tmp.not(); + tmp.and(z); + result.xor(tmp); + } + + function maj(result, x, y, z, tmp) { + result.assign(x); + result.and(y); + tmp.assign(x); + tmp.and(z); + result.xor(tmp); + tmp.assign(y); + tmp.and(z); + result.xor(tmp); + } + + function sigma(result, x, tmp) { + result.assign(x); + result.rotateRight(28); + tmp.assign(x); + tmp.rotateRight(34); + result.xor(tmp); + tmp.assign(x); + tmp.rotateRight(39); + result.xor(tmp); + } + + function sigmaPrime(result, x, tmp) { + result.assign(x); + result.rotateRight(14); + tmp.assign(x); + tmp.rotateRight(18); + result.xor(tmp); + tmp.assign(x); + tmp.rotateRight(41); + result.xor(tmp); + } + + function littleSigma(result, x, tmp) { + result.assign(x); + result.rotateRight(1); + tmp.assign(x); + tmp.rotateRight(8); + result.xor(tmp); + tmp.assign(x); + tmp.shiftRight(7); + result.xor(tmp); + } + + function littleSigmaPrime(result, x, tmp) { + result.assign(x); + result.rotateRight(19); + tmp.assign(x); + tmp.rotateRight(61); + result.xor(tmp); + tmp.assign(x); + tmp.shiftRight(6); + result.xor(tmp); + } + + var k = [ + new Word64(0x428a2f98, 0xd728ae22), + new Word64(0x71374491, 0x23ef65cd), + new Word64(0xb5c0fbcf, 0xec4d3b2f), + new Word64(0xe9b5dba5, 0x8189dbbc), + new Word64(0x3956c25b, 0xf348b538), + new Word64(0x59f111f1, 0xb605d019), + new Word64(0x923f82a4, 0xaf194f9b), + new Word64(0xab1c5ed5, 0xda6d8118), + new Word64(0xd807aa98, 0xa3030242), + new Word64(0x12835b01, 0x45706fbe), + new Word64(0x243185be, 0x4ee4b28c), + new Word64(0x550c7dc3, 0xd5ffb4e2), + new Word64(0x72be5d74, 0xf27b896f), + new Word64(0x80deb1fe, 0x3b1696b1), + new Word64(0x9bdc06a7, 0x25c71235), + new Word64(0xc19bf174, 0xcf692694), + new Word64(0xe49b69c1, 0x9ef14ad2), + new Word64(0xefbe4786, 0x384f25e3), + new Word64(0x0fc19dc6, 0x8b8cd5b5), + new Word64(0x240ca1cc, 0x77ac9c65), + new Word64(0x2de92c6f, 0x592b0275), + new Word64(0x4a7484aa, 0x6ea6e483), + new Word64(0x5cb0a9dc, 0xbd41fbd4), + new Word64(0x76f988da, 0x831153b5), + new Word64(0x983e5152, 0xee66dfab), + new Word64(0xa831c66d, 0x2db43210), + new Word64(0xb00327c8, 0x98fb213f), + new Word64(0xbf597fc7, 0xbeef0ee4), + new Word64(0xc6e00bf3, 0x3da88fc2), + new Word64(0xd5a79147, 0x930aa725), + new Word64(0x06ca6351, 0xe003826f), + new Word64(0x14292967, 0x0a0e6e70), + new Word64(0x27b70a85, 0x46d22ffc), + new Word64(0x2e1b2138, 0x5c26c926), + new Word64(0x4d2c6dfc, 0x5ac42aed), + new Word64(0x53380d13, 0x9d95b3df), + new Word64(0x650a7354, 0x8baf63de), + new Word64(0x766a0abb, 0x3c77b2a8), + new Word64(0x81c2c92e, 0x47edaee6), + new Word64(0x92722c85, 0x1482353b), + new Word64(0xa2bfe8a1, 0x4cf10364), + new Word64(0xa81a664b, 0xbc423001), + new Word64(0xc24b8b70, 0xd0f89791), + new Word64(0xc76c51a3, 0x0654be30), + new Word64(0xd192e819, 0xd6ef5218), + new Word64(0xd6990624, 0x5565a910), + new Word64(0xf40e3585, 0x5771202a), + new Word64(0x106aa070, 0x32bbd1b8), + new Word64(0x19a4c116, 0xb8d2d0c8), + new Word64(0x1e376c08, 0x5141ab53), + new Word64(0x2748774c, 0xdf8eeb99), + new Word64(0x34b0bcb5, 0xe19b48a8), + new Word64(0x391c0cb3, 0xc5c95a63), + new Word64(0x4ed8aa4a, 0xe3418acb), + new Word64(0x5b9cca4f, 0x7763e373), + new Word64(0x682e6ff3, 0xd6b2b8a3), + new Word64(0x748f82ee, 0x5defb2fc), + new Word64(0x78a5636f, 0x43172f60), + new Word64(0x84c87814, 0xa1f0ab72), + new Word64(0x8cc70208, 0x1a6439ec), + new Word64(0x90befffa, 0x23631e28), + new Word64(0xa4506ceb, 0xde82bde9), + new Word64(0xbef9a3f7, 0xb2c67915), + new Word64(0xc67178f2, 0xe372532b), + new Word64(0xca273ece, 0xea26619c), + new Word64(0xd186b8c7, 0x21c0c207), + new Word64(0xeada7dd6, 0xcde0eb1e), + new Word64(0xf57d4f7f, 0xee6ed178), + new Word64(0x06f067aa, 0x72176fba), + new Word64(0x0a637dc5, 0xa2c898a6), + new Word64(0x113f9804, 0xbef90dae), + new Word64(0x1b710b35, 0x131c471b), + new Word64(0x28db77f5, 0x23047d84), + new Word64(0x32caab7b, 0x40c72493), + new Word64(0x3c9ebe0a, 0x15c9bebc), + new Word64(0x431d67c4, 0x9c100d4c), + new Word64(0x4cc5d4be, 0xcb3e42b6), + new Word64(0x597f299c, 0xfc657e2a), + new Word64(0x5fcb6fab, 0x3ad6faec), + new Word64(0x6c44198c, 0x4a475817) + ]; + + function hash(data, offset, length, mode384) { + mode384 = !!mode384; + // initial hash values + var h0, h1, h2, h3, h4, h5, h6, h7; + if (!mode384) { + h0 = new Word64(0x6a09e667, 0xf3bcc908); + h1 = new Word64(0xbb67ae85, 0x84caa73b); + h2 = new Word64(0x3c6ef372, 0xfe94f82b); + h3 = new Word64(0xa54ff53a, 0x5f1d36f1); + h4 = new Word64(0x510e527f, 0xade682d1); + h5 = new Word64(0x9b05688c, 0x2b3e6c1f); + h6 = new Word64(0x1f83d9ab, 0xfb41bd6b); + h7 = new Word64(0x5be0cd19, 0x137e2179); + } else { + // SHA384 is exactly the same + // except with different starting values and a trimmed result + h0 = new Word64(0xcbbb9d5d, 0xc1059ed8); + h1 = new Word64(0x629a292a, 0x367cd507); + h2 = new Word64(0x9159015a, 0x3070dd17); + h3 = new Word64(0x152fecd8, 0xf70e5939); + h4 = new Word64(0x67332667, 0xffc00b31); + h5 = new Word64(0x8eb44a87, 0x68581511); + h6 = new Word64(0xdb0c2e0d, 0x64f98fa7); + h7 = new Word64(0x47b5481d, 0xbefa4fa4); + } + + // pre-processing + var paddedLength = Math.ceil((length + 17) / 128) * 128; + var padded = new Uint8Array(paddedLength); + var i, j, n; + for (i = 0; i < length; ++i) { + padded[i] = data[offset++]; + } + padded[i++] = 0x80; + n = paddedLength - 16; + while (i < n) { + padded[i++] = 0; + } + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = 0; + padded[i++] = (length >>> 29) & 0xff; + padded[i++] = (length >> 21) & 0xff; + padded[i++] = (length >> 13) & 0xff; + padded[i++] = (length >> 5) & 0xff; + padded[i++] = (length << 3) & 0xff; + + var w = new Array(80); + for (i = 0; i < 80; i++) { + w[i] = new Word64(0, 0); + } + var a = new Word64(0, 0), + b = new Word64(0, 0), + c = new Word64(0, 0); + var d = new Word64(0, 0), + e = new Word64(0, 0), + f = new Word64(0, 0); + var g = new Word64(0, 0), + h = new Word64(0, 0); + var t1 = new Word64(0, 0), + t2 = new Word64(0, 0); + var tmp1 = new Word64(0, 0), + tmp2 = new Word64(0, 0), + tmp3; + + // for each 1024 bit block + for (i = 0; i < paddedLength; ) { + for (j = 0; j < 16; ++j) { + w[j].high = + (padded[i] << 24) | + (padded[i + 1] << 16) | + (padded[i + 2] << 8) | + padded[i + 3]; + w[j].low = + (padded[i + 4] << 24) | + (padded[i + 5] << 16) | + (padded[i + 6] << 8) | + padded[i + 7]; + i += 8; + } + for (j = 16; j < 80; ++j) { + tmp3 = w[j]; + littleSigmaPrime(tmp3, w[j - 2], tmp2); + tmp3.add(w[j - 7]); + littleSigma(tmp1, w[j - 15], tmp2); + tmp3.add(tmp1); + tmp3.add(w[j - 16]); + } + + a.assign(h0); + b.assign(h1); + c.assign(h2); + d.assign(h3); + e.assign(h4); + f.assign(h5); + g.assign(h6); + h.assign(h7); + for (j = 0; j < 80; ++j) { + t1.assign(h); + sigmaPrime(tmp1, e, tmp2); + t1.add(tmp1); + ch(tmp1, e, f, g, tmp2); + t1.add(tmp1); + t1.add(k[j]); + t1.add(w[j]); + + sigma(t2, a, tmp2); + maj(tmp1, a, b, c, tmp2); + t2.add(tmp1); + + tmp3 = h; + h = g; + g = f; + f = e; + d.add(t1); + e = d; + d = c; + c = b; + b = a; + tmp3.assign(t1); + tmp3.add(t2); + a = tmp3; + } + h0.add(a); + h1.add(b); + h2.add(c); + h3.add(d); + h4.add(e); + h5.add(f); + h6.add(g); + h7.add(h); + } + + var result; + if (!mode384) { + result = new Uint8Array(64); + h0.copyTo(result, 0); + h1.copyTo(result, 8); + h2.copyTo(result, 16); + h3.copyTo(result, 24); + h4.copyTo(result, 32); + h5.copyTo(result, 40); + h6.copyTo(result, 48); + h7.copyTo(result, 56); + } else { + result = new Uint8Array(48); + h0.copyTo(result, 0); + h1.copyTo(result, 8); + h2.copyTo(result, 16); + h3.copyTo(result, 24); + h4.copyTo(result, 32); + h5.copyTo(result, 40); + } + return result; + } + + return hash; + })(); + var calculateSHA384 = (function calculateSHA384Closure() { + function hash(data, offset, length) { + return calculateSHA512(data, offset, length, true); + } + + return hash; + })(); + var NullCipher = (function NullCipherClosure() { + function NullCipher() {} + + NullCipher.prototype = { + decryptBlock: function NullCipher_decryptBlock(data) { + return data; + } + }; + + return NullCipher; + })(); + + var AES128Cipher = (function AES128CipherClosure() { + var rcon = new Uint8Array([ + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d + ]); + + var s = new Uint8Array([ + 0x63, + 0x7c, + 0x77, + 0x7b, + 0xf2, + 0x6b, + 0x6f, + 0xc5, + 0x30, + 0x01, + 0x67, + 0x2b, + 0xfe, + 0xd7, + 0xab, + 0x76, + 0xca, + 0x82, + 0xc9, + 0x7d, + 0xfa, + 0x59, + 0x47, + 0xf0, + 0xad, + 0xd4, + 0xa2, + 0xaf, + 0x9c, + 0xa4, + 0x72, + 0xc0, + 0xb7, + 0xfd, + 0x93, + 0x26, + 0x36, + 0x3f, + 0xf7, + 0xcc, + 0x34, + 0xa5, + 0xe5, + 0xf1, + 0x71, + 0xd8, + 0x31, + 0x15, + 0x04, + 0xc7, + 0x23, + 0xc3, + 0x18, + 0x96, + 0x05, + 0x9a, + 0x07, + 0x12, + 0x80, + 0xe2, + 0xeb, + 0x27, + 0xb2, + 0x75, + 0x09, + 0x83, + 0x2c, + 0x1a, + 0x1b, + 0x6e, + 0x5a, + 0xa0, + 0x52, + 0x3b, + 0xd6, + 0xb3, + 0x29, + 0xe3, + 0x2f, + 0x84, + 0x53, + 0xd1, + 0x00, + 0xed, + 0x20, + 0xfc, + 0xb1, + 0x5b, + 0x6a, + 0xcb, + 0xbe, + 0x39, + 0x4a, + 0x4c, + 0x58, + 0xcf, + 0xd0, + 0xef, + 0xaa, + 0xfb, + 0x43, + 0x4d, + 0x33, + 0x85, + 0x45, + 0xf9, + 0x02, + 0x7f, + 0x50, + 0x3c, + 0x9f, + 0xa8, + 0x51, + 0xa3, + 0x40, + 0x8f, + 0x92, + 0x9d, + 0x38, + 0xf5, + 0xbc, + 0xb6, + 0xda, + 0x21, + 0x10, + 0xff, + 0xf3, + 0xd2, + 0xcd, + 0x0c, + 0x13, + 0xec, + 0x5f, + 0x97, + 0x44, + 0x17, + 0xc4, + 0xa7, + 0x7e, + 0x3d, + 0x64, + 0x5d, + 0x19, + 0x73, + 0x60, + 0x81, + 0x4f, + 0xdc, + 0x22, + 0x2a, + 0x90, + 0x88, + 0x46, + 0xee, + 0xb8, + 0x14, + 0xde, + 0x5e, + 0x0b, + 0xdb, + 0xe0, + 0x32, + 0x3a, + 0x0a, + 0x49, + 0x06, + 0x24, + 0x5c, + 0xc2, + 0xd3, + 0xac, + 0x62, + 0x91, + 0x95, + 0xe4, + 0x79, + 0xe7, + 0xc8, + 0x37, + 0x6d, + 0x8d, + 0xd5, + 0x4e, + 0xa9, + 0x6c, + 0x56, + 0xf4, + 0xea, + 0x65, + 0x7a, + 0xae, + 0x08, + 0xba, + 0x78, + 0x25, + 0x2e, + 0x1c, + 0xa6, + 0xb4, + 0xc6, + 0xe8, + 0xdd, + 0x74, + 0x1f, + 0x4b, + 0xbd, + 0x8b, + 0x8a, + 0x70, + 0x3e, + 0xb5, + 0x66, + 0x48, + 0x03, + 0xf6, + 0x0e, + 0x61, + 0x35, + 0x57, + 0xb9, + 0x86, + 0xc1, + 0x1d, + 0x9e, + 0xe1, + 0xf8, + 0x98, + 0x11, + 0x69, + 0xd9, + 0x8e, + 0x94, + 0x9b, + 0x1e, + 0x87, + 0xe9, + 0xce, + 0x55, + 0x28, + 0xdf, + 0x8c, + 0xa1, + 0x89, + 0x0d, + 0xbf, + 0xe6, + 0x42, + 0x68, + 0x41, + 0x99, + 0x2d, + 0x0f, + 0xb0, + 0x54, + 0xbb, + 0x16 + ]); + + var inv_s = new Uint8Array([ + 0x52, + 0x09, + 0x6a, + 0xd5, + 0x30, + 0x36, + 0xa5, + 0x38, + 0xbf, + 0x40, + 0xa3, + 0x9e, + 0x81, + 0xf3, + 0xd7, + 0xfb, + 0x7c, + 0xe3, + 0x39, + 0x82, + 0x9b, + 0x2f, + 0xff, + 0x87, + 0x34, + 0x8e, + 0x43, + 0x44, + 0xc4, + 0xde, + 0xe9, + 0xcb, + 0x54, + 0x7b, + 0x94, + 0x32, + 0xa6, + 0xc2, + 0x23, + 0x3d, + 0xee, + 0x4c, + 0x95, + 0x0b, + 0x42, + 0xfa, + 0xc3, + 0x4e, + 0x08, + 0x2e, + 0xa1, + 0x66, + 0x28, + 0xd9, + 0x24, + 0xb2, + 0x76, + 0x5b, + 0xa2, + 0x49, + 0x6d, + 0x8b, + 0xd1, + 0x25, + 0x72, + 0xf8, + 0xf6, + 0x64, + 0x86, + 0x68, + 0x98, + 0x16, + 0xd4, + 0xa4, + 0x5c, + 0xcc, + 0x5d, + 0x65, + 0xb6, + 0x92, + 0x6c, + 0x70, + 0x48, + 0x50, + 0xfd, + 0xed, + 0xb9, + 0xda, + 0x5e, + 0x15, + 0x46, + 0x57, + 0xa7, + 0x8d, + 0x9d, + 0x84, + 0x90, + 0xd8, + 0xab, + 0x00, + 0x8c, + 0xbc, + 0xd3, + 0x0a, + 0xf7, + 0xe4, + 0x58, + 0x05, + 0xb8, + 0xb3, + 0x45, + 0x06, + 0xd0, + 0x2c, + 0x1e, + 0x8f, + 0xca, + 0x3f, + 0x0f, + 0x02, + 0xc1, + 0xaf, + 0xbd, + 0x03, + 0x01, + 0x13, + 0x8a, + 0x6b, + 0x3a, + 0x91, + 0x11, + 0x41, + 0x4f, + 0x67, + 0xdc, + 0xea, + 0x97, + 0xf2, + 0xcf, + 0xce, + 0xf0, + 0xb4, + 0xe6, + 0x73, + 0x96, + 0xac, + 0x74, + 0x22, + 0xe7, + 0xad, + 0x35, + 0x85, + 0xe2, + 0xf9, + 0x37, + 0xe8, + 0x1c, + 0x75, + 0xdf, + 0x6e, + 0x47, + 0xf1, + 0x1a, + 0x71, + 0x1d, + 0x29, + 0xc5, + 0x89, + 0x6f, + 0xb7, + 0x62, + 0x0e, + 0xaa, + 0x18, + 0xbe, + 0x1b, + 0xfc, + 0x56, + 0x3e, + 0x4b, + 0xc6, + 0xd2, + 0x79, + 0x20, + 0x9a, + 0xdb, + 0xc0, + 0xfe, + 0x78, + 0xcd, + 0x5a, + 0xf4, + 0x1f, + 0xdd, + 0xa8, + 0x33, + 0x88, + 0x07, + 0xc7, + 0x31, + 0xb1, + 0x12, + 0x10, + 0x59, + 0x27, + 0x80, + 0xec, + 0x5f, + 0x60, + 0x51, + 0x7f, + 0xa9, + 0x19, + 0xb5, + 0x4a, + 0x0d, + 0x2d, + 0xe5, + 0x7a, + 0x9f, + 0x93, + 0xc9, + 0x9c, + 0xef, + 0xa0, + 0xe0, + 0x3b, + 0x4d, + 0xae, + 0x2a, + 0xf5, + 0xb0, + 0xc8, + 0xeb, + 0xbb, + 0x3c, + 0x83, + 0x53, + 0x99, + 0x61, + 0x17, + 0x2b, + 0x04, + 0x7e, + 0xba, + 0x77, + 0xd6, + 0x26, + 0xe1, + 0x69, + 0x14, + 0x63, + 0x55, + 0x21, + 0x0c, + 0x7d + ]); + var mixCol = new Uint8Array(256); + for (var i = 0; i < 256; i++) { + if (i < 128) { + mixCol[i] = i << 1; + } else { + mixCol[i] = (i << 1) ^ 0x1b; + } + } + var mix = new Uint32Array([ + 0x00000000, + 0x0e090d0b, + 0x1c121a16, + 0x121b171d, + 0x3824342c, + 0x362d3927, + 0x24362e3a, + 0x2a3f2331, + 0x70486858, + 0x7e416553, + 0x6c5a724e, + 0x62537f45, + 0x486c5c74, + 0x4665517f, + 0x547e4662, + 0x5a774b69, + 0xe090d0b0, + 0xee99ddbb, + 0xfc82caa6, + 0xf28bc7ad, + 0xd8b4e49c, + 0xd6bde997, + 0xc4a6fe8a, + 0xcaaff381, + 0x90d8b8e8, + 0x9ed1b5e3, + 0x8ccaa2fe, + 0x82c3aff5, + 0xa8fc8cc4, + 0xa6f581cf, + 0xb4ee96d2, + 0xbae79bd9, + 0xdb3bbb7b, + 0xd532b670, + 0xc729a16d, + 0xc920ac66, + 0xe31f8f57, + 0xed16825c, + 0xff0d9541, + 0xf104984a, + 0xab73d323, + 0xa57ade28, + 0xb761c935, + 0xb968c43e, + 0x9357e70f, + 0x9d5eea04, + 0x8f45fd19, + 0x814cf012, + 0x3bab6bcb, + 0x35a266c0, + 0x27b971dd, + 0x29b07cd6, + 0x038f5fe7, + 0x0d8652ec, + 0x1f9d45f1, + 0x119448fa, + 0x4be30393, + 0x45ea0e98, + 0x57f11985, + 0x59f8148e, + 0x73c737bf, + 0x7dce3ab4, + 0x6fd52da9, + 0x61dc20a2, + 0xad766df6, + 0xa37f60fd, + 0xb16477e0, + 0xbf6d7aeb, + 0x955259da, + 0x9b5b54d1, + 0x894043cc, + 0x87494ec7, + 0xdd3e05ae, + 0xd33708a5, + 0xc12c1fb8, + 0xcf2512b3, + 0xe51a3182, + 0xeb133c89, + 0xf9082b94, + 0xf701269f, + 0x4de6bd46, + 0x43efb04d, + 0x51f4a750, + 0x5ffdaa5b, + 0x75c2896a, + 0x7bcb8461, + 0x69d0937c, + 0x67d99e77, + 0x3daed51e, + 0x33a7d815, + 0x21bccf08, + 0x2fb5c203, + 0x058ae132, + 0x0b83ec39, + 0x1998fb24, + 0x1791f62f, + 0x764dd68d, + 0x7844db86, + 0x6a5fcc9b, + 0x6456c190, + 0x4e69e2a1, + 0x4060efaa, + 0x527bf8b7, + 0x5c72f5bc, + 0x0605bed5, + 0x080cb3de, + 0x1a17a4c3, + 0x141ea9c8, + 0x3e218af9, + 0x302887f2, + 0x223390ef, + 0x2c3a9de4, + 0x96dd063d, + 0x98d40b36, + 0x8acf1c2b, + 0x84c61120, + 0xaef93211, + 0xa0f03f1a, + 0xb2eb2807, + 0xbce2250c, + 0xe6956e65, + 0xe89c636e, + 0xfa877473, + 0xf48e7978, + 0xdeb15a49, + 0xd0b85742, + 0xc2a3405f, + 0xccaa4d54, + 0x41ecdaf7, + 0x4fe5d7fc, + 0x5dfec0e1, + 0x53f7cdea, + 0x79c8eedb, + 0x77c1e3d0, + 0x65daf4cd, + 0x6bd3f9c6, + 0x31a4b2af, + 0x3fadbfa4, + 0x2db6a8b9, + 0x23bfa5b2, + 0x09808683, + 0x07898b88, + 0x15929c95, + 0x1b9b919e, + 0xa17c0a47, + 0xaf75074c, + 0xbd6e1051, + 0xb3671d5a, + 0x99583e6b, + 0x97513360, + 0x854a247d, + 0x8b432976, + 0xd134621f, + 0xdf3d6f14, + 0xcd267809, + 0xc32f7502, + 0xe9105633, + 0xe7195b38, + 0xf5024c25, + 0xfb0b412e, + 0x9ad7618c, + 0x94de6c87, + 0x86c57b9a, + 0x88cc7691, + 0xa2f355a0, + 0xacfa58ab, + 0xbee14fb6, + 0xb0e842bd, + 0xea9f09d4, + 0xe49604df, + 0xf68d13c2, + 0xf8841ec9, + 0xd2bb3df8, + 0xdcb230f3, + 0xcea927ee, + 0xc0a02ae5, + 0x7a47b13c, + 0x744ebc37, + 0x6655ab2a, + 0x685ca621, + 0x42638510, + 0x4c6a881b, + 0x5e719f06, + 0x5078920d, + 0x0a0fd964, + 0x0406d46f, + 0x161dc372, + 0x1814ce79, + 0x322bed48, + 0x3c22e043, + 0x2e39f75e, + 0x2030fa55, + 0xec9ab701, + 0xe293ba0a, + 0xf088ad17, + 0xfe81a01c, + 0xd4be832d, + 0xdab78e26, + 0xc8ac993b, + 0xc6a59430, + 0x9cd2df59, + 0x92dbd252, + 0x80c0c54f, + 0x8ec9c844, + 0xa4f6eb75, + 0xaaffe67e, + 0xb8e4f163, + 0xb6edfc68, + 0x0c0a67b1, + 0x02036aba, + 0x10187da7, + 0x1e1170ac, + 0x342e539d, + 0x3a275e96, + 0x283c498b, + 0x26354480, + 0x7c420fe9, + 0x724b02e2, + 0x605015ff, + 0x6e5918f4, + 0x44663bc5, + 0x4a6f36ce, + 0x587421d3, + 0x567d2cd8, + 0x37a10c7a, + 0x39a80171, + 0x2bb3166c, + 0x25ba1b67, + 0x0f853856, + 0x018c355d, + 0x13972240, + 0x1d9e2f4b, + 0x47e96422, + 0x49e06929, + 0x5bfb7e34, + 0x55f2733f, + 0x7fcd500e, + 0x71c45d05, + 0x63df4a18, + 0x6dd64713, + 0xd731dcca, + 0xd938d1c1, + 0xcb23c6dc, + 0xc52acbd7, + 0xef15e8e6, + 0xe11ce5ed, + 0xf307f2f0, + 0xfd0efffb, + 0xa779b492, + 0xa970b999, + 0xbb6bae84, + 0xb562a38f, + 0x9f5d80be, + 0x91548db5, + 0x834f9aa8, + 0x8d4697a3 + ]); + + function expandKey128(cipherKey) { + var b = 176, + result = new Uint8Array(b); + result.set(cipherKey); + for (var j = 16, i = 1; j < b; ++i) { + // RotWord + var t1 = result[j - 3], + t2 = result[j - 2], + t3 = result[j - 1], + t4 = result[j - 4]; + // SubWord + t1 = s[t1]; + t2 = s[t2]; + t3 = s[t3]; + t4 = s[t4]; + // Rcon + t1 = t1 ^ rcon[i]; + for (var n = 0; n < 4; ++n) { + result[j] = t1 ^= result[j - 16]; + j++; + result[j] = t2 ^= result[j - 16]; + j++; + result[j] = t3 ^= result[j - 16]; + j++; + result[j] = t4 ^= result[j - 16]; + j++; + } + } + return result; + } + + function decrypt128(input, key) { + var state = new Uint8Array(16); + state.set(input); + var i, j, k; + var t, u, v; + // AddRoundKey + for (j = 0, k = 160; j < 16; ++j, ++k) { + state[j] ^= key[k]; + } + for (i = 9; i >= 1; --i) { + // InvShiftRows + t = state[13]; + state[13] = state[9]; + state[9] = state[5]; + state[5] = state[1]; + state[1] = t; + t = state[14]; + u = state[10]; + state[14] = state[6]; + state[10] = state[2]; + state[6] = t; + state[2] = u; + t = state[15]; + u = state[11]; + v = state[7]; + state[15] = state[3]; + state[11] = t; + state[7] = u; + state[3] = v; + // InvSubBytes + for (j = 0; j < 16; ++j) { + state[j] = inv_s[state[j]]; + } + // AddRoundKey + for (j = 0, k = i * 16; j < 16; ++j, ++k) { + state[j] ^= key[k]; + } + // InvMixColumns + for (j = 0; j < 16; j += 4) { + var s0 = mix[state[j]], + s1 = mix[state[j + 1]], + s2 = mix[state[j + 2]], + s3 = mix[state[j + 3]]; + t = + s0 ^ + (s1 >>> 8) ^ + (s1 << 24) ^ + (s2 >>> 16) ^ + (s2 << 16) ^ + (s3 >>> 24) ^ + (s3 << 8); + state[j] = (t >>> 24) & 0xff; + state[j + 1] = (t >> 16) & 0xff; + state[j + 2] = (t >> 8) & 0xff; + state[j + 3] = t & 0xff; + } + } + // InvShiftRows + t = state[13]; + state[13] = state[9]; + state[9] = state[5]; + state[5] = state[1]; + state[1] = t; + t = state[14]; + u = state[10]; + state[14] = state[6]; + state[10] = state[2]; + state[6] = t; + state[2] = u; + t = state[15]; + u = state[11]; + v = state[7]; + state[15] = state[3]; + state[11] = t; + state[7] = u; + state[3] = v; + for (j = 0; j < 16; ++j) { + // InvSubBytes + state[j] = inv_s[state[j]]; + // AddRoundKey + state[j] ^= key[j]; + } + return state; + } + + function encrypt128(input, key) { + var t, u, v, k; + var state = new Uint8Array(16); + state.set(input); + for (j = 0; j < 16; ++j) { + // AddRoundKey + state[j] ^= key[j]; + } + + for (i = 1; i < 10; i++) { + //SubBytes + for (j = 0; j < 16; ++j) { + state[j] = s[state[j]]; + } + //ShiftRows + v = state[1]; + state[1] = state[5]; + state[5] = state[9]; + state[9] = state[13]; + state[13] = v; + v = state[2]; + u = state[6]; + state[2] = state[10]; + state[6] = state[14]; + state[10] = v; + state[14] = u; + v = state[3]; + u = state[7]; + t = state[11]; + state[3] = state[15]; + state[7] = v; + state[11] = u; + state[15] = t; + //MixColumns + for (var j = 0; j < 16; j += 4) { + var s0 = state[j + 0], + s1 = state[j + 1]; + var s2 = state[j + 2], + s3 = state[j + 3]; + t = s0 ^ s1 ^ s2 ^ s3; + state[j + 0] ^= t ^ mixCol[s0 ^ s1]; + state[j + 1] ^= t ^ mixCol[s1 ^ s2]; + state[j + 2] ^= t ^ mixCol[s2 ^ s3]; + state[j + 3] ^= t ^ mixCol[s3 ^ s0]; + } + //AddRoundKey + for (j = 0, k = i * 16; j < 16; ++j, ++k) { + state[j] ^= key[k]; + } + } + + //SubBytes + for (j = 0; j < 16; ++j) { + state[j] = s[state[j]]; + } + //ShiftRows + v = state[1]; + state[1] = state[5]; + state[5] = state[9]; + state[9] = state[13]; + state[13] = v; + v = state[2]; + u = state[6]; + state[2] = state[10]; + state[6] = state[14]; + state[10] = v; + state[14] = u; + v = state[3]; + u = state[7]; + t = state[11]; + state[3] = state[15]; + state[7] = v; + state[11] = u; + state[15] = t; + //AddRoundKey + for (j = 0, k = 160; j < 16; ++j, ++k) { + state[j] ^= key[k]; + } + return state; + } + + function AES128Cipher(key) { + this.key = expandKey128(key); + this.buffer = new Uint8Array(16); + this.bufferPosition = 0; + } + + function decryptBlock2(data, finalize) { + var i, + j, + ii, + sourceLength = data.length, + buffer = this.buffer, + bufferLength = this.bufferPosition, + result = [], + iv = this.iv; + for (i = 0; i < sourceLength; ++i) { + buffer[bufferLength] = data[i]; + ++bufferLength; + if (bufferLength < 16) { + continue; + } + // buffer is full, decrypting + var plain = decrypt128(buffer, this.key); + // xor-ing the IV vector to get plain text + for (j = 0; j < 16; ++j) { + plain[j] ^= iv[j]; + } + iv = buffer; + result.push(plain); + buffer = new Uint8Array(16); + bufferLength = 0; + } + // saving incomplete buffer + this.buffer = buffer; + this.bufferLength = bufferLength; + this.iv = iv; + if (result.length === 0) { + return new Uint8Array([]); + } + // combining plain text blocks into one + var outputLength = 16 * result.length; + if (finalize) { + // undo a padding that is described in RFC 2898 + var lastBlock = result[result.length - 1]; + var psLen = lastBlock[15]; + if (psLen <= 16) { + for (i = 15, ii = 16 - psLen; i >= ii; --i) { + if (lastBlock[i] !== psLen) { + // Invalid padding, assume that the block has no padding. + psLen = 0; + break; + } + } + outputLength -= psLen; + result[result.length - 1] = lastBlock.subarray(0, 16 - psLen); + } + } + var output = new Uint8Array(outputLength); + for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) { + output.set(result[i], j); + } + return output; + } + + AES128Cipher.prototype = { + decryptBlock: function AES128Cipher_decryptBlock(data, finalize) { + var i, + sourceLength = data.length; + var buffer = this.buffer, + bufferLength = this.bufferPosition; + // waiting for IV values -- they are at the start of the stream + for ( + i = 0; + bufferLength < 16 && i < sourceLength; + ++i, ++bufferLength + ) { + buffer[bufferLength] = data[i]; + } + if (bufferLength < 16) { + // need more data + this.bufferLength = bufferLength; + return new Uint8Array([]); + } + this.iv = buffer; + this.buffer = new Uint8Array(16); + this.bufferLength = 0; + // starting decryption + this.decryptBlock = decryptBlock2; + return this.decryptBlock(data.subarray(16), finalize); + }, + encrypt: function AES128Cipher_encrypt(data, iv) { + var i, + j, + ii, + sourceLength = data.length, + buffer = this.buffer, + bufferLength = this.bufferPosition, + result = []; + if (!iv) { + iv = new Uint8Array(16); + } + for (i = 0; i < sourceLength; ++i) { + buffer[bufferLength] = data[i]; + ++bufferLength; + if (bufferLength < 16) { + continue; + } + for (j = 0; j < 16; ++j) { + buffer[j] ^= iv[j]; + } + + // buffer is full, encrypting + var cipher = encrypt128(buffer, this.key); + iv = cipher; + result.push(cipher); + buffer = new Uint8Array(16); + bufferLength = 0; + } + // saving incomplete buffer + this.buffer = buffer; + this.bufferLength = bufferLength; + this.iv = iv; + if (result.length === 0) { + return new Uint8Array([]); + } + // combining plain text blocks into one + var outputLength = 16 * result.length; + var output = new Uint8Array(outputLength); + for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) { + output.set(result[i], j); + } + return output; + } + }; + + return AES128Cipher; + })(); + + var AES256Cipher = (function AES256CipherClosure() { + var rcon = new Uint8Array([ + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d, + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91, + 0x39, + 0x72, + 0xe4, + 0xd3, + 0xbd, + 0x61, + 0xc2, + 0x9f, + 0x25, + 0x4a, + 0x94, + 0x33, + 0x66, + 0xcc, + 0x83, + 0x1d, + 0x3a, + 0x74, + 0xe8, + 0xcb, + 0x8d + ]); + + var s = new Uint8Array([ + 0x63, + 0x7c, + 0x77, + 0x7b, + 0xf2, + 0x6b, + 0x6f, + 0xc5, + 0x30, + 0x01, + 0x67, + 0x2b, + 0xfe, + 0xd7, + 0xab, + 0x76, + 0xca, + 0x82, + 0xc9, + 0x7d, + 0xfa, + 0x59, + 0x47, + 0xf0, + 0xad, + 0xd4, + 0xa2, + 0xaf, + 0x9c, + 0xa4, + 0x72, + 0xc0, + 0xb7, + 0xfd, + 0x93, + 0x26, + 0x36, + 0x3f, + 0xf7, + 0xcc, + 0x34, + 0xa5, + 0xe5, + 0xf1, + 0x71, + 0xd8, + 0x31, + 0x15, + 0x04, + 0xc7, + 0x23, + 0xc3, + 0x18, + 0x96, + 0x05, + 0x9a, + 0x07, + 0x12, + 0x80, + 0xe2, + 0xeb, + 0x27, + 0xb2, + 0x75, + 0x09, + 0x83, + 0x2c, + 0x1a, + 0x1b, + 0x6e, + 0x5a, + 0xa0, + 0x52, + 0x3b, + 0xd6, + 0xb3, + 0x29, + 0xe3, + 0x2f, + 0x84, + 0x53, + 0xd1, + 0x00, + 0xed, + 0x20, + 0xfc, + 0xb1, + 0x5b, + 0x6a, + 0xcb, + 0xbe, + 0x39, + 0x4a, + 0x4c, + 0x58, + 0xcf, + 0xd0, + 0xef, + 0xaa, + 0xfb, + 0x43, + 0x4d, + 0x33, + 0x85, + 0x45, + 0xf9, + 0x02, + 0x7f, + 0x50, + 0x3c, + 0x9f, + 0xa8, + 0x51, + 0xa3, + 0x40, + 0x8f, + 0x92, + 0x9d, + 0x38, + 0xf5, + 0xbc, + 0xb6, + 0xda, + 0x21, + 0x10, + 0xff, + 0xf3, + 0xd2, + 0xcd, + 0x0c, + 0x13, + 0xec, + 0x5f, + 0x97, + 0x44, + 0x17, + 0xc4, + 0xa7, + 0x7e, + 0x3d, + 0x64, + 0x5d, + 0x19, + 0x73, + 0x60, + 0x81, + 0x4f, + 0xdc, + 0x22, + 0x2a, + 0x90, + 0x88, + 0x46, + 0xee, + 0xb8, + 0x14, + 0xde, + 0x5e, + 0x0b, + 0xdb, + 0xe0, + 0x32, + 0x3a, + 0x0a, + 0x49, + 0x06, + 0x24, + 0x5c, + 0xc2, + 0xd3, + 0xac, + 0x62, + 0x91, + 0x95, + 0xe4, + 0x79, + 0xe7, + 0xc8, + 0x37, + 0x6d, + 0x8d, + 0xd5, + 0x4e, + 0xa9, + 0x6c, + 0x56, + 0xf4, + 0xea, + 0x65, + 0x7a, + 0xae, + 0x08, + 0xba, + 0x78, + 0x25, + 0x2e, + 0x1c, + 0xa6, + 0xb4, + 0xc6, + 0xe8, + 0xdd, + 0x74, + 0x1f, + 0x4b, + 0xbd, + 0x8b, + 0x8a, + 0x70, + 0x3e, + 0xb5, + 0x66, + 0x48, + 0x03, + 0xf6, + 0x0e, + 0x61, + 0x35, + 0x57, + 0xb9, + 0x86, + 0xc1, + 0x1d, + 0x9e, + 0xe1, + 0xf8, + 0x98, + 0x11, + 0x69, + 0xd9, + 0x8e, + 0x94, + 0x9b, + 0x1e, + 0x87, + 0xe9, + 0xce, + 0x55, + 0x28, + 0xdf, + 0x8c, + 0xa1, + 0x89, + 0x0d, + 0xbf, + 0xe6, + 0x42, + 0x68, + 0x41, + 0x99, + 0x2d, + 0x0f, + 0xb0, + 0x54, + 0xbb, + 0x16 + ]); + + var inv_s = new Uint8Array([ + 0x52, + 0x09, + 0x6a, + 0xd5, + 0x30, + 0x36, + 0xa5, + 0x38, + 0xbf, + 0x40, + 0xa3, + 0x9e, + 0x81, + 0xf3, + 0xd7, + 0xfb, + 0x7c, + 0xe3, + 0x39, + 0x82, + 0x9b, + 0x2f, + 0xff, + 0x87, + 0x34, + 0x8e, + 0x43, + 0x44, + 0xc4, + 0xde, + 0xe9, + 0xcb, + 0x54, + 0x7b, + 0x94, + 0x32, + 0xa6, + 0xc2, + 0x23, + 0x3d, + 0xee, + 0x4c, + 0x95, + 0x0b, + 0x42, + 0xfa, + 0xc3, + 0x4e, + 0x08, + 0x2e, + 0xa1, + 0x66, + 0x28, + 0xd9, + 0x24, + 0xb2, + 0x76, + 0x5b, + 0xa2, + 0x49, + 0x6d, + 0x8b, + 0xd1, + 0x25, + 0x72, + 0xf8, + 0xf6, + 0x64, + 0x86, + 0x68, + 0x98, + 0x16, + 0xd4, + 0xa4, + 0x5c, + 0xcc, + 0x5d, + 0x65, + 0xb6, + 0x92, + 0x6c, + 0x70, + 0x48, + 0x50, + 0xfd, + 0xed, + 0xb9, + 0xda, + 0x5e, + 0x15, + 0x46, + 0x57, + 0xa7, + 0x8d, + 0x9d, + 0x84, + 0x90, + 0xd8, + 0xab, + 0x00, + 0x8c, + 0xbc, + 0xd3, + 0x0a, + 0xf7, + 0xe4, + 0x58, + 0x05, + 0xb8, + 0xb3, + 0x45, + 0x06, + 0xd0, + 0x2c, + 0x1e, + 0x8f, + 0xca, + 0x3f, + 0x0f, + 0x02, + 0xc1, + 0xaf, + 0xbd, + 0x03, + 0x01, + 0x13, + 0x8a, + 0x6b, + 0x3a, + 0x91, + 0x11, + 0x41, + 0x4f, + 0x67, + 0xdc, + 0xea, + 0x97, + 0xf2, + 0xcf, + 0xce, + 0xf0, + 0xb4, + 0xe6, + 0x73, + 0x96, + 0xac, + 0x74, + 0x22, + 0xe7, + 0xad, + 0x35, + 0x85, + 0xe2, + 0xf9, + 0x37, + 0xe8, + 0x1c, + 0x75, + 0xdf, + 0x6e, + 0x47, + 0xf1, + 0x1a, + 0x71, + 0x1d, + 0x29, + 0xc5, + 0x89, + 0x6f, + 0xb7, + 0x62, + 0x0e, + 0xaa, + 0x18, + 0xbe, + 0x1b, + 0xfc, + 0x56, + 0x3e, + 0x4b, + 0xc6, + 0xd2, + 0x79, + 0x20, + 0x9a, + 0xdb, + 0xc0, + 0xfe, + 0x78, + 0xcd, + 0x5a, + 0xf4, + 0x1f, + 0xdd, + 0xa8, + 0x33, + 0x88, + 0x07, + 0xc7, + 0x31, + 0xb1, + 0x12, + 0x10, + 0x59, + 0x27, + 0x80, + 0xec, + 0x5f, + 0x60, + 0x51, + 0x7f, + 0xa9, + 0x19, + 0xb5, + 0x4a, + 0x0d, + 0x2d, + 0xe5, + 0x7a, + 0x9f, + 0x93, + 0xc9, + 0x9c, + 0xef, + 0xa0, + 0xe0, + 0x3b, + 0x4d, + 0xae, + 0x2a, + 0xf5, + 0xb0, + 0xc8, + 0xeb, + 0xbb, + 0x3c, + 0x83, + 0x53, + 0x99, + 0x61, + 0x17, + 0x2b, + 0x04, + 0x7e, + 0xba, + 0x77, + 0xd6, + 0x26, + 0xe1, + 0x69, + 0x14, + 0x63, + 0x55, + 0x21, + 0x0c, + 0x7d + ]); + + var mixCol = new Uint8Array(256); + for (var i = 0; i < 256; i++) { + if (i < 128) { + mixCol[i] = i << 1; + } else { + mixCol[i] = (i << 1) ^ 0x1b; + } + } + var mix = new Uint32Array([ + 0x00000000, + 0x0e090d0b, + 0x1c121a16, + 0x121b171d, + 0x3824342c, + 0x362d3927, + 0x24362e3a, + 0x2a3f2331, + 0x70486858, + 0x7e416553, + 0x6c5a724e, + 0x62537f45, + 0x486c5c74, + 0x4665517f, + 0x547e4662, + 0x5a774b69, + 0xe090d0b0, + 0xee99ddbb, + 0xfc82caa6, + 0xf28bc7ad, + 0xd8b4e49c, + 0xd6bde997, + 0xc4a6fe8a, + 0xcaaff381, + 0x90d8b8e8, + 0x9ed1b5e3, + 0x8ccaa2fe, + 0x82c3aff5, + 0xa8fc8cc4, + 0xa6f581cf, + 0xb4ee96d2, + 0xbae79bd9, + 0xdb3bbb7b, + 0xd532b670, + 0xc729a16d, + 0xc920ac66, + 0xe31f8f57, + 0xed16825c, + 0xff0d9541, + 0xf104984a, + 0xab73d323, + 0xa57ade28, + 0xb761c935, + 0xb968c43e, + 0x9357e70f, + 0x9d5eea04, + 0x8f45fd19, + 0x814cf012, + 0x3bab6bcb, + 0x35a266c0, + 0x27b971dd, + 0x29b07cd6, + 0x038f5fe7, + 0x0d8652ec, + 0x1f9d45f1, + 0x119448fa, + 0x4be30393, + 0x45ea0e98, + 0x57f11985, + 0x59f8148e, + 0x73c737bf, + 0x7dce3ab4, + 0x6fd52da9, + 0x61dc20a2, + 0xad766df6, + 0xa37f60fd, + 0xb16477e0, + 0xbf6d7aeb, + 0x955259da, + 0x9b5b54d1, + 0x894043cc, + 0x87494ec7, + 0xdd3e05ae, + 0xd33708a5, + 0xc12c1fb8, + 0xcf2512b3, + 0xe51a3182, + 0xeb133c89, + 0xf9082b94, + 0xf701269f, + 0x4de6bd46, + 0x43efb04d, + 0x51f4a750, + 0x5ffdaa5b, + 0x75c2896a, + 0x7bcb8461, + 0x69d0937c, + 0x67d99e77, + 0x3daed51e, + 0x33a7d815, + 0x21bccf08, + 0x2fb5c203, + 0x058ae132, + 0x0b83ec39, + 0x1998fb24, + 0x1791f62f, + 0x764dd68d, + 0x7844db86, + 0x6a5fcc9b, + 0x6456c190, + 0x4e69e2a1, + 0x4060efaa, + 0x527bf8b7, + 0x5c72f5bc, + 0x0605bed5, + 0x080cb3de, + 0x1a17a4c3, + 0x141ea9c8, + 0x3e218af9, + 0x302887f2, + 0x223390ef, + 0x2c3a9de4, + 0x96dd063d, + 0x98d40b36, + 0x8acf1c2b, + 0x84c61120, + 0xaef93211, + 0xa0f03f1a, + 0xb2eb2807, + 0xbce2250c, + 0xe6956e65, + 0xe89c636e, + 0xfa877473, + 0xf48e7978, + 0xdeb15a49, + 0xd0b85742, + 0xc2a3405f, + 0xccaa4d54, + 0x41ecdaf7, + 0x4fe5d7fc, + 0x5dfec0e1, + 0x53f7cdea, + 0x79c8eedb, + 0x77c1e3d0, + 0x65daf4cd, + 0x6bd3f9c6, + 0x31a4b2af, + 0x3fadbfa4, + 0x2db6a8b9, + 0x23bfa5b2, + 0x09808683, + 0x07898b88, + 0x15929c95, + 0x1b9b919e, + 0xa17c0a47, + 0xaf75074c, + 0xbd6e1051, + 0xb3671d5a, + 0x99583e6b, + 0x97513360, + 0x854a247d, + 0x8b432976, + 0xd134621f, + 0xdf3d6f14, + 0xcd267809, + 0xc32f7502, + 0xe9105633, + 0xe7195b38, + 0xf5024c25, + 0xfb0b412e, + 0x9ad7618c, + 0x94de6c87, + 0x86c57b9a, + 0x88cc7691, + 0xa2f355a0, + 0xacfa58ab, + 0xbee14fb6, + 0xb0e842bd, + 0xea9f09d4, + 0xe49604df, + 0xf68d13c2, + 0xf8841ec9, + 0xd2bb3df8, + 0xdcb230f3, + 0xcea927ee, + 0xc0a02ae5, + 0x7a47b13c, + 0x744ebc37, + 0x6655ab2a, + 0x685ca621, + 0x42638510, + 0x4c6a881b, + 0x5e719f06, + 0x5078920d, + 0x0a0fd964, + 0x0406d46f, + 0x161dc372, + 0x1814ce79, + 0x322bed48, + 0x3c22e043, + 0x2e39f75e, + 0x2030fa55, + 0xec9ab701, + 0xe293ba0a, + 0xf088ad17, + 0xfe81a01c, + 0xd4be832d, + 0xdab78e26, + 0xc8ac993b, + 0xc6a59430, + 0x9cd2df59, + 0x92dbd252, + 0x80c0c54f, + 0x8ec9c844, + 0xa4f6eb75, + 0xaaffe67e, + 0xb8e4f163, + 0xb6edfc68, + 0x0c0a67b1, + 0x02036aba, + 0x10187da7, + 0x1e1170ac, + 0x342e539d, + 0x3a275e96, + 0x283c498b, + 0x26354480, + 0x7c420fe9, + 0x724b02e2, + 0x605015ff, + 0x6e5918f4, + 0x44663bc5, + 0x4a6f36ce, + 0x587421d3, + 0x567d2cd8, + 0x37a10c7a, + 0x39a80171, + 0x2bb3166c, + 0x25ba1b67, + 0x0f853856, + 0x018c355d, + 0x13972240, + 0x1d9e2f4b, + 0x47e96422, + 0x49e06929, + 0x5bfb7e34, + 0x55f2733f, + 0x7fcd500e, + 0x71c45d05, + 0x63df4a18, + 0x6dd64713, + 0xd731dcca, + 0xd938d1c1, + 0xcb23c6dc, + 0xc52acbd7, + 0xef15e8e6, + 0xe11ce5ed, + 0xf307f2f0, + 0xfd0efffb, + 0xa779b492, + 0xa970b999, + 0xbb6bae84, + 0xb562a38f, + 0x9f5d80be, + 0x91548db5, + 0x834f9aa8, + 0x8d4697a3 + ]); + + function expandKey256(cipherKey) { + var b = 240, + result = new Uint8Array(b); + var r = 1; + + result.set(cipherKey); + for (var j = 32, i = 1; j < b; ++i) { + if (j % 32 === 16) { + t1 = s[t1]; + t2 = s[t2]; + t3 = s[t3]; + t4 = s[t4]; + } else if (j % 32 === 0) { + // RotWord + var t1 = result[j - 3], + t2 = result[j - 2], + t3 = result[j - 1], + t4 = result[j - 4]; + // SubWord + t1 = s[t1]; + t2 = s[t2]; + t3 = s[t3]; + t4 = s[t4]; + // Rcon + t1 = t1 ^ r; + if ((r <<= 1) >= 256) { + r = (r ^ 0x1b) & 0xff; + } + } + + for (var n = 0; n < 4; ++n) { + result[j] = t1 ^= result[j - 32]; + j++; + result[j] = t2 ^= result[j - 32]; + j++; + result[j] = t3 ^= result[j - 32]; + j++; + result[j] = t4 ^= result[j - 32]; + j++; + } + } + return result; + } + + function decrypt256(input, key) { + var state = new Uint8Array(16); + state.set(input); + var i, j, k; + var t, u, v; + // AddRoundKey + for (j = 0, k = 224; j < 16; ++j, ++k) { + state[j] ^= key[k]; + } + for (i = 13; i >= 1; --i) { + // InvShiftRows + t = state[13]; + state[13] = state[9]; + state[9] = state[5]; + state[5] = state[1]; + state[1] = t; + t = state[14]; + u = state[10]; + state[14] = state[6]; + state[10] = state[2]; + state[6] = t; + state[2] = u; + t = state[15]; + u = state[11]; + v = state[7]; + state[15] = state[3]; + state[11] = t; + state[7] = u; + state[3] = v; + // InvSubBytes + for (j = 0; j < 16; ++j) { + state[j] = inv_s[state[j]]; + } + // AddRoundKey + for (j = 0, k = i * 16; j < 16; ++j, ++k) { + state[j] ^= key[k]; + } + // InvMixColumns + for (j = 0; j < 16; j += 4) { + var s0 = mix[state[j]], + s1 = mix[state[j + 1]], + s2 = mix[state[j + 2]], + s3 = mix[state[j + 3]]; + t = + s0 ^ + (s1 >>> 8) ^ + (s1 << 24) ^ + (s2 >>> 16) ^ + (s2 << 16) ^ + (s3 >>> 24) ^ + (s3 << 8); + state[j] = (t >>> 24) & 0xff; + state[j + 1] = (t >> 16) & 0xff; + state[j + 2] = (t >> 8) & 0xff; + state[j + 3] = t & 0xff; + } + } + // InvShiftRows + t = state[13]; + state[13] = state[9]; + state[9] = state[5]; + state[5] = state[1]; + state[1] = t; + t = state[14]; + u = state[10]; + state[14] = state[6]; + state[10] = state[2]; + state[6] = t; + state[2] = u; + t = state[15]; + u = state[11]; + v = state[7]; + state[15] = state[3]; + state[11] = t; + state[7] = u; + state[3] = v; + for (j = 0; j < 16; ++j) { + // InvSubBytes + state[j] = inv_s[state[j]]; + // AddRoundKey + state[j] ^= key[j]; + } + return state; + } + + function encrypt256(input, key) { + var t, u, v, k; + var state = new Uint8Array(16); + state.set(input); + for (j = 0; j < 16; ++j) { + // AddRoundKey + state[j] ^= key[j]; + } + + for (i = 1; i < 14; i++) { + //SubBytes + for (j = 0; j < 16; ++j) { + state[j] = s[state[j]]; + } + //ShiftRows + v = state[1]; + state[1] = state[5]; + state[5] = state[9]; + state[9] = state[13]; + state[13] = v; + v = state[2]; + u = state[6]; + state[2] = state[10]; + state[6] = state[14]; + state[10] = v; + state[14] = u; + v = state[3]; + u = state[7]; + t = state[11]; + state[3] = state[15]; + state[7] = v; + state[11] = u; + state[15] = t; + //MixColumns + for (var j = 0; j < 16; j += 4) { + var s0 = state[j + 0], + s1 = state[j + 1]; + var s2 = state[j + 2], + s3 = state[j + 3]; + t = s0 ^ s1 ^ s2 ^ s3; + state[j + 0] ^= t ^ mixCol[s0 ^ s1]; + state[j + 1] ^= t ^ mixCol[s1 ^ s2]; + state[j + 2] ^= t ^ mixCol[s2 ^ s3]; + state[j + 3] ^= t ^ mixCol[s3 ^ s0]; + } + //AddRoundKey + for (j = 0, k = i * 16; j < 16; ++j, ++k) { + state[j] ^= key[k]; + } + } + + //SubBytes + for (j = 0; j < 16; ++j) { + state[j] = s[state[j]]; + } + //ShiftRows + v = state[1]; + state[1] = state[5]; + state[5] = state[9]; + state[9] = state[13]; + state[13] = v; + v = state[2]; + u = state[6]; + state[2] = state[10]; + state[6] = state[14]; + state[10] = v; + state[14] = u; + v = state[3]; + u = state[7]; + t = state[11]; + state[3] = state[15]; + state[7] = v; + state[11] = u; + state[15] = t; + //AddRoundKey + for (j = 0, k = 224; j < 16; ++j, ++k) { + state[j] ^= key[k]; + } + + return state; + } + + function AES256Cipher(key) { + this.key = expandKey256(key); + this.buffer = new Uint8Array(16); + this.bufferPosition = 0; + } + + function decryptBlock2(data, finalize) { + var i, + j, + ii, + sourceLength = data.length, + buffer = this.buffer, + bufferLength = this.bufferPosition, + result = [], + iv = this.iv; + + for (i = 0; i < sourceLength; ++i) { + buffer[bufferLength] = data[i]; + ++bufferLength; + if (bufferLength < 16) { + continue; + } + // buffer is full, decrypting + var plain = decrypt256(buffer, this.key); + // xor-ing the IV vector to get plain text + for (j = 0; j < 16; ++j) { + plain[j] ^= iv[j]; + } + iv = buffer; + result.push(plain); + buffer = new Uint8Array(16); + bufferLength = 0; + } + // saving incomplete buffer + this.buffer = buffer; + this.bufferLength = bufferLength; + this.iv = iv; + if (result.length === 0) { + return new Uint8Array([]); + } + // combining plain text blocks into one + var outputLength = 16 * result.length; + if (finalize) { + // undo a padding that is described in RFC 2898 + var lastBlock = result[result.length - 1]; + var psLen = lastBlock[15]; + if (psLen <= 16) { + for (i = 15, ii = 16 - psLen; i >= ii; --i) { + if (lastBlock[i] !== psLen) { + // Invalid padding, assume that the block has no padding. + psLen = 0; + break; + } + } + outputLength -= psLen; + result[result.length - 1] = lastBlock.subarray(0, 16 - psLen); + } + } + var output = new Uint8Array(outputLength); + for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) { + output.set(result[i], j); + } + return output; + } + + AES256Cipher.prototype = { + decryptBlock: function AES256Cipher_decryptBlock(data, finalize, iv) { + var i, + sourceLength = data.length; + var buffer = this.buffer, + bufferLength = this.bufferPosition; + // if not supplied an IV wait for IV values + // they are at the start of the stream + if (iv) { + this.iv = iv; + } else { + for ( + i = 0; + bufferLength < 16 && i < sourceLength; + ++i, ++bufferLength + ) { + buffer[bufferLength] = data[i]; + } + if (bufferLength < 16) { + //need more data + this.bufferLength = bufferLength; + return new Uint8Array([]); + } + this.iv = buffer; + data = data.subarray(16); + } + this.buffer = new Uint8Array(16); + this.bufferLength = 0; + // starting decryption + this.decryptBlock = decryptBlock2; + return this.decryptBlock(data, finalize); + }, + encrypt: function AES256Cipher_encrypt(data, iv) { + var i, + j, + ii, + sourceLength = data.length, + buffer = this.buffer, + bufferLength = this.bufferPosition, + result = []; + if (!iv) { + iv = new Uint8Array(16); + } + for (i = 0; i < sourceLength; ++i) { + buffer[bufferLength] = data[i]; + ++bufferLength; + if (bufferLength < 16) { + continue; + } + for (j = 0; j < 16; ++j) { + buffer[j] ^= iv[j]; + } + + // buffer is full, encrypting + var cipher = encrypt256(buffer, this.key); + this.iv = cipher; + result.push(cipher); + buffer = new Uint8Array(16); + bufferLength = 0; + } + // saving incomplete buffer + this.buffer = buffer; + this.bufferLength = bufferLength; + this.iv = iv; + if (result.length === 0) { + return new Uint8Array([]); + } + // combining plain text blocks into one + var outputLength = 16 * result.length; + var output = new Uint8Array(outputLength); + for (i = 0, j = 0, ii = result.length; i < ii; ++i, j += 16) { + output.set(result[i], j); + } + return output; + } + }; + + return AES256Cipher; + })(); + + var PDF17 = (function PDF17Closure() { + function compareByteArrays(array1, array2) { + if (array1.length !== array2.length) { + return false; + } + for (var i = 0; i < array1.length; i++) { + if (array1[i] !== array2[i]) { + return false; + } + } + return true; + } + + function PDF17() {} + + PDF17.prototype = { + checkOwnerPassword: function PDF17_checkOwnerPassword( + password, + ownerValidationSalt, + userBytes, + ownerPassword + ) { + var hashData = new Uint8Array(password.length + 56); + hashData.set(password, 0); + hashData.set(ownerValidationSalt, password.length); + hashData.set(userBytes, password.length + ownerValidationSalt.length); + var result = calculateSHA256(hashData, 0, hashData.length); + return compareByteArrays(result, ownerPassword); + }, + checkUserPassword: function PDF17_checkUserPassword( + password, + userValidationSalt, + userPassword + ) { + var hashData = new Uint8Array(password.length + 8); + hashData.set(password, 0); + hashData.set(userValidationSalt, password.length); + var result = calculateSHA256(hashData, 0, hashData.length); + return compareByteArrays(result, userPassword); + }, + getOwnerKey: function PDF17_getOwnerKey( + password, + ownerKeySalt, + userBytes, + ownerEncryption + ) { + var hashData = new Uint8Array(password.length + 56); + hashData.set(password, 0); + hashData.set(ownerKeySalt, password.length); + hashData.set(userBytes, password.length + ownerKeySalt.length); + var key = calculateSHA256(hashData, 0, hashData.length); + var cipher = new AES256Cipher(key); + return cipher.decryptBlock(ownerEncryption, false, new Uint8Array(16)); + }, + getUserKey: function PDF17_getUserKey( + password, + userKeySalt, + userEncryption + ) { + var hashData = new Uint8Array(password.length + 8); + hashData.set(password, 0); + hashData.set(userKeySalt, password.length); + //key is the decryption key for the UE string + var key = calculateSHA256(hashData, 0, hashData.length); + var cipher = new AES256Cipher(key); + return cipher.decryptBlock(userEncryption, false, new Uint8Array(16)); + } + }; + return PDF17; + })(); + + var PDF20 = (function PDF20Closure() { + function concatArrays(array1, array2) { + var t = new Uint8Array(array1.length + array2.length); + t.set(array1, 0); + t.set(array2, array1.length); + return t; + } + + function calculatePDF20Hash(password, input, userBytes) { + //This refers to Algorithm 2.B as defined in ISO 32000-2 + var k = calculateSHA256(input, 0, input.length).subarray(0, 32); + var e = [0]; + var i = 0; + while (i < 64 || e[e.length - 1] > i - 32) { + var arrayLength = password.length + k.length + userBytes.length; + + var k1 = new Uint8Array(arrayLength * 64); + var array = concatArrays(password, k); + array = concatArrays(array, userBytes); + for (var j = 0, pos = 0; j < 64; j++, pos += arrayLength) { + k1.set(array, pos); + } + //AES128 CBC NO PADDING with + //first 16 bytes of k as the key and the second 16 as the iv. + var cipher = new AES128Cipher(k.subarray(0, 16)); + e = cipher.encrypt(k1, k.subarray(16, 32)); + //Now we have to take the first 16 bytes of an unsigned + //big endian integer... and compute the remainder + //modulo 3.... That is a fairly large number and + //JavaScript isn't going to handle that well... + //So we're using a trick that allows us to perform + //modulo math byte by byte + var remainder = 0; + for (var z = 0; z < 16; z++) { + remainder *= 256 % 3; + remainder %= 3; + remainder += (e[z] >>> 0) % 3; + remainder %= 3; + } + if (remainder === 0) { + k = calculateSHA256(e, 0, e.length); + } else if (remainder === 1) { + k = calculateSHA384(e, 0, e.length); + } else if (remainder === 2) { + k = calculateSHA512(e, 0, e.length); + } + i++; + } + return k.subarray(0, 32); + } + + function PDF20() {} + + function compareByteArrays(array1, array2) { + if (array1.length !== array2.length) { + return false; + } + for (var i = 0; i < array1.length; i++) { + if (array1[i] !== array2[i]) { + return false; + } + } + return true; + } + + PDF20.prototype = { + hash: function PDF20_hash(password, concatBytes, userBytes) { + return calculatePDF20Hash(password, concatBytes, userBytes); + }, + checkOwnerPassword: function PDF20_checkOwnerPassword( + password, + ownerValidationSalt, + userBytes, + ownerPassword + ) { + var hashData = new Uint8Array(password.length + 56); + hashData.set(password, 0); + hashData.set(ownerValidationSalt, password.length); + hashData.set(userBytes, password.length + ownerValidationSalt.length); + var result = calculatePDF20Hash(password, hashData, userBytes); + return compareByteArrays(result, ownerPassword); + }, + checkUserPassword: function PDF20_checkUserPassword( + password, + userValidationSalt, + userPassword + ) { + var hashData = new Uint8Array(password.length + 8); + hashData.set(password, 0); + hashData.set(userValidationSalt, password.length); + var result = calculatePDF20Hash(password, hashData, []); + return compareByteArrays(result, userPassword); + }, + getOwnerKey: function PDF20_getOwnerKey( + password, + ownerKeySalt, + userBytes, + ownerEncryption + ) { + var hashData = new Uint8Array(password.length + 56); + hashData.set(password, 0); + hashData.set(ownerKeySalt, password.length); + hashData.set(userBytes, password.length + ownerKeySalt.length); + var key = calculatePDF20Hash(password, hashData, userBytes); + var cipher = new AES256Cipher(key); + return cipher.decryptBlock(ownerEncryption, false, new Uint8Array(16)); + }, + getUserKey: function PDF20_getUserKey( + password, + userKeySalt, + userEncryption + ) { + var hashData = new Uint8Array(password.length + 8); + hashData.set(password, 0); + hashData.set(userKeySalt, password.length); + //key is the decryption key for the UE string + var key = calculatePDF20Hash(password, hashData, []); + var cipher = new AES256Cipher(key); + return cipher.decryptBlock(userEncryption, false, new Uint8Array(16)); + } + }; + return PDF20; + })(); + + var CipherTransform = (function CipherTransformClosure() { + function CipherTransform(stringCipherConstructor, streamCipherConstructor) { + this.stringCipherConstructor = stringCipherConstructor; + this.streamCipherConstructor = streamCipherConstructor; + } + + CipherTransform.prototype = { + createStream: function CipherTransform_createStream(stream, length) { + var cipher = new this.streamCipherConstructor(); + return new DecryptStream( + stream, + length, + function cipherTransformDecryptStream(data, finalize) { + return cipher.decryptBlock(data, finalize); + } + ); + }, + decryptString: function CipherTransform_decryptString(s) { + var cipher = new this.stringCipherConstructor(); + var data = stringToBytes(s); + data = cipher.decryptBlock(data, true); + return bytesToString(data); + } + }; + return CipherTransform; + })(); + + var CipherTransformFactory = (function CipherTransformFactoryClosure() { + var 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 + ]); + + function createEncryptionKey20( + revision, + password, + ownerPassword, + ownerValidationSalt, + ownerKeySalt, + uBytes, + userPassword, + userValidationSalt, + userKeySalt, + ownerEncryption, + userEncryption, + perms + ) { + if (password) { + var passwordLength = Math.min(127, password.length); + password = password.subarray(0, passwordLength); + } else { + password = []; + } + var pdfAlgorithm; + if (revision === 6) { + pdfAlgorithm = new PDF20(); + } else { + pdfAlgorithm = new PDF17(); + } + + if (pdfAlgorithm) { + if ( + pdfAlgorithm.checkUserPassword( + password, + userValidationSalt, + userPassword + ) + ) { + return pdfAlgorithm.getUserKey(password, userKeySalt, userEncryption); + } else if ( + password.length && + pdfAlgorithm.checkOwnerPassword( + password, + ownerValidationSalt, + uBytes, + ownerPassword + ) + ) { + return pdfAlgorithm.getOwnerKey( + password, + ownerKeySalt, + uBytes, + ownerEncryption + ); + } + } + + return null; + } + + function prepareKeyData( + fileId, + password, + ownerPassword, + userPassword, + flags, + revision, + keyLength, + encryptMetadata + ) { + var hashDataSize = 40 + ownerPassword.length + fileId.length; + var hashData = new Uint8Array(hashDataSize), + 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]; + } + if (revision >= 4 && !encryptMetadata) { + hashData[i++] = 0xff; + hashData[i++] = 0xff; + hashData[i++] = 0xff; + hashData[i++] = 0xff; + } + var hash = calculateMD5(hashData, 0, i); + var keyLengthInBytes = keyLength >> 3; + if (revision >= 3) { + for (j = 0; j < 50; ++j) { + hash = calculateMD5(hash, 0, keyLengthInBytes); + } + } + var encryptionKey = hash.subarray(0, keyLengthInBytes); + var cipher, checkData; + + if (revision >= 3) { + for (i = 0; i < 32; ++i) { + hashData[i] = defaultPasswordBytes[i]; + } + for (j = 0, n = fileId.length; j < n; ++j) { + hashData[i++] = fileId[j]; + } + cipher = new ARCFourCipher(encryptionKey); + checkData = cipher.encryptBlock(calculateMD5(hashData, 0, i)); + n = encryptionKey.length; + var derivedKey = new Uint8Array(n), + k; + for (j = 1; j <= 19; ++j) { + for (k = 0; k < n; ++k) { + derivedKey[k] = encryptionKey[k] ^ j; + } + cipher = new ARCFourCipher(derivedKey); + checkData = cipher.encryptBlock(checkData); + } + for (j = 0, n = checkData.length; j < n; ++j) { + if (userPassword[j] !== checkData[j]) { + return null; + } + } + } else { + cipher = new ARCFourCipher(encryptionKey); + checkData = cipher.encryptBlock(defaultPasswordBytes); + for (j = 0, n = checkData.length; j < n; ++j) { + if (userPassword[j] !== checkData[j]) { + return null; + } + } + } + return encryptionKey; + } + + function decodeUserPassword(password, ownerPassword, revision, keyLength) { + var hashData = new Uint8Array(32), + i = 0, + j, + n; + n = Math.min(32, password.length); + for (; i < n; ++i) { + hashData[i] = password[i]; + } + j = 0; + while (i < 32) { + hashData[i++] = defaultPasswordBytes[j++]; + } + var hash = calculateMD5(hashData, 0, i); + var keyLengthInBytes = keyLength >> 3; + if (revision >= 3) { + for (j = 0; j < 50; ++j) { + hash = calculateMD5(hash, 0, hash.length); + } + } + + var cipher, userPassword; + if (revision >= 3) { + userPassword = ownerPassword; + var derivedKey = new Uint8Array(keyLengthInBytes), + k; + for (j = 19; j >= 0; j--) { + for (k = 0; k < keyLengthInBytes; ++k) { + derivedKey[k] = hash[k] ^ j; + } + cipher = new ARCFourCipher(derivedKey); + userPassword = cipher.encryptBlock(userPassword); + } + } else { + cipher = new ARCFourCipher(hash.subarray(0, keyLengthInBytes)); + userPassword = cipher.encryptBlock(ownerPassword); + } + return userPassword; + } + + var identityName = Name.get("Identity"); + + function CipherTransformFactory(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 && + algorithm !== 4 && + algorithm !== 5) + ) { + error("unsupported encryption algorithm"); + } + this.algorithm = algorithm; + 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")).subarray(0, 32); + var userPassword = stringToBytes(dict.get("U")).subarray(0, 32); + var flags = dict.get("P"); + var revision = dict.get("R"); + // meaningful when V is 4 or 5 + var encryptMetadata = + (algorithm === 4 || algorithm === 5) && + dict.get("EncryptMetadata") !== false; + this.encryptMetadata = encryptMetadata; + + var fileIdBytes = stringToBytes(fileId); + var passwordBytes; + if (password) { + if (revision === 6) { + try { + password = utf8StringToString(password); + } catch (ex) { + warn( + "CipherTransformFactory: " + + "Unable to convert UTF8 encoded password." + ); + } + } + passwordBytes = stringToBytes(password); + } + + var encryptionKey; + if (algorithm !== 5) { + encryptionKey = prepareKeyData( + fileIdBytes, + passwordBytes, + ownerPassword, + userPassword, + flags, + revision, + keyLength, + encryptMetadata + ); + } else { + var ownerValidationSalt = stringToBytes(dict.get("O")).subarray(32, 40); + var ownerKeySalt = stringToBytes(dict.get("O")).subarray(40, 48); + var uBytes = stringToBytes(dict.get("U")).subarray(0, 48); + var userValidationSalt = stringToBytes(dict.get("U")).subarray(32, 40); + var userKeySalt = stringToBytes(dict.get("U")).subarray(40, 48); + var ownerEncryption = stringToBytes(dict.get("OE")); + var userEncryption = stringToBytes(dict.get("UE")); + var perms = stringToBytes(dict.get("Perms")); + encryptionKey = createEncryptionKey20( + revision, + passwordBytes, + ownerPassword, + ownerValidationSalt, + ownerKeySalt, + uBytes, + userPassword, + userValidationSalt, + userKeySalt, + ownerEncryption, + userEncryption, + perms + ); + } + if (!encryptionKey && !password) { + throw new PasswordException( + "No password given", + PasswordResponses.NEED_PASSWORD + ); + } else if (!encryptionKey && password) { + // Attempting use the password as an owner password + var decodedPassword = decodeUserPassword( + passwordBytes, + ownerPassword, + revision, + keyLength + ); + encryptionKey = prepareKeyData( + fileIdBytes, + decodedPassword, + ownerPassword, + userPassword, + flags, + revision, + keyLength, + encryptMetadata + ); + } + + if (!encryptionKey) { + throw new PasswordException( + "Incorrect Password", + PasswordResponses.INCORRECT_PASSWORD + ); + } + + this.encryptionKey = encryptionKey; + + if (algorithm >= 4) { + this.cf = dict.get("CF"); + this.stmf = dict.get("StmF") || identityName; + this.strf = dict.get("StrF") || identityName; + this.eff = dict.get("EFF") || this.stmf; + } + } + + function buildObjectKey(num, gen, encryptionKey, isAes) { + var key = new Uint8Array(encryptionKey.length + 9), + 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; + if (isAes) { + key[i++] = 0x73; + key[i++] = 0x41; + key[i++] = 0x6c; + key[i++] = 0x54; + } + var hash = calculateMD5(key, 0, i); + return hash.subarray(0, Math.min(encryptionKey.length + 5, 16)); + } + + function buildCipherConstructor(cf, name, num, gen, key) { + var cryptFilter = cf.get(name.name); + var cfm; + if (cryptFilter !== null && cryptFilter !== undefined) { + cfm = cryptFilter.get("CFM"); + } + if (!cfm || cfm.name === "None") { + return function cipherTransformFactoryBuildCipherConstructorNone() { + return new NullCipher(); + }; + } + if ("V2" === cfm.name) { + return function cipherTransformFactoryBuildCipherConstructorV2() { + return new ARCFourCipher(buildObjectKey(num, gen, key, false)); + }; + } + if ("AESV2" === cfm.name) { + return function cipherTransformFactoryBuildCipherConstructorAESV2() { + return new AES128Cipher(buildObjectKey(num, gen, key, true)); + }; + } + if ("AESV3" === cfm.name) { + return function cipherTransformFactoryBuildCipherConstructorAESV3() { + return new AES256Cipher(key); + }; + } + error("Unknown crypto method"); + } + + CipherTransformFactory.prototype = { + createCipherTransform: function CipherTransformFactory_createCipherTransform( + num, + gen + ) { + if (this.algorithm === 4 || this.algorithm === 5) { + return new CipherTransform( + buildCipherConstructor( + this.cf, + this.stmf, + num, + gen, + this.encryptionKey + ), + buildCipherConstructor( + this.cf, + this.strf, + num, + gen, + this.encryptionKey + ) + ); + } + // algorithms 1 and 2 + var key = buildObjectKey(num, gen, this.encryptionKey, false); + var cipherConstructor = function buildCipherCipherConstructor() { + return new ARCFourCipher(key); + }; + return new CipherTransform(cipherConstructor, cipherConstructor); + } + }; + + return CipherTransformFactory; + })(); + + exports.AES128Cipher = AES128Cipher; + exports.AES256Cipher = AES256Cipher; + exports.ARCFourCipher = ARCFourCipher; + exports.CipherTransformFactory = CipherTransformFactory; + exports.PDF17 = PDF17; + exports.PDF20 = PDF20; + exports.calculateMD5 = calculateMD5; + exports.calculateSHA256 = calculateSHA256; + exports.calculateSHA384 = calculateSHA384; + exports.calculateSHA512 = calculateSHA512; +}); + +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +("use strict"); + +(function(root, factory) { + //if (typeof define === 'function' && define.amd) { + // define('pdfjs/core/obj', ['exports', 'pdfjs/shared/util', + // 'pdfjs/core/primitives', 'pdfjs/core/crypto', 'pdfjs/core/parser', + // 'pdfjs/core/chunked_stream'], factory); + // } else if (typeof exports !== 'undefined') { + // factory(exports, require('../shared/util.js'), require('./primitives.js'), + // require('./crypto.js'), require('./parser.js'), + // require('./chunked_stream.js')); + //} else { + factory( + (root.pdfjsCoreObj = {}), + root.pdfjsSharedUtil, + root.pdfjsCorePrimitives, + root.pdfjsCoreCrypto, + root.pdfjsCoreParser, + root.pdfjsCoreChunkedStream + ); + //} +})(window, function( + exports, + sharedUtil, + corePrimitives, + coreCrypto, + coreParser, + coreChunkedStream +) { + var InvalidPDFException = sharedUtil.InvalidPDFException; + var MissingDataException = sharedUtil.MissingDataException; + var XRefParseException = sharedUtil.XRefParseException; + var assert = sharedUtil.assert; + var bytesToString = sharedUtil.bytesToString; + var createPromiseCapability = sharedUtil.createPromiseCapability; + var error = sharedUtil.error; + var info = sharedUtil.info; + var isArray = sharedUtil.isArray; + var isInt = sharedUtil.isInt; + var isString = sharedUtil.isString; + var shadow = sharedUtil.shadow; + var stringToPDFString = sharedUtil.stringToPDFString; + var stringToUTF8String = sharedUtil.stringToUTF8String; + var warn = sharedUtil.warn; + var Ref = corePrimitives.Ref; + var RefSet = corePrimitives.RefSet; + var RefSetCache = corePrimitives.RefSetCache; + var isName = corePrimitives.isName; + var isCmd = corePrimitives.isCmd; + var isDict = corePrimitives.isDict; + var isRef = corePrimitives.isRef; + var isStream = corePrimitives.isStream; + var CipherTransformFactory = coreCrypto.CipherTransformFactory; + var Lexer = coreParser.Lexer; + var Parser = coreParser.Parser; + var ChunkedStream = coreChunkedStream.ChunkedStream; + + var Catalog = (function CatalogClosure() { + function Catalog(pdfManager, xref, pageFactory) { + this.pdfManager = pdfManager; + this.xref = xref; + this.catDict = xref.getCatalogObj(); + this.fontCache = new RefSetCache(); + assert(isDict(this.catDict), "catalog object is not a dictionary"); + + // TODO refactor to move getPage() to the PDFDocument. + this.pageFactory = pageFactory; + this.pagePromises = []; + } + + Catalog.prototype = { + get metadata() { + var streamRef = this.catDict.getRaw("Metadata"); + if (!isRef(streamRef)) { + return shadow(this, "metadata", null); + } + + var encryptMetadata = !this.xref.encrypt + ? false + : this.xref.encrypt.encryptMetadata; + + var stream = this.xref.fetch(streamRef, !encryptMetadata); + var metadata; + if (stream && isDict(stream.dict)) { + var type = stream.dict.get("Type"); + var subtype = stream.dict.get("Subtype"); + + if ( + isName(type) && + isName(subtype) && + type.name === "Metadata" && + subtype.name === "XML" + ) { + // XXX: This should examine the charset the XML document defines, + // however since there are currently no real means to decode + // arbitrary charsets, let's just hope that the author of the PDF + // was reasonable enough to stick with the XML default charset, + // which is UTF-8. + try { + metadata = stringToUTF8String(bytesToString(stream.getBytes())); + } catch (e) { + info("Skipping invalid metadata."); + } + } + } + + return shadow(this, "metadata", metadata); + }, + get toplevelPagesDict() { + var pagesObj = this.catDict.get("Pages"); + assert(isDict(pagesObj), "invalid top-level pages dictionary"); + // shadow the prototype getter + return shadow(this, "toplevelPagesDict", pagesObj); + }, + get documentOutline() { + var obj = null; + try { + obj = this.readDocumentOutline(); + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn("Unable to read document outline"); + } + return shadow(this, "documentOutline", obj); + }, + readDocumentOutline: function Catalog_readDocumentOutline() { + var xref = this.xref; + var obj = this.catDict.get("Outlines"); + var root = { items: [] }; + if (isDict(obj)) { + obj = obj.getRaw("First"); + var processed = new RefSet(); + if (isRef(obj)) { + var queue = [{ obj: obj, parent: root }]; + // to avoid recursion keeping track of the items + // in the processed dictionary + processed.put(obj); + while (queue.length > 0) { + var i = queue.shift(); + var outlineDict = xref.fetchIfRef(i.obj); + if (outlineDict === null) { + continue; + } + if (!outlineDict.has("Title")) { + error("Invalid outline item"); + } + var dest = outlineDict.get("A"); + if (dest) { + dest = dest.get("D"); + } else if (outlineDict.has("Dest")) { + dest = outlineDict.getRaw("Dest"); + if (isName(dest)) { + dest = dest.name; + } + } + var title = outlineDict.get("Title"); + var outlineItem = { + dest: dest, + title: stringToPDFString(title), + color: outlineDict.get("C") || [0, 0, 0], + count: outlineDict.get("Count"), + bold: !!(outlineDict.get("F") & 2), + italic: !!(outlineDict.get("F") & 1), + items: [] + }; + i.parent.items.push(outlineItem); + obj = outlineDict.getRaw("First"); + if (isRef(obj) && !processed.has(obj)) { + queue.push({ obj: obj, parent: outlineItem }); + processed.put(obj); + } + obj = outlineDict.getRaw("Next"); + if (isRef(obj) && !processed.has(obj)) { + queue.push({ obj: obj, parent: i.parent }); + processed.put(obj); + } + } + } + } + return root.items.length > 0 ? root.items : null; + }, + get numPages() { + var obj = this.toplevelPagesDict.get("Count"); + assert( + isInt(obj), + "page count in top level pages object is not an integer" + ); + // shadow the prototype getter + return shadow(this, "num", obj); + }, + get destinations() { + function fetchDestination(dest) { + return isDict(dest) ? dest.get("D") : dest; + } + + var xref = this.xref; + var dests = {}, + nameTreeRef, + nameDictionaryRef; + var obj = this.catDict.get("Names"); + if (obj && obj.has("Dests")) { + nameTreeRef = obj.getRaw("Dests"); + } else if (this.catDict.has("Dests")) { + nameDictionaryRef = this.catDict.get("Dests"); + } + + if (nameDictionaryRef) { + // reading simple destination dictionary + obj = nameDictionaryRef; + obj.forEach(function catalogForEach(key, value) { + if (!value) { + return; + } + dests[key] = fetchDestination(value); + }); + } + if (nameTreeRef) { + var nameTree = new NameTree(nameTreeRef, xref); + var names = nameTree.getAll(); + for (var name in names) { + if (!names.hasOwnProperty(name)) { + continue; + } + dests[name] = fetchDestination(names[name]); + } + } + return shadow(this, "destinations", dests); + }, + getDestination: function Catalog_getDestination(destinationId) { + function fetchDestination(dest) { + return isDict(dest) ? dest.get("D") : dest; + } + + var xref = this.xref; + var dest = null, + nameTreeRef, + nameDictionaryRef; + var obj = this.catDict.get("Names"); + if (obj && obj.has("Dests")) { + nameTreeRef = obj.getRaw("Dests"); + } else if (this.catDict.has("Dests")) { + nameDictionaryRef = this.catDict.get("Dests"); + } + + if (nameDictionaryRef) { + // Simple destination dictionary. + var value = nameDictionaryRef.get(destinationId); + if (value) { + dest = fetchDestination(value); + } + } + if (nameTreeRef) { + var nameTree = new NameTree(nameTreeRef, xref); + dest = fetchDestination(nameTree.get(destinationId)); + } + return dest; + }, + get attachments() { + var xref = this.xref; + var attachments = null, + nameTreeRef; + var obj = this.catDict.get("Names"); + if (obj) { + nameTreeRef = obj.getRaw("EmbeddedFiles"); + } + + if (nameTreeRef) { + var nameTree = new NameTree(nameTreeRef, xref); + var names = nameTree.getAll(); + for (var name in names) { + if (!names.hasOwnProperty(name)) { + continue; + } + var fs = new FileSpec(names[name], xref); + if (!attachments) { + attachments = {}; + } + attachments[stringToPDFString(name)] = fs.serializable; + } + } + return shadow(this, "attachments", attachments); + }, + get javaScript() { + var xref = this.xref; + var obj = this.catDict.get("Names"); + + var javaScript = []; + function appendIfJavaScriptDict(jsDict) { + var type = jsDict.get("S"); + if (!isName(type) || type.name !== "JavaScript") { + return; + } + var js = jsDict.get("JS"); + if (isStream(js)) { + js = bytesToString(js.getBytes()); + } else if (!isString(js)) { + return; + } + javaScript.push(stringToPDFString(js)); + } + if (obj && obj.has("JavaScript")) { + var nameTree = new NameTree(obj.getRaw("JavaScript"), xref); + var names = nameTree.getAll(); + for (var name in names) { + if (!names.hasOwnProperty(name)) { + continue; + } + // We don't really use the JavaScript right now. This code is + // defensive so we don't cause errors on document load. + var jsDict = names[name]; + if (isDict(jsDict)) { + appendIfJavaScriptDict(jsDict); + } + } + } + + // Append OpenAction actions to javaScript array + var openactionDict = this.catDict.get("OpenAction"); + if (isDict(openactionDict, "Action")) { + var actionType = openactionDict.get("S"); + if (isName(actionType) && actionType.name === "Named") { + // The named Print action is not a part of the PDF 1.7 specification, + // but is supported by many PDF readers/writers (including Adobe's). + var action = openactionDict.get("N"); + if (isName(action) && action.name === "Print") { + javaScript.push("print({});"); + } + } else { + appendIfJavaScriptDict(openactionDict); + } + } + + return shadow(this, "javaScript", javaScript); + }, + + cleanup: function Catalog_cleanup() { + var promises = []; + this.fontCache.forEach(function(promise) { + promises.push(promise); + }); + return Promise.all(promises).then( + function(translatedFonts) { + for (var i = 0, ii = translatedFonts.length; i < ii; i++) { + var font = translatedFonts[i].dict; + delete font.translated; + } + this.fontCache.clear(); + }.bind(this) + ); + }, + + getPage: function Catalog_getPage(pageIndex) { + if (!(pageIndex in this.pagePromises)) { + this.pagePromises[pageIndex] = this.getPageDict(pageIndex).then( + function(a) { + var dict = a[0]; + var ref = a[1]; + return this.pageFactory.createPage( + pageIndex, + dict, + ref, + this.fontCache + ); + }.bind(this) + ); + } + return this.pagePromises[pageIndex]; + }, + + getPageDict: function Catalog_getPageDict(pageIndex) { + var capability = createPromiseCapability(); + var nodesToVisit = [this.catDict.getRaw("Pages")]; + var currentPageIndex = 0; + var xref = this.xref; + var checkAllKids = false; + + function next() { + while (nodesToVisit.length) { + var currentNode = nodesToVisit.pop(); + + if (isRef(currentNode)) { + xref.fetchAsync(currentNode).then(function(obj) { + if (isDict(obj, "Page") || (isDict(obj) && !obj.has("Kids"))) { + if (pageIndex === currentPageIndex) { + capability.resolve([obj, currentNode]); + } else { + currentPageIndex++; + next(); + } + return; + } + nodesToVisit.push(obj); + next(); + }, capability.reject); + return; + } + + // Must be a child page dictionary. + assert( + isDict(currentNode), + "page dictionary kid reference points to wrong type of object" + ); + var count = currentNode.get("Count"); + // If the current node doesn't have any children, avoid getting stuck + // in an empty node further down in the tree (see issue5644.pdf). + if (count === 0) { + checkAllKids = true; + } + // Skip nodes where the page can't be. + if (currentPageIndex + count <= pageIndex) { + currentPageIndex += count; + continue; + } + + var kids = currentNode.get("Kids"); + assert( + isArray(kids), + "page dictionary kids object is not an array" + ); + if (!checkAllKids && count === kids.length) { + // Nodes that don't have the page have been skipped and this is the + // bottom of the tree which means the page requested must be a + // descendant of this pages node. Ideally we would just resolve the + // promise with the page ref here, but there is the case where more + // pages nodes could link to single a page (see issue 3666 pdf). To + // handle this push it back on the queue so if it is a pages node it + // will be descended into. + nodesToVisit = [kids[pageIndex - currentPageIndex]]; + currentPageIndex = pageIndex; + continue; + } else { + for (var last = kids.length - 1; last >= 0; last--) { + nodesToVisit.push(kids[last]); + } + } + } + capability.reject("Page index " + pageIndex + " not found."); + } + next(); + return capability.promise; + }, + + getPageIndex: function Catalog_getPageIndex(ref) { + // The page tree nodes have the count of all the leaves below them. To get + // how many pages are before we just have to walk up the tree and keep + // adding the count of siblings to the left of the node. + var xref = this.xref; + function pagesBeforeRef(kidRef) { + var total = 0; + var parentRef; + return xref + .fetchAsync(kidRef) + .then(function(node) { + if (!node) { + return null; + } + parentRef = node.getRaw("Parent"); + return node.getAsync("Parent"); + }) + .then(function(parent) { + if (!parent) { + return null; + } + return parent.getAsync("Kids"); + }) + .then(function(kids) { + if (!kids) { + return null; + } + var kidPromises = []; + var found = false; + for (var i = 0; i < kids.length; i++) { + var kid = kids[i]; + assert(isRef(kid), "kids must be a ref"); + if (kid.num === kidRef.num) { + found = true; + break; + } + kidPromises.push( + xref.fetchAsync(kid).then(function(kid) { + if (kid.has("Count")) { + var count = kid.get("Count"); + total += count; + } else { + // page leaf node + total++; + } + }) + ); + } + if (!found) { + error("kid ref not found in parents kids"); + } + return Promise.all(kidPromises).then(function() { + return [total, parentRef]; + }); + }); + } + + var total = 0; + function next(ref) { + return pagesBeforeRef(ref).then(function(args) { + if (!args) { + return total; + } + var count = args[0]; + var parentRef = args[1]; + total += count; + return next(parentRef); + }); + } + + return next(ref); + } + }; + + return Catalog; + })(); + + var XRef = (function XRefClosure() { + function XRef(stream, password) { + this.stream = stream; + this.entries = []; + this.xrefstms = {}; + // prepare the XRef cache + this.cache = []; + this.password = password; + this.stats = { + streamTypes: [], + fontTypes: [] + }; + } + + XRef.prototype = { + setStartXRef: function XRef_setStartXRef(startXRef) { + // Store the starting positions of xref tables as we process them + // so we can recover from missing data errors + this.startXRefQueue = [startXRef]; + this.xrefBlocks = [startXRef]; + }, + + parse: function XRef_parse(recoveryMode) { + var trailerDict; + if (!recoveryMode) { + trailerDict = this.readXRef(); + } else { + warn("Indexing all PDF objects"); + trailerDict = this.indexObjects(); + } + trailerDict.assignXref(this); + this.trailer = trailerDict; + var encrypt = trailerDict.get("Encrypt"); + if (encrypt) { + var ids = trailerDict.get("ID"); + var fileId = ids && ids.length ? ids[0] : ""; + this.encrypt = new CipherTransformFactory( + encrypt, + fileId, + this.password + ); + } + + // get the root dictionary (catalog) object + if (!(this.root = trailerDict.get("Root"))) { + error("Invalid root reference"); + } + }, + + processXRefTable: function XRef_processXRefTable(parser) { + if (!("tableState" in this)) { + // Stores state of the table as we process it so we can resume + // from middle of table in case of missing data error + this.tableState = { + entryNum: 0, + streamPos: parser.lexer.stream.pos, + parserBuf1: parser.buf1, + parserBuf2: parser.buf2 + }; + } + + var obj = this.readXRefTable(parser); + + // Sanity check + if (!isCmd(obj, "trailer")) { + error("Invalid XRef table: could not find trailer dictionary"); + } + // Read trailer dictionary, e.g. + // trailer + // << /Size 22 + // /Root 20R + // /Info 10R + // /ID [ <81b14aafa313db63dbd6f981e49f94f4> ] + // >> + // The parser goes through the entire stream << ... >> and provides + // a getter interface for the key-value table + var dict = parser.getObj(); + + // The pdflib PDF generator can generate a nested trailer dictionary + if (!isDict(dict) && dict.dict) { + dict = dict.dict; + } + if (!isDict(dict)) { + error("Invalid XRef table: could not parse trailer dictionary"); + } + delete this.tableState; + + return dict; + }, + + readXRefTable: function XRef_readXRefTable(parser) { + // Example of cross-reference table: + // xref + // 0 1 <-- subsection header (first obj #, obj count) + // 0000000000 65535 f <-- actual object (offset, generation #, f/n) + // 23 2 <-- subsection header ... and so on ... + // 0000025518 00002 n + // 0000025635 00000 n + // trailer + // ... + + var stream = parser.lexer.stream; + var tableState = this.tableState; + stream.pos = tableState.streamPos; + parser.buf1 = tableState.parserBuf1; + parser.buf2 = tableState.parserBuf2; + + // Outer loop is over subsection headers + var obj; + + while (true) { + if ( + !("firstEntryNum" in tableState) || + !("entryCount" in tableState) + ) { + if (isCmd((obj = parser.getObj()), "trailer")) { + break; + } + tableState.firstEntryNum = obj; + tableState.entryCount = parser.getObj(); + } + + var first = tableState.firstEntryNum; + var count = tableState.entryCount; + if (!isInt(first) || !isInt(count)) { + error("Invalid XRef table: wrong types in subsection header"); + } + // Inner loop is over objects themselves + for (var i = tableState.entryNum; i < count; i++) { + tableState.streamPos = stream.pos; + tableState.entryNum = i; + tableState.parserBuf1 = parser.buf1; + tableState.parserBuf2 = parser.buf2; + + var entry = {}; + entry.offset = parser.getObj(); + entry.gen = parser.getObj(); + var type = parser.getObj(); + + if (isCmd(type, "f")) { + entry.free = true; + } else if (isCmd(type, "n")) { + entry.uncompressed = true; + } + + // Validate entry obj + if ( + !isInt(entry.offset) || + !isInt(entry.gen) || + !(entry.free || entry.uncompressed) + ) { + error( + "Invalid entry in XRef subsection: " + first + ", " + count + ); + } + + if (!this.entries[i + first]) { + this.entries[i + first] = entry; + } + } + + tableState.entryNum = 0; + tableState.streamPos = stream.pos; + tableState.parserBuf1 = parser.buf1; + tableState.parserBuf2 = parser.buf2; + delete tableState.firstEntryNum; + delete tableState.entryCount; + } + + // Per issue 3248: hp scanners generate bad XRef + if (first === 1 && this.entries[1] && this.entries[1].free) { + // shifting the entries + this.entries.shift(); + } + + // Sanity check: as per spec, first object must be free + if (this.entries[0] && !this.entries[0].free) { + error("Invalid XRef table: unexpected first object"); + } + return obj; + }, + + processXRefStream: function XRef_processXRefStream(stream) { + if (!("streamState" in this)) { + // Stores state of the stream as we process it so we can resume + // from middle of stream in case of missing data error + var streamParameters = stream.dict; + var byteWidths = streamParameters.get("W"); + var range = streamParameters.get("Index"); + if (!range) { + range = [0, streamParameters.get("Size")]; + } + + this.streamState = { + entryRanges: range, + byteWidths: byteWidths, + entryNum: 0, + streamPos: stream.pos + }; + } + this.readXRefStream(stream); + delete this.streamState; + + return stream.dict; + }, + + readXRefStream: function XRef_readXRefStream(stream) { + var i, j; + var streamState = this.streamState; + stream.pos = streamState.streamPos; + + var byteWidths = streamState.byteWidths; + var typeFieldWidth = byteWidths[0]; + var offsetFieldWidth = byteWidths[1]; + var generationFieldWidth = byteWidths[2]; + + var entryRanges = streamState.entryRanges; + while (entryRanges.length > 0) { + var first = entryRanges[0]; + var n = entryRanges[1]; + + if (!isInt(first) || !isInt(n)) { + error("Invalid XRef range fields: " + first + ", " + n); + } + if ( + !isInt(typeFieldWidth) || + !isInt(offsetFieldWidth) || + !isInt(generationFieldWidth) + ) { + error("Invalid XRef entry fields length: " + first + ", " + n); + } + for (i = streamState.entryNum; i < n; ++i) { + streamState.entryNum = i; + streamState.streamPos = stream.pos; + + var type = 0, + offset = 0, + generation = 0; + for (j = 0; j < typeFieldWidth; ++j) { + type = (type << 8) | stream.getByte(); + } + // if type field is absent, its default value is 1 + if (typeFieldWidth === 0) { + type = 1; + } + for (j = 0; j < offsetFieldWidth; ++j) { + offset = (offset << 8) | stream.getByte(); + } + for (j = 0; j < generationFieldWidth; ++j) { + generation = (generation << 8) | stream.getByte(); + } + var entry = {}; + entry.offset = offset; + entry.gen = generation; + switch (type) { + case 0: + entry.free = true; + break; + case 1: + entry.uncompressed = true; + break; + case 2: + break; + default: + error("Invalid XRef entry type: " + type); + } + if (!this.entries[first + i]) { + this.entries[first + i] = entry; + } + } + + streamState.entryNum = 0; + streamState.streamPos = stream.pos; + entryRanges.splice(0, 2); + } + }, + + indexObjects: function XRef_indexObjects() { + // Simple scan through the PDF content to find objects, + // trailers and XRef streams. + var TAB = 0x9, + LF = 0xa, + CR = 0xd, + SPACE = 0x20; + var PERCENT = 0x25, + LT = 0x3c; + + function readToken(data, offset) { + var token = "", + ch = data[offset]; + while (ch !== LF && ch !== CR && ch !== LT) { + if (++offset >= data.length) { + break; + } + token += String.fromCharCode(ch); + ch = data[offset]; + } + return token; + } + function skipUntil(data, offset, what) { + var length = what.length, + dataLength = data.length; + var skipped = 0; + // finding byte sequence + while (offset < dataLength) { + var i = 0; + while (i < length && data[offset + i] === what[i]) { + ++i; + } + if (i >= length) { + break; // sequence found + } + offset++; + skipped++; + } + return skipped; + } + var objRegExp = /^(\d+)\s+(\d+)\s+obj\b/; + var trailerBytes = new Uint8Array([116, 114, 97, 105, 108, 101, 114]); + var startxrefBytes = new Uint8Array([ + 115, + 116, + 97, + 114, + 116, + 120, + 114, + 101, + 102 + ]); + var endobjBytes = new Uint8Array([101, 110, 100, 111, 98, 106]); + var xrefBytes = new Uint8Array([47, 88, 82, 101, 102]); + + // Clear out any existing entries, since they may be bogus. + this.entries.length = 0; + + var stream = this.stream; + stream.pos = 0; + var buffer = stream.getBytes(); + var position = stream.start, + length = buffer.length; + var trailers = [], + xrefStms = []; + while (position < length) { + var ch = buffer[position]; + if (ch === TAB || ch === LF || ch === CR || ch === SPACE) { + ++position; + continue; + } + if (ch === PERCENT) { + // %-comment + do { + ++position; + if (position >= length) { + break; + } + ch = buffer[position]; + } while (ch !== LF && ch !== CR); + continue; + } + var token = readToken(buffer, position); + var m; + if ( + token.indexOf("xref") === 0 && + (token.length === 4 || /\s/.test(token[4])) + ) { + position += skipUntil(buffer, position, trailerBytes); + trailers.push(position); + position += skipUntil(buffer, position, startxrefBytes); + } else if ((m = objRegExp.exec(token))) { + if (typeof this.entries[m[1]] === "undefined") { + this.entries[m[1]] = { + offset: position - stream.start, + gen: m[2] | 0, + uncompressed: true + }; + } + var contentLength = skipUntil(buffer, position, endobjBytes) + 7; + var content = buffer.subarray(position, position + contentLength); + + // checking XRef stream suspect + // (it shall have '/XRef' and next char is not a letter) + var xrefTagOffset = skipUntil(content, 0, xrefBytes); + if ( + xrefTagOffset < contentLength && + content[xrefTagOffset + 5] < 64 + ) { + xrefStms.push(position - stream.start); + this.xrefstms[position - stream.start] = 1; // Avoid recursion + } + + position += contentLength; + } else if ( + token.indexOf("trailer") === 0 && + (token.length === 7 || /\s/.test(token[7])) + ) { + trailers.push(position); + position += skipUntil(buffer, position, startxrefBytes); + } else { + position += token.length + 1; + } + } + // reading XRef streams + var i, ii; + for (i = 0, ii = xrefStms.length; i < ii; ++i) { + this.startXRefQueue.push(xrefStms[i]); + this.xrefBlocks.push(xrefStms[i]); + this.readXRef(/* recoveryMode */ true); + } + // finding main trailer + var dict; + for (i = 0, ii = trailers.length; i < ii; ++i) { + stream.pos = trailers[i]; + var parser = new Parser(new Lexer(stream), true, this); + var obj = parser.getObj(); + if (!isCmd(obj, "trailer")) { + continue; + } + // read the trailer dictionary + if (!isDict((dict = parser.getObj()))) { + continue; + } + // taking the first one with 'ID' + if (dict.has("ID")) { + return dict; + } + } + // no tailer with 'ID', taking last one (if exists) + if (dict) { + return dict; + } + // nothing helps + // calling error() would reject worker with an UnknownErrorException. + throw new InvalidPDFException("Invalid PDF structure"); + }, + + readXRef: function XRef_readXRef(recoveryMode) { + var stream = this.stream; + + try { + while (this.startXRefQueue.length) { + var startXRef = this.startXRefQueue[0]; + + stream.pos = startXRef + stream.start; + + var parser = new Parser(new Lexer(stream), true, this); + var obj = parser.getObj(); + var dict; + + // Get dictionary + if (isCmd(obj, "xref")) { + // Parse end-of-file XRef + dict = this.processXRefTable(parser); + if (!this.topDict) { + this.topDict = dict; + } + + // Recursively get other XRefs 'XRefStm', if any + obj = dict.get("XRefStm"); + if (isInt(obj)) { + var pos = obj; + // ignore previously loaded xref streams + // (possible infinite recursion) + if (!(pos in this.xrefstms)) { + this.xrefstms[pos] = 1; + this.startXRefQueue.push(pos); + this.xrefBlocks.push(pos); + } + } + } else if (isInt(obj)) { + // Parse in-stream XRef + if ( + !isInt(parser.getObj()) || + !isCmd(parser.getObj(), "obj") || + !isStream((obj = parser.getObj())) + ) { + error("Invalid XRef stream"); + } + dict = this.processXRefStream(obj); + if (!this.topDict) { + this.topDict = dict; + } + if (!dict) { + error("Failed to read XRef stream"); + } + } else { + error("Invalid XRef stream header"); + } + + // Recursively get previous dictionary, if any + obj = dict.get("Prev"); + if (isInt(obj)) { + this.startXRefQueue.push(obj); + this.xrefBlocks.push(obj); + } else if (isRef(obj)) { + // The spec says Prev must not be a reference, i.e. "/Prev NNN" + // This is a fallback for non-compliant PDFs, i.e. "/Prev NNN 0 R" + this.startXRefQueue.push(obj.num); + this.xrefBlocks.push(obj.num); + } + this.xrefBlocks.push(stream.pos); + this.startXRefQueue.shift(); + } + + return this.topDict; + } catch (e) { + if (e instanceof MissingDataException) { + throw e; + } + info("(while reading XRef): " + e); + } + + if (recoveryMode) { + return; + } + throw new XRefParseException(); + }, + + getEntry: function XRef_getEntry(i) { + var xrefEntry = this.entries[i]; + if (xrefEntry && !xrefEntry.free && xrefEntry.offset) { + return xrefEntry; + } + return null; + }, + + fetchIfRef: function XRef_fetchIfRef(obj) { + if (!isRef(obj)) { + return obj; + } + return this.fetch(obj); + }, + + fetch: function XRef_fetch(ref, suppressEncryption) { + assert(isRef(ref), "ref object is not a reference"); + var num = ref.num; + if (num in this.cache) { + var cacheEntry = this.cache[num]; + return cacheEntry; + } + + var xrefEntry = this.getEntry(num); + + // the referenced entry can be free + if (xrefEntry === null) { + return (this.cache[num] = null); + } + + if (xrefEntry.uncompressed) { + xrefEntry = this.fetchUncompressed( + ref, + xrefEntry, + suppressEncryption + ); + } else { + xrefEntry = this.fetchCompressed(xrefEntry, suppressEncryption); + } + if (isDict(xrefEntry)) { + xrefEntry.objId = ref.toString(); + } else if (isStream(xrefEntry)) { + xrefEntry.dict.objId = ref.toString(); + } + return xrefEntry; + }, + + fetchUncompressed: function XRef_fetchUncompressed( + ref, + xrefEntry, + suppressEncryption + ) { + var gen = ref.gen; + var num = ref.num; + if (xrefEntry.gen !== gen) { + error("inconsistent generation in XRef"); + } + var stream = this.stream.makeSubStream( + xrefEntry.offset + this.stream.start + ); + var parser = new Parser(new Lexer(stream), true, this); + var obj1 = parser.getObj(); + var obj2 = parser.getObj(); + var obj3 = parser.getObj(); + if ( + !isInt(obj1) || + parseInt(obj1, 10) !== num || + !isInt(obj2) || + parseInt(obj2, 10) !== gen || + !isCmd(obj3) + ) { + error("bad XRef entry"); + } + if (!isCmd(obj3, "obj")) { + // some bad PDFs use "obj1234" and really mean 1234 + if (obj3.cmd.indexOf("obj") === 0) { + num = parseInt(obj3.cmd.substring(3), 10); + if (!isNaN(num)) { + return num; + } + } + error("bad XRef entry"); + } + if (this.encrypt && !suppressEncryption) { + xrefEntry = parser.getObj( + this.encrypt.createCipherTransform(num, gen) + ); + } else { + xrefEntry = parser.getObj(); + } + if (!isStream(xrefEntry)) { + this.cache[num] = xrefEntry; + } + return xrefEntry; + }, + + fetchCompressed: function XRef_fetchCompressed( + xrefEntry, + suppressEncryption + ) { + var tableOffset = xrefEntry.offset; + var stream = this.fetch(new Ref(tableOffset, 0)); + if (!isStream(stream)) { + error("bad ObjStm stream"); + } + var first = stream.dict.get("First"); + var n = stream.dict.get("N"); + if (!isInt(first) || !isInt(n)) { + error("invalid first and n parameters for ObjStm stream"); + } + var parser = new Parser(new Lexer(stream), false, this); + parser.allowStreams = true; + var i, + entries = [], + num, + nums = []; + // read the object numbers to populate cache + for (i = 0; i < n; ++i) { + num = parser.getObj(); + if (!isInt(num)) { + error("invalid object number in the ObjStm stream: " + num); + } + nums.push(num); + var offset = parser.getObj(); + if (!isInt(offset)) { + error("invalid object offset in the ObjStm stream: " + offset); + } + } + // read stream objects for cache + for (i = 0; i < n; ++i) { + entries.push(parser.getObj()); + num = nums[i]; + var entry = this.entries[num]; + if (entry && entry.offset === tableOffset && entry.gen === i) { + this.cache[num] = entries[i]; + } + } + xrefEntry = entries[xrefEntry.gen]; + if (xrefEntry === undefined) { + error("bad XRef entry for compressed object"); + } + return xrefEntry; + }, + + fetchIfRefAsync: function XRef_fetchIfRefAsync(obj) { + if (!isRef(obj)) { + return Promise.resolve(obj); + } + return this.fetchAsync(obj); + }, + + fetchAsync: function XRef_fetchAsync(ref, suppressEncryption) { + var streamManager = this.stream.manager; + var xref = this; + return new Promise(function tryFetch(resolve, reject) { + try { + resolve(xref.fetch(ref, suppressEncryption)); + } catch (e) { + if (e instanceof MissingDataException) { + streamManager.requestRange(e.begin, e.end).then(function() { + tryFetch(resolve, reject); + }, reject); + return; + } + reject(e); + } + }); + }, + + getCatalogObj: function XRef_getCatalogObj() { + return this.root; + } + }; + + return XRef; + })(); + + /** + * A NameTree is like a Dict but has some advantageous properties, see the + * spec (7.9.6) for more details. + * TODO: implement all the Dict functions and make this more efficent. + */ + var NameTree = (function NameTreeClosure() { + function NameTree(root, xref) { + this.root = root; + this.xref = xref; + } + + NameTree.prototype = { + getAll: function NameTree_getAll() { + var dict = {}; + if (!this.root) { + return dict; + } + var xref = this.xref; + // reading name tree + var processed = new RefSet(); + processed.put(this.root); + var queue = [this.root]; + while (queue.length > 0) { + var i, n; + var obj = xref.fetchIfRef(queue.shift()); + if (!isDict(obj)) { + continue; + } + if (obj.has("Kids")) { + var kids = obj.get("Kids"); + for (i = 0, n = kids.length; i < n; i++) { + var kid = kids[i]; + if (processed.has(kid)) { + error("invalid destinations"); + } + queue.push(kid); + processed.put(kid); + } + continue; + } + var names = obj.get("Names"); + if (names) { + for (i = 0, n = names.length; i < n; i += 2) { + dict[xref.fetchIfRef(names[i])] = xref.fetchIfRef(names[i + 1]); + } + } + } + return dict; + }, + + get: function NameTree_get(destinationId) { + if (!this.root) { + return null; + } + + var xref = this.xref; + var kidsOrNames = xref.fetchIfRef(this.root); + var loopCount = 0; + var MAX_NAMES_LEVELS = 10; + var l, r, m; + + // Perform a binary search to quickly find the entry that + // contains the named destination we are looking for. + while (kidsOrNames.has("Kids")) { + loopCount++; + if (loopCount > MAX_NAMES_LEVELS) { + warn( + "Search depth limit for named destionations has been reached." + ); + return null; + } + + var kids = kidsOrNames.get("Kids"); + if (!isArray(kids)) { + return null; + } + + l = 0; + r = kids.length - 1; + while (l <= r) { + m = (l + r) >> 1; + var kid = xref.fetchIfRef(kids[m]); + var limits = kid.get("Limits"); + + if (destinationId < xref.fetchIfRef(limits[0])) { + r = m - 1; + } else if (destinationId > xref.fetchIfRef(limits[1])) { + l = m + 1; + } else { + kidsOrNames = xref.fetchIfRef(kids[m]); + break; + } + } + if (l > r) { + return null; + } + } + + // If we get here, then we have found the right entry. Now + // go through the named destinations in the Named dictionary + // until we find the exact destination we're looking for. + var names = kidsOrNames.get("Names"); + if (isArray(names)) { + // Perform a binary search to reduce the lookup time. + l = 0; + r = names.length - 2; + while (l <= r) { + // Check only even indices (0, 2, 4, ...) because the + // odd indices contain the actual D array. + m = (l + r) & ~1; + if (destinationId < xref.fetchIfRef(names[m])) { + r = m - 2; + } else if (destinationId > xref.fetchIfRef(names[m])) { + l = m + 2; + } else { + return xref.fetchIfRef(names[m + 1]); + } + } + } + return null; + } + }; + return NameTree; + })(); + + /** + * "A PDF file can refer to the contents of another file by using a File + * Specification (PDF 1.1)", see the spec (7.11) for more details. + * NOTE: Only embedded files are supported (as part of the attachments support) + * TODO: support the 'URL' file system (with caching if !/V), portable + * collections attributes and related files (/RF) + */ + var FileSpec = (function FileSpecClosure() { + function FileSpec(root, xref) { + if (!root || !isDict(root)) { + return; + } + this.xref = xref; + this.root = root; + if (root.has("FS")) { + this.fs = root.get("FS"); + } + this.description = root.has("Desc") + ? stringToPDFString(root.get("Desc")) + : ""; + if (root.has("RF")) { + warn("Related file specifications are not supported"); + } + this.contentAvailable = true; + if (!root.has("EF")) { + this.contentAvailable = false; + warn("Non-embedded file specifications are not supported"); + } + } + + function pickPlatformItem(dict) { + // Look for the filename in this order: + // UF, F, Unix, Mac, DOS + if (dict.has("UF")) { + return dict.get("UF"); + } else if (dict.has("F")) { + return dict.get("F"); + } else if (dict.has("Unix")) { + return dict.get("Unix"); + } else if (dict.has("Mac")) { + return dict.get("Mac"); + } else if (dict.has("DOS")) { + return dict.get("DOS"); + } else { + return null; + } + } + + FileSpec.prototype = { + get filename() { + if (!this._filename && this.root) { + var filename = pickPlatformItem(this.root) || "unnamed"; + this._filename = stringToPDFString(filename) + .replace(/\\\\/g, "\\") + .replace(/\\\//g, "/") + .replace(/\\/g, "/"); + } + return this._filename; + }, + get content() { + if (!this.contentAvailable) { + return null; + } + if (!this.contentRef && this.root) { + this.contentRef = pickPlatformItem(this.root.get("EF")); + } + var content = null; + if (this.contentRef) { + var xref = this.xref; + var fileObj = xref.fetchIfRef(this.contentRef); + if (fileObj && isStream(fileObj)) { + content = fileObj.getBytes(); + } else { + warn( + "Embedded file specification points to non-existing/invalid " + + "content" + ); + } + } else { + warn("Embedded file specification does not have a content"); + } + return content; + }, + get serializable() { + return { + filename: this.filename, + content: this.content + }; + } + }; + return FileSpec; + })(); + + /** + * A helper for loading missing data in object graphs. It traverses the graph + * depth first and queues up any objects that have missing data. Once it has + * has traversed as many objects that are available it attempts to bundle the + * missing data requests and then resume from the nodes that weren't ready. + * + * NOTE: It provides protection from circular references by keeping track of + * of loaded references. However, you must be careful not to load any graphs + * that have references to the catalog or other pages since that will cause the + * entire PDF document object graph to be traversed. + */ + var ObjectLoader = (function() { + function mayHaveChildren(value) { + return isRef(value) || isDict(value) || isArray(value) || isStream(value); + } + + function addChildren(node, nodesToVisit) { + var value; + if (isDict(node) || isStream(node)) { + var map; + if (isDict(node)) { + map = node.map; + } else { + map = node.dict.map; + } + for (var key in map) { + value = map[key]; + if (mayHaveChildren(value)) { + nodesToVisit.push(value); + } + } + } else if (isArray(node)) { + for (var i = 0, ii = node.length; i < ii; i++) { + value = node[i]; + if (mayHaveChildren(value)) { + nodesToVisit.push(value); + } + } + } + } + + function ObjectLoader(obj, keys, xref) { + this.obj = obj; + this.keys = keys; + this.xref = xref; + this.refSet = null; + this.capability = null; + } + + ObjectLoader.prototype = { + load: function ObjectLoader_load() { + var keys = this.keys; + this.capability = createPromiseCapability(); + // Don't walk the graph if all the data is already loaded. + if ( + !(this.xref.stream instanceof ChunkedStream) || + this.xref.stream.getMissingChunks().length === 0 + ) { + this.capability.resolve(); + return this.capability.promise; + } + + this.refSet = new RefSet(); + // Setup the initial nodes to visit. + var nodesToVisit = []; + for (var i = 0; i < keys.length; i++) { + nodesToVisit.push(this.obj[keys[i]]); + } + + this._walk(nodesToVisit); + return this.capability.promise; + }, + + _walk: function ObjectLoader_walk(nodesToVisit) { + var nodesToRevisit = []; + var pendingRequests = []; + // DFS walk of the object graph. + while (nodesToVisit.length) { + var currentNode = nodesToVisit.pop(); + + // Only references or chunked streams can cause missing data exceptions. + if (isRef(currentNode)) { + // Skip nodes that have already been visited. + if (this.refSet.has(currentNode)) { + continue; + } + try { + var ref = currentNode; + this.refSet.put(ref); + currentNode = this.xref.fetch(currentNode); + } catch (e) { + if (!(e instanceof MissingDataException)) { + throw e; + } + nodesToRevisit.push(currentNode); + pendingRequests.push({ begin: e.begin, end: e.end }); + } + } + if (currentNode && currentNode.getBaseStreams) { + var baseStreams = currentNode.getBaseStreams(); + var foundMissingData = false; + for (var i = 0; i < baseStreams.length; i++) { + var stream = baseStreams[i]; + if (stream.getMissingChunks && stream.getMissingChunks().length) { + foundMissingData = true; + pendingRequests.push({ + begin: stream.start, + end: stream.end + }); + } + } + if (foundMissingData) { + nodesToRevisit.push(currentNode); + } + } + + addChildren(currentNode, nodesToVisit); + } + + if (pendingRequests.length) { + this.xref.stream.manager.requestRanges(pendingRequests).then( + function pendingRequestCallback() { + nodesToVisit = nodesToRevisit; + for (var i = 0; i < nodesToRevisit.length; i++) { + var node = nodesToRevisit[i]; + // Remove any reference nodes from the currrent refset so they + // aren't skipped when we revist them. + if (isRef(node)) { + this.refSet.remove(node); + } + } + this._walk(nodesToVisit); + }.bind(this), + this.capability.reject + ); + return; + } + // Everything is loaded. + this.refSet = null; + this.capability.resolve(); + } + }; + + return ObjectLoader; + })(); + + exports.Catalog = Catalog; + exports.ObjectLoader = ObjectLoader; + exports.XRef = XRef; +}); + +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +("use strict"); + +(function(root, factory) { + //if (typeof define === 'function' && define.amd) { + // define('pdfjs/core/document', ['exports', 'pdfjs/shared/util', + // 'pdfjs/core/primitives', 'pdfjs/core/stream', 'pdfjs/core/obj', + // 'pdfjs/core/parser', 'pdfjs/core/crypto'], factory); + // } else if (typeof exports !== 'undefined') { + // factory(exports, require('../shared/util.js'), require('./primitives.js'), + // require('./stream.js'), require('./obj.js'), require('./parser.js'), + // require('./crypto.js')); + //} else { + factory( + (root.pdfjsCoreDocument = {}), + root.pdfjsSharedUtil, + root.pdfjsCorePrimitives, + root.pdfjsCoreStream, + root.pdfjsCoreObj, + root.pdfjsCoreParser, + root.pdfjsCoreCrypto + ); + //} +})(window, function( + exports, + sharedUtil, + corePrimitives, + coreStream, + coreObj, + coreParser, + coreCrypto +) { + var MissingDataException = sharedUtil.MissingDataException; + var Util = sharedUtil.Util; + var assert = sharedUtil.assert; + var error = sharedUtil.error; + var info = sharedUtil.info; + var isArray = sharedUtil.isArray; + var isArrayBuffer = sharedUtil.isArrayBuffer; + var isString = sharedUtil.isString; + var shadow = sharedUtil.shadow; + var stringToBytes = sharedUtil.stringToBytes; + var stringToPDFString = sharedUtil.stringToPDFString; + var warn = sharedUtil.warn; + var Dict = corePrimitives.Dict; + var isDict = corePrimitives.isDict; + var isName = corePrimitives.isName; + var isStream = corePrimitives.isStream; + var NullStream = coreStream.NullStream; + var Stream = coreStream.Stream; + var StreamsSequenceStream = coreStream.StreamsSequenceStream; + var Catalog = coreObj.Catalog; + var ObjectLoader = coreObj.ObjectLoader; + var XRef = coreObj.XRef; + var Lexer = coreParser.Lexer; + var Linearization = coreParser.Linearization; + var calculateMD5 = coreCrypto.calculateMD5; + + /** + * The `PDFDocument` holds all the data of the PDF file. Compared to the + * `PDFDoc`, this one doesn't have any job management code. + * Right now there exists one PDFDocument on the main thread + one object + * for each worker. If there is no worker support enabled, there are two + * `PDFDocument` objects on the main thread created. + */ + var PDFDocument = (function PDFDocumentClosure() { + var FINGERPRINT_FIRST_BYTES = 1024; + var EMPTY_FINGERPRINT = + "\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + + function PDFDocument(pdfManager, arg, password) { + if (isStream(arg)) { + init.call(this, pdfManager, arg, password); + } else if (isArrayBuffer(arg)) { + init.call(this, pdfManager, new Stream(arg), password); + } else { + error("PDFDocument: Unknown argument type"); + } + } + + function init(pdfManager, stream, password) { + assert(stream.length > 0, "stream must have data"); + this.pdfManager = pdfManager; + this.stream = stream; + var xref = new XRef(this.stream, password, pdfManager); + this.xref = xref; + } + + function find(stream, needle, limit, backwards) { + var pos = stream.pos; + var end = stream.end; + var strBuf = []; + if (pos + limit > end) { + limit = end - pos; + } + for (var n = 0; n < limit; ++n) { + strBuf.push(String.fromCharCode(stream.getByte())); + } + var str = strBuf.join(""); + stream.pos = pos; + var index = backwards ? str.lastIndexOf(needle) : str.indexOf(needle); + if (index === -1) { + return false; /* not found */ + } + stream.pos += index; + return true; /* found */ + } + + var DocumentInfoValidators = { + get entries() { + // Lazily build this since all the validation functions below are not + // defined until after this file loads. + return shadow(this, "entries", { + Title: isString, + Author: isString, + Subject: isString, + Keywords: isString, + Creator: isString, + Producer: isString, + CreationDate: isString, + ModDate: isString, + Trapped: isName + }); + } + }; + + PDFDocument.prototype = { + parse: function PDFDocument_parse(recoveryMode) { + this.setup(recoveryMode); + var version = this.catalog.catDict.get("Version"); + if (isName(version)) { + this.pdfFormatVersion = version.name; + } + try { + // checking if AcroForm is present + this.acroForm = this.catalog.catDict.get("AcroForm"); + if (this.acroForm) { + this.xfa = this.acroForm.get("XFA"); + var fields = this.acroForm.get("Fields"); + if ( + (!fields || !isArray(fields) || fields.length === 0) && + !this.xfa + ) { + // no fields and no XFA -- not a form (?) + this.acroForm = null; + } + } + } catch (ex) { + info("Something wrong with AcroForm entry"); + this.acroForm = null; + } + }, + + get linearization() { + var linearization = null; + if (this.stream.length) { + try { + linearization = Linearization.create(this.stream); + } catch (err) { + if (err instanceof MissingDataException) { + throw err; + } + info(err); + } + } + // shadow the prototype getter with a data property + return shadow(this, "linearization", linearization); + }, + get startXRef() { + var stream = this.stream; + var startXRef = 0; + var linearization = this.linearization; + if (linearization) { + // Find end of first obj. + stream.reset(); + if (find(stream, "endobj", 1024)) { + startXRef = stream.pos + 6; + } + } else { + // Find startxref by jumping backward from the end of the file. + var step = 1024; + var found = false, + pos = stream.end; + while (!found && pos > 0) { + pos -= step - "startxref".length; + if (pos < 0) { + pos = 0; + } + stream.pos = pos; + found = find(stream, "startxref", step, true); + } + if (found) { + stream.skip(9); + var ch; + do { + ch = stream.getByte(); + } while (Lexer.isSpace(ch)); + var str = ""; + while (ch >= 0x20 && ch <= 0x39) { + // < '9' + str += String.fromCharCode(ch); + ch = stream.getByte(); + } + startXRef = parseInt(str, 10); + if (isNaN(startXRef)) { + startXRef = 0; + } + } + } + // shadow the prototype getter with a data property + return shadow(this, "startXRef", startXRef); + }, + get mainXRefEntriesOffset() { + var mainXRefEntriesOffset = 0; + var linearization = this.linearization; + if (linearization) { + mainXRefEntriesOffset = linearization.mainXRefEntriesOffset; + } + // shadow the prototype getter with a data property + return shadow(this, "mainXRefEntriesOffset", mainXRefEntriesOffset); + }, + // Find the header, remove leading garbage and setup the stream + // starting from the header. + checkHeader: function PDFDocument_checkHeader() { + var stream = this.stream; + stream.reset(); + if (find(stream, "%PDF-", 1024)) { + // Found the header, trim off any garbage before it. + stream.moveStart(); + // Reading file format version + var MAX_VERSION_LENGTH = 12; + var version = "", + ch; + while ((ch = stream.getByte()) > 0x20) { + // SPACE + if (version.length >= MAX_VERSION_LENGTH) { + break; + } + version += String.fromCharCode(ch); + } + if (!this.pdfFormatVersion) { + // removing "%PDF-"-prefix + this.pdfFormatVersion = version.substring(5); + } + return; + } + // May not be a PDF file, continue anyway. + }, + parseStartXRef: function PDFDocument_parseStartXRef() { + var startXRef = this.startXRef; + this.xref.setStartXRef(startXRef); + }, + setup: function PDFDocument_setup(recoveryMode) { + this.xref.parse(recoveryMode); + var self = this; + this.catalog = new Catalog(this.pdfManager, this.xref, false); + }, + get numPages() { + var linearization = this.linearization; + var num = linearization + ? linearization.numPages + : this.catalog.numPages; + // shadow the prototype getter + return shadow(this, "numPages", num); + }, + get documentInfo() { + var docInfo = { + PDFFormatVersion: this.pdfFormatVersion, + IsAcroFormPresent: !!this.acroForm, + IsXFAPresent: !!this.xfa + }; + var infoDict; + try { + infoDict = this.xref.trailer.get("Info"); + } catch (err) { + info("The document information dictionary is invalid."); + } + if (infoDict) { + var validEntries = DocumentInfoValidators.entries; + // Only fill the document info with valid entries from the spec. + for (var key in validEntries) { + if (infoDict.has(key)) { + var value = infoDict.get(key); + // Make sure the value conforms to the spec. + if (validEntries[key](value)) { + docInfo[key] = + typeof value !== "string" ? value : stringToPDFString(value); + } else { + info('Bad value in document info for "' + key + '"'); + } + } + } + } + return shadow(this, "documentInfo", docInfo); + }, + get fingerprint() { + var xref = this.xref, + hash, + fileID = ""; + var idArray = xref.trailer.get("ID"); + + if ( + idArray && + isArray(idArray) && + idArray[0] && + isString(idArray[0]) && + idArray[0] !== EMPTY_FINGERPRINT + ) { + hash = stringToBytes(idArray[0]); + } else { + if (this.stream.ensureRange) { + this.stream.ensureRange( + 0, + Math.min(FINGERPRINT_FIRST_BYTES, this.stream.end) + ); + } + hash = calculateMD5( + this.stream.bytes.subarray(0, FINGERPRINT_FIRST_BYTES), + 0, + FINGERPRINT_FIRST_BYTES + ); + } + + for (var i = 0, n = hash.length; i < n; i++) { + var hex = hash[i].toString(16); + fileID += hex.length === 1 ? "0" + hex : hex; + } + + return shadow(this, "fingerprint", fileID); + }, + + getPage: function PDFDocument_getPage(pageIndex) { + return this.catalog.getPage(pageIndex); + }, + + cleanup: function PDFDocument_cleanup() { + return this.catalog.cleanup(); + } + }; + + return PDFDocument; + })(); + + exports.PDFDocument = PDFDocument; +}); diff --git a/javascript/src/utilities/appUtility.js b/javascript/src/utilities/appUtility.js index c6090f2558c3a71d5edb8ee60efcb79fc49b27a1..f5f84f4a9340110d74c912f911a5b36f08453b72 100644 --- a/javascript/src/utilities/appUtility.js +++ b/javascript/src/utilities/appUtility.js @@ -1,6 +1,6 @@ -import { getCrypto } from 'pkijs'; +import { getCrypto } from "pkijs"; -export const createDeviceHash = async (publicKey) => { +export const createDeviceHash = async publicKey => { try { const stringToEncode = publicKey + navigator.userAgent; const crypto = getCrypto(); @@ -21,13 +21,14 @@ export const encodeResponse = (code, data, status) => { }; export function makeid(len) { - if (typeof len === 'undefined') { - len = 10 + if (typeof len === "undefined") { + len = 10; } - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let text = ""; + let possible = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - for (var i = 0; i < len; i++) + for (let i = 0; i < len; i++) text += possible.charAt(Math.floor(Math.random() * possible.length)); return text; @@ -49,25 +50,32 @@ export function getTimeLeftInLocalStorage() { let minutes = Math.floor((blockFinishTime - timeNow) / 60); minutes %= 60; - const left = "Your identity has been locked. Try again in " + minutes + " minutes and " + seconds + " seconds."; + const left = + "Your identity has been locked. Try again in " + + minutes + + " minutes and " + + seconds + + " seconds."; return left; } export function listIdentitiesFromLocalStorage() { - var serializedIdentitiesList = localStorage.getItem("identities"); - var identities = JSON.parse(serializedIdentitiesList); - var identitiesResult = {}; - - for(var key in identities) { - var profile = JSON.parse(JSON.stringify(localStorage.getItem("profiles/" + key))); - if(profile != null && profile !== "") { - identitiesResult[key] = JSON.parse(profile) + let serializedIdentitiesList = localStorage.getItem("identities"); + let identities = JSON.parse(serializedIdentitiesList); + let identitiesResult = {}; + + for (let key in identities) { + let profile = JSON.parse( + JSON.stringify(localStorage.getItem("profiles/" + key)) + ); + if (profile != null && profile !== "") { + identitiesResult[key] = JSON.parse(profile); } else { - identitiesResult[key] = {} + identitiesResult[key] = {}; } } - return identitiesResult + return identitiesResult; } export function destroyIdentityFromLocalStorage(key) { @@ -75,20 +83,20 @@ export function destroyIdentityFromLocalStorage(key) { localStorage.removeItem("profiles/" + key); localStorage.removeItem("colors/" + key); - var serializedIdentitiesList = localStorage.getItem("identities"); + let serializedIdentitiesList = localStorage.getItem("identities"); - var identities = JSON.parse(serializedIdentitiesList); + let identities = JSON.parse(serializedIdentitiesList); identities[key] = null; delete identities[key]; - localStorage.setItem("identities", JSON.stringify(identities)) + localStorage.setItem("identities", JSON.stringify(identities)); } export function failPincodeAttempt(password) { let message = "Wrong pincode"; - if (password !== '00000000') { + if (password !== "00000000") { let attempt = localStorage.getItem("attempt") || 1; attempt = parseInt(attempt); if (attempt === 9) { @@ -120,4 +128,4 @@ export function canTryPincode() { } else { return false; } -} \ No newline at end of file +} diff --git a/javascript/src/utilities/emailUtilities.js b/javascript/src/utilities/emailUtilities.js index 0b5f92035105c1cc3b693c62147471a0ae20c679..df59737b20636ffca4b5b5431c024be65ff6668f 100644 --- a/javascript/src/utilities/emailUtilities.js +++ b/javascript/src/utilities/emailUtilities.js @@ -1,6 +1,6 @@ import dataUriToBlob from "data-uri-to-blob"; -import libmime from 'libmime'; -import union from 'lodash/union'; +import libmime from "libmime"; +import union from "lodash/union"; import { fixNewLines, @@ -14,14 +14,16 @@ import { import { getCertificateChain } from "./signingUtilities"; const SIGNATURE_CONTENT_TYPE = "application/pkcs7-signature"; -export const DEFAULT_ATTACHMENT_NAME = 'attachment'; +export const DEFAULT_ATTACHMENT_NAME = "attachment"; const splitParticipants = participantsList => { if (!participantsList) { return []; } - const participants = participantsList.map(participants => participants.split(",").map(p => p.trim())); + const participants = participantsList.map(participants => + participants.split(",").map(p => p.trim()) + ); return union.apply(null, participants); }; @@ -84,14 +86,16 @@ export const parseSMIME = smimeString => { * @returns {string} ('file.txt') */ export const getFilenameFromHeaders = headers => { - const headersToSearch = ['content-type', 'content-disposition']; + const headersToSearch = ["content-type", "content-disposition"]; - const filename = headers && Object.keys(headers) - .filter(key => headersToSearch.includes(key)) - .reduce((result, key) => { - const headerValue = libmime.parseHeaderValue(headers[key][0]); - return result || headerValue.params.name || headerValue.params.filename; - }, ''); + const filename = + headers && + Object.keys(headers) + .filter(key => headersToSearch.includes(key)) + .reduce((result, key) => { + const headerValue = libmime.parseHeaderValue(headers[key][0]); + return result || headerValue.params.name || headerValue.params.filename; + }, ""); return filename || DEFAULT_ATTACHMENT_NAME; }; @@ -110,8 +114,9 @@ export const extractHtmlBodyFromString = string => { if (bodyMatch && bodyMatch[1]) { body = bodyMatch[1] - .replace(/>\s+</gm, '><') - .replace(/<!--[\s\S]*?-->/gm, '').trim() + .replace(/>\s+</gm, "><") + .replace(/<!--[\s\S]*?-->/gm, "") + .trim(); } return body; diff --git a/javascript/src/utilities/pdfUtilities.js b/javascript/src/utilities/pdfUtilities.js index f869fd153a022644976085d44b58e0cfeeff14b5..06ecb23b185e9f1a65acf9b4d9c1f2efe061e3c1 100644 --- a/javascript/src/utilities/pdfUtilities.js +++ b/javascript/src/utilities/pdfUtilities.js @@ -8,330 +8,350 @@ * http://www.opensource.org/licenses/MIT */ -import { - ObjectIdentifier, - UTCTime, - OctetString -} from 'asn1js'; - -import { - ContentInfo, - SignedData, - Attribute, - SignerInfo, - IssuerAndSerialNumber, - SignedAndUnsignedAttributes, - EncapsulatedContentInfo, - getCrypto -} from 'pkijs'; +import { ObjectIdentifier, UTCTime, OctetString } from "asn1js"; import { - parseCertificate, - parsePrivateKey, -} from './signingUtilities'; + ContentInfo, + SignedData, + Attribute, + SignerInfo, + IssuerAndSerialNumber, + SignedAndUnsignedAttributes, + EncapsulatedContentInfo, + getCrypto +} from "pkijs"; + +import { parseCertificate, parsePrivateKey } from "./signingUtilities"; //keep this import to include the patched version of pdfjs library -import {PDFJS} from "../lib/pdfjs.parser.js"; - +import { PDFJS } from "../lib/pdfjs.parser.js"; function createXrefTable(xrefEntries) { - xrefEntries = sortOnKeys(xrefEntries); - var retVal ='xref\n'; - var last = -2; - for(var i in xrefEntries) { - i = parseInt(i); - if(typeof xrefEntries[i].offset === 'undefined') { continue; } - retVal += calcFlow(i, last, xrefEntries); - var offset = xrefEntries[i].offset; - retVal += pad10(offset)+' '+pad5(xrefEntries[i].gen)+' '+(xrefEntries[i].free?'f':'n')+' \n'; - last = i; - } - return retVal; + xrefEntries = sortOnKeys(xrefEntries); + var retVal = "xref\n"; + var last = -2; + for (var i in xrefEntries) { + i = parseInt(i); + if (typeof xrefEntries[i].offset === "undefined") { + continue; + } + retVal += calcFlow(i, last, xrefEntries); + var offset = xrefEntries[i].offset; + retVal += + pad10(offset) + + " " + + pad5(xrefEntries[i].gen) + + " " + + (xrefEntries[i].free ? "f" : "n") + + " \n"; + last = i; + } + return retVal; } function calcFlow(i, last, xrefEntries) { - if(last + 1 === i) {return '';} - var count = 1; - while(typeof xrefEntries[(i+count)] !== 'undefined' - && typeof xrefEntries[(i+count)].offset !== 'undefined') {count ++;} - return i + ' '+count+'\n'; + if (last + 1 === i) { + return ""; + } + var count = 1; + while ( + typeof xrefEntries[i + count] !== "undefined" && + typeof xrefEntries[i + count].offset !== "undefined" + ) { + count++; + } + return i + " " + count + "\n"; } function createTrailer(topDict, startxref, sha256Hex, size, prev) { - var retVal ='trailer <<\n'; - retVal +=' /Size '+(size)+'\n'; - var refRoot = topDict.getRaw('Root'); - if(typeof refRoot !== 'undefined') { - retVal +=' /Root '+refRoot.num+' '+refRoot.gen+' R\n'; - } - var refInfo = topDict.getRaw('Info'); - if(typeof refInfo !== 'undefined') { - retVal +=' /Info '+refInfo.num+' '+refInfo.gen+' R\n'; - } - retVal +=' /ID [<'+sha256Hex.substring(0,32)+'><'+sha256Hex.substring(32,64)+'>]\n'; - if(typeof prev !== 'undefined' ) { - retVal +=' /Prev '+prev+'\n'; - } - retVal +='>>\n'; - retVal +='startxref\n'; - retVal +=startxref + '\n'; - retVal +='%%EOF\n'; - return retVal; + var retVal = "trailer <<\n"; + retVal += " /Size " + size + "\n"; + var refRoot = topDict.getRaw("Root"); + if (typeof refRoot !== "undefined") { + retVal += " /Root " + refRoot.num + " " + refRoot.gen + " R\n"; + } + var refInfo = topDict.getRaw("Info"); + if (typeof refInfo !== "undefined") { + retVal += " /Info " + refInfo.num + " " + refInfo.gen + " R\n"; + } + retVal += + " /ID [<" + + sha256Hex.substring(0, 32) + + "><" + + sha256Hex.substring(32, 64) + + ">]\n"; + if (typeof prev !== "undefined") { + retVal += " /Prev " + prev + "\n"; + } + retVal += ">>\n"; + retVal += "startxref\n"; + retVal += startxref + "\n"; + retVal += "%%EOF\n"; + return retVal; } function createXrefTableAppend(xrefEntries) { - xrefEntries = sortOnKeys(xrefEntries); - - var retVal ='xref\n'; - var last = -2; - for(var i in xrefEntries) { - i = parseInt(i); - if(typeof xrefEntries[i].offset === 'undefined') { continue; } - retVal += calcFlow(i, last, xrefEntries); - var offset = xrefEntries[i].offset; - retVal += pad10(offset)+' '+pad5(xrefEntries[i].gen)+' '+(xrefEntries[i].free?'f':'n')+' \n'; - last = i; - } - return retVal; + xrefEntries = sortOnKeys(xrefEntries); + + var retVal = "xref\n"; + var last = -2; + for (var i in xrefEntries) { + i = parseInt(i); + if (typeof xrefEntries[i].offset === "undefined") { + continue; + } + retVal += calcFlow(i, last, xrefEntries); + var offset = xrefEntries[i].offset; + retVal += + pad10(offset) + + " " + + pad5(xrefEntries[i].gen) + + " " + + (xrefEntries[i].free ? "f" : "n") + + " \n"; + last = i; + } + return retVal; } //http://stackoverflow.com/questions/10946880/sort-a-dictionary-or-whatever-key-value-data-structure-in-js-on-word-number-ke function sortOnKeys(dict) { - var sorted = []; - for(var key in dict) { - sorted[sorted.length] = key; - } - sorted.sort(); + var sorted = []; + for (var key in dict) { + sorted[sorted.length] = key; + } + sorted.sort(); - var tempDict = {}; - for(var i = 0; i < sorted.length; i++) { - tempDict[sorted[i]] = dict[sorted[i]]; - } + var tempDict = {}; + for (var i = 0; i < sorted.length; i++) { + tempDict[sorted[i]] = dict[sorted[i]]; + } - return tempDict; + return tempDict; } function removeFromArray(array, from, to) { - var cutlen = to - from; - var buf = new Uint8Array(array.length - cutlen); + var cutlen = to - from; + var buf = new Uint8Array(array.length - cutlen); - for (var i = 0; i < from; i++) { - buf[i] = array[i]; - } - for (var i = to, len = array.length; i < len; i++) { - buf[i-cutlen] = array[i]; - } - return buf; + for (var i = 0; i < from; i++) { + buf[i] = array[i]; + } + for (var i = to, len = array.length; i < len; i++) { + buf[i - cutlen] = array[i]; + } + return buf; } - + function findXrefBlocks(xrefBlocks) { - var num = xrefBlocks.length / 2; - var retVal = []; - for (var i=0;i<num;i++) { - retVal.push({start: xrefBlocks[i], end: xrefBlocks[i+num]}); - } - return retVal; + var num = xrefBlocks.length / 2; + var retVal = []; + for (var i = 0; i < num; i++) { + retVal.push({ start: xrefBlocks[i], end: xrefBlocks[i + num] }); + } + return retVal; } function convertUint8ArrayToBinaryString(u8Array) { - var i, len = u8Array.length, b_str = ""; - for (i=0; i<len; i++) { - b_str += String.fromCharCode(u8Array[i]); - } - return b_str; + var i, + len = u8Array.length, + b_str = ""; + for (i = 0; i < len; i++) { + b_str += String.fromCharCode(u8Array[i]); + } + return b_str; } function arrayObjectIndexOf(array, start, end, orig) { - for(var i = 0, len = array.length; i < len; i++) { - if ((array[i].start === start) && (array[i].end === end) && (array[i].orig === orig)) { - return i; - } + for (var i = 0, len = array.length; i < len; i++) { + if ( + array[i].start === start && + array[i].end === end && + array[i].orig === orig + ) { + return i; } - return -1; + } + return -1; } function pad10(num) { - var s = "000000000" + num; - return s.substr(s.length-10); + var s = "000000000" + num; + return s.substr(s.length - 10); } function pad5(num) { - var s = "0000" + num; - return s.substr(s.length-5); + var s = "0000" + num; + return s.substr(s.length - 5); } function pad2(num) { - var s = "0" + num; - return s.substr(s.length-2); + var s = "0" + num; + return s.substr(s.length - 2); } function findRootEntry(xref) { - var rootNr = xref.root.objId.substring(0, xref.root.objId.length - 1); - return xref.entries[rootNr]; + var rootNr = xref.root.objId.substring(0, xref.root.objId.length - 1); + return xref.entries[rootNr]; } - + function findSuccessorEntry(xrefEntries, current) { - //find it first - var currentOffset = current.offset; - var currentMin = Number.MAX_SAFE_INTEGER; - var currentMinIndex = -1; - for(var i in xrefEntries) { - if(xrefEntries[i].offset > currentOffset) { - if(xrefEntries[i].offset < currentMin) { - currentMin = xrefEntries[i].offset; - currentMinIndex = i; - } - } - } - if(currentMinIndex === -1) { - return current; + //find it first + var currentOffset = current.offset; + var currentMin = Number.MAX_SAFE_INTEGER; + var currentMinIndex = -1; + for (var i in xrefEntries) { + if (xrefEntries[i].offset > currentOffset) { + if (xrefEntries[i].offset < currentMin) { + currentMin = xrefEntries[i].offset; + currentMinIndex = i; + } } - return xrefEntries[currentMinIndex]; + } + if (currentMinIndex === -1) { + return current; + } + return xrefEntries[currentMinIndex]; } function updateArray(array, pos, str) { - var upd = stringToUint8Array(str); - for (var i = 0, len=upd.length; i < len; i++) { - array[i+pos] = upd[i]; - } - return array; + var upd = stringToUint8Array(str); + for (var i = 0, len = upd.length; i < len; i++) { + array[i + pos] = upd[i]; + } + return array; } function copyToEnd(array, from, to) { - var buf = new Uint8Array(array.length + (to - from)); - for (var i = 0, len=array.length; i < len; i++) { - buf[i] = array[i]; - } + var buf = new Uint8Array(array.length + (to - from)); + for (var i = 0, len = array.length; i < len; i++) { + buf[i] = array[i]; + } - for (var i = 0, len=(to - from); i < len; i++) { - buf[array.length + i] = array[from + i]; - } - return buf; + for (var i = 0, len = to - from; i < len; i++) { + buf[array.length + i] = array[from + i]; + } + return buf; } function insertIntoArray(array, pos, str) { - var ins = stringToUint8Array(str); - var buf = new Uint8Array(array.length + ins.length); - for (var i = 0; i < pos; i++) { - buf[i] = array[i]; - } - for (var i = 0; i < ins.length; i++) { - buf[pos+i] = ins[i]; - } - for (var i = pos; i < array.length; i++) { - buf[ins.length+i] = array[i]; - } - return buf; + var ins = stringToUint8Array(str); + var buf = new Uint8Array(array.length + ins.length); + for (var i = 0; i < pos; i++) { + buf[i] = array[i]; + } + for (var i = 0; i < ins.length; i++) { + buf[pos + i] = ins[i]; + } + for (var i = pos; i < array.length; i++) { + buf[ins.length + i] = array[i]; + } + return buf; } function stringToUint8Array(str) { - var buf = new Uint8Array(str.length); - for (var i=0, strLen=str.length; i<strLen; i++) { - buf[i] = str.charCodeAt(i); - } - return buf; + var buf = new Uint8Array(str.length); + for (var i = 0, strLen = str.length; i < strLen; i++) { + buf[i] = str.charCodeAt(i); + } + return buf; } function uint8ArrayToString(buf, from, to) { - if(typeof from !== 'undefined' && typeof to !== 'undefined') { - var s = ''; - for (var i=from; i<to; i++) { - s = s + String.fromCharCode(buf[i]); - } - return s; + if (typeof from !== "undefined" && typeof to !== "undefined") { + var s = ""; + for (var i = from; i < to; i++) { + s = s + String.fromCharCode(buf[i]); } - return String.fromCharCode.apply(null, buf); + return s; + } + return String.fromCharCode.apply(null, buf); } - - function findFreeXrefNr(xrefEntries, used) { - used = typeof used !== 'undefined' ? used : []; - var inc = used.length; - - for (var i=1;i<xrefEntries.length;i++) { - - var index = used.indexOf(i); - var entry = xrefEntries[""+i]; - if(index === -1 && (typeof entry === 'undefined' || entry.free)) { - return i; - } - if(index !== -1) { - inc--; - } + used = typeof used !== "undefined" ? used : []; + var inc = used.length; + + for (var i = 1; i < xrefEntries.length; i++) { + var index = used.indexOf(i); + var entry = xrefEntries["" + i]; + if (index === -1 && (typeof entry === "undefined" || entry.free)) { + return i; } - return xrefEntries.length + inc; + if (index !== -1) { + inc--; + } + } + return xrefEntries.length + inc; } function find(uint8, needle, start, limit) { - start = typeof start !== 'undefined' ? start : 0; - limit = typeof limit !== 'undefined' ? limit : Number.MAX_SAFE_INTEGER; - - var search = stringToUint8Array(needle); - var match = 0; - - for(var i=start;i<uint8.length && i<limit;i++) { - if(uint8[i] === search[match]) { - match++; - } else { - match = 0; - if(uint8[i] === search[match]) { - match++; - } - } - - if(match === search.length) { - return (i + 1) - match; - } + start = typeof start !== "undefined" ? start : 0; + limit = typeof limit !== "undefined" ? limit : Number.MAX_SAFE_INTEGER; + + var search = stringToUint8Array(needle); + var match = 0; + + for (var i = start; i < uint8.length && i < limit; i++) { + if (uint8[i] === search[match]) { + match++; + } else { + match = 0; + if (uint8[i] === search[match]) { + match++; + } + } + + if (match === search.length) { + return i + 1 - match; } - return -1; + } + return -1; } - + function findBackwards(uint8, needle, start, limit) { - start = typeof start !== 'undefined' ? start : uint8.length; - limit = typeof limit !== 'undefined' ? limit : Number.MAX_SAFE_INTEGER; - - var search = stringToUint8Array(needle); - var match = search.length - 1; - - for(var i=start;i>=0 && i<limit;i--) { - if(uint8[i] === search[match]) { - match--; - } else { - match = search.length - 1; - if(uint8[i] === search[match]) { - match--; - } - } - - if(match === 0) { - return i - 1; - } + start = typeof start !== "undefined" ? start : uint8.length; + limit = typeof limit !== "undefined" ? limit : Number.MAX_SAFE_INTEGER; + + var search = stringToUint8Array(needle); + var match = search.length - 1; + + for (var i = start; i >= 0 && i < limit; i--) { + if (uint8[i] === search[match]) { + match--; + } else { + match = search.length - 1; + if (uint8[i] === search[match]) { + match--; + } } - return -1; -} - -function strHex(s) { - var a = ""; - for( var i=0; i<s.length; i++ ) { - a = a + pad2(s.charCodeAt(i).toString(16)); + + if (match === 0) { + return i - 1; } - return a; + } + return -1; } +function strHex(s) { + var a = ""; + for (var i = 0; i < s.length; i++) { + a = a + pad2(s.charCodeAt(i).toString(16)); + } + return a; +} async function sha256(array) { - - const cryptoLib = getCrypto(); - const digestTmpBuf = await cryptoLib.digest({ name: "SHA-256" }, array); - const digestTmpArray = new Uint8Array(digestTmpBuf); - const digestTmpStr = uint8ArrayToString(digestTmpArray); - const sha256Hex = strHex(digestTmpStr); - return sha256Hex; + const cryptoLib = getCrypto(); + const digestTmpBuf = await cryptoLib.digest({ name: "SHA-256" }, array); + const digestTmpArray = new Uint8Array(digestTmpBuf); + const digestTmpStr = uint8ArrayToString(digestTmpArray); + const sha256Hex = strHex(digestTmpStr); + return sha256Hex; } function isSigInRoot(pdf) { - if (typeof pdf.acroForm === 'undefined') { + if (typeof pdf.acroForm === "undefined") { return false; } @@ -339,42 +359,42 @@ function isSigInRoot(pdf) { return false; } - return pdf.acroForm.get('SigFlags') === 3; + return pdf.acroForm.get("SigFlags") === 3; } function updateXrefOffset(xref, offset, offsetDelta) { - for(var i in xref.entries) { - if(xref.entries[i].offset >= offset) { - xref.entries[i].offset += offsetDelta; - } + for (var i in xref.entries) { + if (xref.entries[i].offset >= offset) { + xref.entries[i].offset += offsetDelta; } - for(var i in xref.xrefBlocks) { - if(xref.xrefBlocks[i] >= offset) { - xref.xrefBlocks[i] += offsetDelta; - } + } + for (var i in xref.xrefBlocks) { + if (xref.xrefBlocks[i] >= offset) { + xref.xrefBlocks[i] += offsetDelta; } + } } function updateXrefBlocks(xrefBlocks, offset, offsetDelta) { - for(var i in xrefBlocks) { - if(xrefBlocks[i].start >= offset) { - xrefBlocks[i].start += offsetDelta; - } - if(xrefBlocks[i].end >= offset) { - xrefBlocks[i].end += offsetDelta; - } + for (var i in xrefBlocks) { + if (xrefBlocks[i].start >= offset) { + xrefBlocks[i].start += offsetDelta; } + if (xrefBlocks[i].end >= offset) { + xrefBlocks[i].end += offsetDelta; + } + } } function updateOffset(pos, offset, offsetDelta) { - if(pos >= offset) { - return pos + offsetDelta; - } - return pos; + if (pos >= offset) { + return pos + offsetDelta; + } + return pos; } function round256(x) { - return (Math.ceil(x/256)*256) - 1; + return Math.ceil(x / 256) * 256 - 1; } /** @@ -392,385 +412,530 @@ function round256(x) { * mm shall be the absolute value of the offset from UT in minutes (00–59) */ function now(date) { - //date = typeof date !== 'undefined' ? date : new Date(); - var yyyy = date.getFullYear().toString(); - var MM = pad2(date.getMonth() + 1); - var dd = pad2(date.getDate()); - var hh = pad2(date.getHours()); - var mm = pad2(date.getMinutes()); - var ss = pad2(date.getSeconds()); - return yyyy + MM + dd+ hh + mm + ss + createOffset(date); + //date = typeof date !== 'undefined' ? date : new Date(); + var yyyy = date.getFullYear().toString(); + var MM = pad2(date.getMonth() + 1); + var dd = pad2(date.getDate()); + var hh = pad2(date.getHours()); + var mm = pad2(date.getMinutes()); + var ss = pad2(date.getSeconds()); + return yyyy + MM + dd + hh + mm + ss + createOffset(date); } function createOffset(date) { - var sign = (date.getTimezoneOffset() > 0) ? "-" : "+"; - var offset = Math.abs(date.getTimezoneOffset()); - var hours = pad2(Math.floor(offset / 60)); - var minutes = pad2(offset % 60); - return sign + hours + "'" + minutes; -} - -async function newSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey) { - try { - // {annotEntry} is the ref to the annot widget. If we enlarge the array, make sure all the offsets - // after the modification will be updated -> xref table and startxref - var annotEntry = findFreeXrefNr(pdf.xref.entries); - // we'll store all the modifications we make, as we need to adjust the offset in the PDF - var offsetForm = find(pdf.stream.bytes, '<<', root.offset, rootSuccessor.offset) + 2; - //first we need to find the root element and add the following: - // - // /AcroForm<</Fields[{annotEntry} 0 R] /SigFlags 3>> - // - var appendAcroForm = '/AcroForm<</Fields[' + annotEntry + ' 0 R] /SigFlags 3>>'; - //before we insert the acroform, we find the right place for annotentry - - //we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Contents[ - var pages = pdf.catalog.catDict.get('Pages'); - //get first page, we have hidden sig, so don't bother - var ref = pages.get('Kids')[0]; - var xref = pdf.xref.fetch(ref); - var offsetContentEnd = xref.get('#Contents_offset'); - //we now search backwards, this is safe as we don't expect user content here - var offsetContent = findBackwards(pdf.stream.bytes, '/Contents', offsetContentEnd); - var appendAnnots = '/Annots[' + annotEntry + ' 0 R]\n '; - - //now insert string into stream - var array = insertIntoArray(pdf.stream.bytes, offsetForm, appendAcroForm); - //recalculate the offsets in the xref table, only update those that are affected - updateXrefOffset(pdf.xref, offsetForm, appendAcroForm.length); - offsetContent = updateOffset(offsetContent, offsetForm, appendAcroForm.length); - - var array = insertIntoArray(array, offsetContent, appendAnnots); - updateXrefOffset(pdf.xref, offsetContent, appendAnnots.length); - offsetContent = -1; //not needed anymore, don't update when offset changes - - //Then add to the next free object (annotEntry) - //add right before the xref table or stream - //if its a table, place element before the xref table - // - // sigEntry is the ref to the signature content. Next we need the signature object - var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]); - - // - // {annotEntry} 0 obj - // <</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature)/V Y 0 R>> - // endobj - // - var append = annotEntry + ' 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature' + annotEntry + ')/V ' + sigEntry + ' 0 R>>\nendobj\n\n'; - - // we want the offset just before the last xref table or entry - var blocks = findXrefBlocks(pdf.xref.xrefBlocks); - var offsetAnnot = blocks[0].start; - array = insertIntoArray(array, offsetAnnot, append); - //no updateXrefOffset, as the next entry will be following - - // - // {sigEntry} 0 obj - // <</Contents <0481801e6d931d561563fb254e27c846e08325570847ed63d6f9e35 ... b2c8788a5> - // /Type/Sig/SubFilter/adbe.pkcs7.detached/Location(Ghent)/M(D:20120928104114+02'00') - // /ByteRange [A B C D]/Filter/Adobe.PPKLite/Reason(Test)/ContactInfo()>> - // endobj - // - - //the next entry goes below the above - var offsetSig = offsetAnnot + append.length; - - // Both {annotEntry} and {sigEntry} objects need to be added to the last xref table. The byte range needs - // to be adjusted. Since the signature will always be in a gap, use first an empty sig - // to check the size, add ~25% size, then calculate the signature and place in the empty - // space. - var start = sigEntry + ' 0 obj\n<</Contents <'; - var dummy = await sign_pki(signingCert, certificateChain, privateKey, stringToUint8Array('A'), date); - //TODO: Adobe thinks its important to have the right size, no idea why this is the case - var crypto = new Array(round256(dummy.length * 2)).join('0'); - var middle = '>\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:' + now(date) + '\')\n/ByteRange '; - var byteRange = '[0000000000 0000000000 0000000000 0000000000]'; - var end = '/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n'; - //all together - var append2 = start + crypto + middle + byteRange + end; - var offsetByteRange = start.length + crypto.length + middle.length; - - array = insertIntoArray(array, offsetSig, append2); - updateXrefOffset(pdf.xref, offsetAnnot, append2.length + append.length); - - //find the xref tables, remove them and also the EOF, as we'll write a new table - var xrefBlocks = findXrefBlocks(pdf.xref.xrefBlocks); - - for (var i in xrefBlocks) { - var oldSize = array.length; - array = removeFromArray(array, xrefBlocks[i].start, xrefBlocks[i].end); - var length = array.length - oldSize; - updateXrefOffset(pdf.xref, xrefBlocks[i].start, length); - - //check for %%EOF and remove it as well - var offsetEOF = find(array, '%%EOF', xrefBlocks[i].start, xrefBlocks[i].start + 20); - if (offsetEOF > 0) { - var lengthEOF = '%%EOF'.length; - array = removeFromArray(array, offsetEOF, offsetEOF + lengthEOF); - updateXrefOffset(pdf.xref, offsetEOF, -lengthEOF); - updateXrefBlocks(xrefBlocks, offsetEOF, -lengthEOF); - offsetAnnot = updateOffset(offsetAnnot, offsetEOF, -lengthEOF); - offsetSig = updateOffset(offsetSig, offsetEOF, -lengthEOF); - } - updateXrefBlocks(xrefBlocks, xrefBlocks[i].start, length); - offsetAnnot = updateOffset(offsetAnnot, xrefBlocks[i].start, length); - offsetSig = updateOffset(offsetSig, xrefBlocks[i].start, length); + var sign = date.getTimezoneOffset() > 0 ? "-" : "+"; + var offset = Math.abs(date.getTimezoneOffset()); + var hours = pad2(Math.floor(offset / 60)); + var minutes = pad2(offset % 60); + return sign + hours + "'" + minutes; +} + +async function newSig( + pdf, + root, + rootSuccessor, + date, + signingCert, + certificateChain, + privateKey +) { + try { + // {annotEntry} is the ref to the annot widget. If we enlarge the array, make sure all the offsets + // after the modification will be updated -> xref table and startxref + var annotEntry = findFreeXrefNr(pdf.xref.entries); + // we'll store all the modifications we make, as we need to adjust the offset in the PDF + var offsetForm = + find(pdf.stream.bytes, "<<", root.offset, rootSuccessor.offset) + 2; + //first we need to find the root element and add the following: + // + // /AcroForm<</Fields[{annotEntry} 0 R] /SigFlags 3>> + // + var appendAcroForm = + "/AcroForm<</Fields[" + annotEntry + " 0 R] /SigFlags 3>>"; + //before we insert the acroform, we find the right place for annotentry + + //we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Contents[ + var pages = pdf.catalog.catDict.get("Pages"); + //get first page, we have hidden sig, so don't bother + var ref = pages.get("Kids")[0]; + var xref = pdf.xref.fetch(ref); + var offsetContentEnd = xref.get("#Contents_offset"); + //we now search backwards, this is safe as we don't expect user content here + var offsetContent = findBackwards( + pdf.stream.bytes, + "/Contents", + offsetContentEnd + ); + var appendAnnots = "/Annots[" + annotEntry + " 0 R]\n "; + + //now insert string into stream + var array = insertIntoArray(pdf.stream.bytes, offsetForm, appendAcroForm); + //recalculate the offsets in the xref table, only update those that are affected + updateXrefOffset(pdf.xref, offsetForm, appendAcroForm.length); + offsetContent = updateOffset( + offsetContent, + offsetForm, + appendAcroForm.length + ); + + var array = insertIntoArray(array, offsetContent, appendAnnots); + updateXrefOffset(pdf.xref, offsetContent, appendAnnots.length); + offsetContent = -1; //not needed anymore, don't update when offset changes + + //Then add to the next free object (annotEntry) + //add right before the xref table or stream + //if its a table, place element before the xref table + // + // sigEntry is the ref to the signature content. Next we need the signature object + var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]); + + // + // {annotEntry} 0 obj + // <</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature)/V Y 0 R>> + // endobj + // + var append = + annotEntry + + " 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature" + + annotEntry + + ")/V " + + sigEntry + + " 0 R>>\nendobj\n\n"; + + // we want the offset just before the last xref table or entry + var blocks = findXrefBlocks(pdf.xref.xrefBlocks); + var offsetAnnot = blocks[0].start; + array = insertIntoArray(array, offsetAnnot, append); + //no updateXrefOffset, as the next entry will be following + + // + // {sigEntry} 0 obj + // <</Contents <0481801e6d931d561563fb254e27c846e08325570847ed63d6f9e35 ... b2c8788a5> + // /Type/Sig/SubFilter/adbe.pkcs7.detached/Location(Ghent)/M(D:20120928104114+02'00') + // /ByteRange [A B C D]/Filter/Adobe.PPKLite/Reason(Test)/ContactInfo()>> + // endobj + // + + //the next entry goes below the above + var offsetSig = offsetAnnot + append.length; + + // Both {annotEntry} and {sigEntry} objects need to be added to the last xref table. The byte range needs + // to be adjusted. Since the signature will always be in a gap, use first an empty sig + // to check the size, add ~25% size, then calculate the signature and place in the empty + // space. + var start = sigEntry + " 0 obj\n<</Contents <"; + var dummy = await sign_pki( + signingCert, + certificateChain, + privateKey, + stringToUint8Array("A"), + date + ); + //TODO: Adobe thinks its important to have the right size, no idea why this is the case + var crypto = new Array(round256(dummy.length * 2)).join("0"); + var middle = + ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:" + + now(date) + + "')\n/ByteRange "; + var byteRange = "[0000000000 0000000000 0000000000 0000000000]"; + var end = "/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n"; + //all together + var append2 = start + crypto + middle + byteRange + end; + var offsetByteRange = start.length + crypto.length + middle.length; + + array = insertIntoArray(array, offsetSig, append2); + updateXrefOffset(pdf.xref, offsetAnnot, append2.length + append.length); + + //find the xref tables, remove them and also the EOF, as we'll write a new table + var xrefBlocks = findXrefBlocks(pdf.xref.xrefBlocks); + + for (var i in xrefBlocks) { + var oldSize = array.length; + array = removeFromArray(array, xrefBlocks[i].start, xrefBlocks[i].end); + var length = array.length - oldSize; + updateXrefOffset(pdf.xref, xrefBlocks[i].start, length); + + //check for %%EOF and remove it as well + var offsetEOF = find( + array, + "%%EOF", + xrefBlocks[i].start, + xrefBlocks[i].start + 20 + ); + if (offsetEOF > 0) { + var lengthEOF = "%%EOF".length; + array = removeFromArray(array, offsetEOF, offsetEOF + lengthEOF); + updateXrefOffset(pdf.xref, offsetEOF, -lengthEOF); + updateXrefBlocks(xrefBlocks, offsetEOF, -lengthEOF); + offsetAnnot = updateOffset(offsetAnnot, offsetEOF, -lengthEOF); + offsetSig = updateOffset(offsetSig, offsetEOF, -lengthEOF); } - - var sha256Hex = await sha256(array); - - //add the new entries to the xref - pdf.xref.entries[annotEntry] = {offset: offsetAnnot, gen: 0, free: false}; - pdf.xref.entries[sigEntry] = {offset: offsetSig, gen: 0, free: false}; - - var xrefTable = createXrefTable(pdf.xref.entries); - //also empty entries count as in the PDF spec, page 720 (example) - xrefTable += createTrailer(pdf.xref.topDict, array.length, sha256Hex, pdf.xref.entries.length); - array = insertIntoArray(array, array.length, xrefTable); - - //since we consolidate, no prev! [adjust /Prev -> rawparsing + offset] - var from1 = 0; - var to1 = offsetSig + start.length; - var from2 = to1 + crypto.length; - var to2 = (array.length - from2) - 1; - var byteRange = '[' + pad10(from1) + ' ' + pad10(to1 - 1) + ' ' + pad10(from2 + 1) + ' ' + pad10(to2) + ']'; - array = updateArray(array, (offsetSig + offsetByteRange), byteRange); - var data = removeFromArray(array, to1 - 1, from2 + 1); - var crypto2 = await sign_pki(signingCert, certificateChain, privateKey, data.buffer, date); - array = updateArray(array, to1, crypto2); - return array; - } catch (err) { - throw new Error('Error creating new signature in PDF: ' + err); - } + updateXrefBlocks(xrefBlocks, xrefBlocks[i].start, length); + offsetAnnot = updateOffset(offsetAnnot, xrefBlocks[i].start, length); + offsetSig = updateOffset(offsetSig, xrefBlocks[i].start, length); + } + + var sha256Hex = await sha256(array); + + //add the new entries to the xref + pdf.xref.entries[annotEntry] = { offset: offsetAnnot, gen: 0, free: false }; + pdf.xref.entries[sigEntry] = { offset: offsetSig, gen: 0, free: false }; + + var xrefTable = createXrefTable(pdf.xref.entries); + //also empty entries count as in the PDF spec, page 720 (example) + xrefTable += createTrailer( + pdf.xref.topDict, + array.length, + sha256Hex, + pdf.xref.entries.length + ); + array = insertIntoArray(array, array.length, xrefTable); + + //since we consolidate, no prev! [adjust /Prev -> rawparsing + offset] + var from1 = 0; + var to1 = offsetSig + start.length; + var from2 = to1 + crypto.length; + var to2 = array.length - from2 - 1; + var byteRange = + "[" + + pad10(from1) + + " " + + pad10(to1 - 1) + + " " + + pad10(from2 + 1) + + " " + + pad10(to2) + + "]"; + array = updateArray(array, offsetSig + offsetByteRange, byteRange); + var data = removeFromArray(array, to1 - 1, from2 + 1); + var crypto2 = await sign_pki( + signingCert, + certificateChain, + privateKey, + data.buffer, + date + ); + array = updateArray(array, to1, crypto2); + return array; + } catch (err) { + throw new Error("Error creating new signature in PDF: " + err); + } } -async function appendSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey) { - try { - //copy root and the entry with contents to the end - var startRoot = pdf.stream.bytes.length + 1; - - var array = copyToEnd(pdf.stream.bytes, root.offset - 1, rootSuccessor.offset); - - //since we signed the first one, we know how the pdf has to look like: - var offsetAcroForm = find(array, '/AcroForm<</Fields', startRoot); - var endOffsetAcroForm = find(array, ']', offsetAcroForm); - - var annotEntry = findFreeXrefNr(pdf.xref.entries); - var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]); - - var appendAnnot = ' ' + annotEntry + ' 0 R'; - array = insertIntoArray(array, endOffsetAcroForm, appendAnnot); - - //we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Annots - var pages = pdf.catalog.catDict.get('Pages'); - //get first page, we have hidden sig, so don't bother - var contentRef = pages.get('Kids')[0]; - var xref = pdf.xref.fetch(contentRef); - var offsetAnnotEnd = xref.get('#Annots_offset'); - //we now search ], this is safe as we signed it previously - var endOffsetAnnot = find(array, ']', offsetAnnotEnd); - var xrefEntry = pdf.xref.getEntry(contentRef.num); - var xrefEntrySuccosser = findSuccessorEntry(pdf.xref.entries, xrefEntry); - var offsetAnnotRelative = endOffsetAnnot - xrefEntrySuccosser.offset; - var startContent = array.length; - array = copyToEnd(array, xrefEntry.offset, xrefEntrySuccosser.offset); - array = insertIntoArray(array, array.length + offsetAnnotRelative, appendAnnot); - - var startAnnot = array.length; - var append = annotEntry + ' 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature' + annotEntry + ')/V ' + sigEntry + ' 0 R>>\nendobj\n\n'; - array = insertIntoArray(array, startAnnot, append); - - var startSig = array.length; - var start = sigEntry + ' 0 obj\n<</Contents <'; - var dummy = await sign_pki(signingCert, certificateChain, privateKey, stringToUint8Array('A'), date); - //TODO: Adobe thinks its important to have the right size, no idea why this is the case - var crypto = new Array(round256(dummy.length * 2)).join('0'); - var middle = '>\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:' + now(date) + '\')\n/ByteRange '; - var byteRange = '[0000000000 0000000000 0000000000 0000000000]'; - var end = '/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n'; - //all together - var append2 = start + crypto + middle + byteRange + end; - array = insertIntoArray(array, startSig, append2); - - var sha256Hex = await sha256(array); - - var prev = pdf.xref.xrefBlocks[0]; - var startxref = array.length; - var xrefEntries = []; - xrefEntries[0] = {offset: 0, gen: 65535, free: true}; - xrefEntries[pdf.xref.topDict.getRaw('Root').num] = {offset: startRoot, gen: 0, free: false}; - xrefEntries[contentRef.num] = {offset: startContent, gen: 0, free: false}; - xrefEntries[annotEntry] = {offset: startAnnot, gen: 0, free: false}; - xrefEntries[sigEntry] = {offset: startSig, gen: 0, free: false}; - var xrefTable = createXrefTableAppend(xrefEntries); - xrefTable += createTrailer(pdf.xref.topDict, startxref, sha256Hex, xrefEntries.length, prev); - array = insertIntoArray(array, array.length, xrefTable); - - var from1 = 0; - var to1 = startSig + start.length; - var from2 = to1 + crypto.length; - var to2 = (array.length - from2) - 1; - var byteRange = '[' + pad10(from1) + ' ' + pad10(to1 - 1) + ' ' + pad10(from2 + 1) + ' ' + pad10(to2) + ']'; - - array = updateArray(array, from2 + middle.length, byteRange); - //now sign from1-to1 / from2-to2 and update byterange - - var data = removeFromArray(array, to1 - 1, from2 + 1); - var crypto2 = await sign_pki(signingCert, certificateChain, privateKey, data.buffer, date); - array = updateArray(array, to1, crypto2); - return array; - } catch (err) { - throw new Error('Error appending signature in PDF: ' + err); - } +async function appendSig( + pdf, + root, + rootSuccessor, + date, + signingCert, + certificateChain, + privateKey +) { + try { + //copy root and the entry with contents to the end + var startRoot = pdf.stream.bytes.length + 1; + + var array = copyToEnd( + pdf.stream.bytes, + root.offset - 1, + rootSuccessor.offset + ); + + //since we signed the first one, we know how the pdf has to look like: + var offsetAcroForm = find(array, "/AcroForm<</Fields", startRoot); + var endOffsetAcroForm = find(array, "]", offsetAcroForm); + + var annotEntry = findFreeXrefNr(pdf.xref.entries); + var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]); + + var appendAnnot = " " + annotEntry + " 0 R"; + array = insertIntoArray(array, endOffsetAcroForm, appendAnnot); + + //we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Annots + var pages = pdf.catalog.catDict.get("Pages"); + //get first page, we have hidden sig, so don't bother + var contentRef = pages.get("Kids")[0]; + var xref = pdf.xref.fetch(contentRef); + var offsetAnnotEnd = xref.get("#Annots_offset"); + //we now search ], this is safe as we signed it previously + var endOffsetAnnot = find(array, "]", offsetAnnotEnd); + var xrefEntry = pdf.xref.getEntry(contentRef.num); + var xrefEntrySuccosser = findSuccessorEntry(pdf.xref.entries, xrefEntry); + var offsetAnnotRelative = endOffsetAnnot - xrefEntrySuccosser.offset; + var startContent = array.length; + array = copyToEnd(array, xrefEntry.offset, xrefEntrySuccosser.offset); + array = insertIntoArray( + array, + array.length + offsetAnnotRelative, + appendAnnot + ); + + var startAnnot = array.length; + var append = + annotEntry + + " 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature" + + annotEntry + + ")/V " + + sigEntry + + " 0 R>>\nendobj\n\n"; + array = insertIntoArray(array, startAnnot, append); + + var startSig = array.length; + var start = sigEntry + " 0 obj\n<</Contents <"; + var dummy = await sign_pki( + signingCert, + certificateChain, + privateKey, + stringToUint8Array("A"), + date + ); + //TODO: Adobe thinks its important to have the right size, no idea why this is the case + var crypto = new Array(round256(dummy.length * 2)).join("0"); + var middle = + ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:" + + now(date) + + "')\n/ByteRange "; + var byteRange = "[0000000000 0000000000 0000000000 0000000000]"; + var end = "/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n"; + //all together + var append2 = start + crypto + middle + byteRange + end; + array = insertIntoArray(array, startSig, append2); + + var sha256Hex = await sha256(array); + + var prev = pdf.xref.xrefBlocks[0]; + var startxref = array.length; + var xrefEntries = []; + xrefEntries[0] = { offset: 0, gen: 65535, free: true }; + xrefEntries[pdf.xref.topDict.getRaw("Root").num] = { + offset: startRoot, + gen: 0, + free: false + }; + xrefEntries[contentRef.num] = { offset: startContent, gen: 0, free: false }; + xrefEntries[annotEntry] = { offset: startAnnot, gen: 0, free: false }; + xrefEntries[sigEntry] = { offset: startSig, gen: 0, free: false }; + var xrefTable = createXrefTableAppend(xrefEntries); + xrefTable += createTrailer( + pdf.xref.topDict, + startxref, + sha256Hex, + xrefEntries.length, + prev + ); + array = insertIntoArray(array, array.length, xrefTable); + + var from1 = 0; + var to1 = startSig + start.length; + var from2 = to1 + crypto.length; + var to2 = array.length - from2 - 1; + var byteRange = + "[" + + pad10(from1) + + " " + + pad10(to1 - 1) + + " " + + pad10(from2 + 1) + + " " + + pad10(to2) + + "]"; + + array = updateArray(array, from2 + middle.length, byteRange); + //now sign from1-to1 / from2-to2 and update byterange + + var data = removeFromArray(array, to1 - 1, from2 + 1); + var crypto2 = await sign_pki( + signingCert, + certificateChain, + privateKey, + data.buffer, + date + ); + array = updateArray(array, to1, crypto2); + return array; + } catch (err) { + throw new Error("Error appending signature in PDF: " + err); + } } function loadPdf(pdfArray) { - try { - var pdf = new pdfjsCoreDocument.PDFDocument(false, pdfArray, ''); - pdf.parseStartXRef(); - pdf.parse(); - return pdf; - } - catch(err) { - throw new Error('Error parsing PDF: ' + err); - } + try { + var pdf = new pdfjsCoreDocument.PDFDocument(false, pdfArray, ""); + pdf.parseStartXRef(); + pdf.parse(); + return pdf; + } catch (err) { + throw new Error("Error parsing PDF: " + err); + } } - //data must be Uint8Array async function sign_pki(signingCert, certificateChain, privateKey, data, date) { console.log("Calling sign_pki"); - const crypto = getCrypto(); + const crypto = getCrypto(); - //date = typeof date !== 'undefined' ? date : new Date(); + //date = typeof date !== 'undefined' ? date : new Date(); - const hashAlg = "SHA-256"; + const hashAlg = "SHA-256"; - const digest = await crypto.digest({ name: hashAlg }, data); + const digest = await crypto.digest({ name: hashAlg }, data); - const signedAttr = []; + const signedAttr = []; - signedAttr.push(new Attribute({ - type: "1.2.840.113549.1.9.3", - values: [ - new ObjectIdentifier({ value: "1.2.840.113549.1.7.1" }) - ] - })); // contentType + signedAttr.push( + new Attribute({ + type: "1.2.840.113549.1.9.3", + values: [new ObjectIdentifier({ value: "1.2.840.113549.1.7.1" })] + }) + ); // contentType - signedAttr.push(new Attribute({ - type: "1.2.840.113549.1.9.4", - values: [ - new OctetString({ valueHex: digest }) - ] - })); // messageDigest + signedAttr.push( + new Attribute({ + type: "1.2.840.113549.1.9.4", + values: [new OctetString({ valueHex: digest })] + }) + ); // messageDigest - signedAttr.push(new Attribute({ - type: "1.2.840.113549.1.9.5", - values: [ - new UTCTime({ valueDate: date }) - ] - })); // signingTime + signedAttr.push( + new Attribute({ + type: "1.2.840.113549.1.9.5", + values: [new UTCTime({ valueDate: date })] + }) + ); // signingTime - const cmsSignedSimpl = new SignedData({ + const cmsSignedSimpl = new SignedData({ + version: 1, + encapContentInfo: new EncapsulatedContentInfo({ + eContentType: "1.2.840.113549.1.7.1" // "data" content type + }), + signerInfos: [ + new SignerInfo({ version: 1, - encapContentInfo: new EncapsulatedContentInfo({ - eContentType: "1.2.840.113549.1.7.1" // "data" content type + sid: new IssuerAndSerialNumber({ + issuer: signingCert.issuer, + serialNumber: signingCert.serialNumber }), - signerInfos: [ - new SignerInfo({ - version: 1, - sid: new IssuerAndSerialNumber({ - issuer: signingCert.issuer, - serialNumber: signingCert.serialNumber - }), - signedAttrs: new SignedAndUnsignedAttributes({ - type: 0, - attributes: signedAttr - }) - }) - ], - certificates: certificateChain - }); + signedAttrs: new SignedAndUnsignedAttributes({ + type: 0, + attributes: signedAttr + }) + }) + ], + certificates: certificateChain + }); - const signatureBuffer = await cmsSignedSimpl.sign(privateKey, 0, hashAlg, data.buffer); + const signatureBuffer = await cmsSignedSimpl.sign( + privateKey, + 0, + hashAlg, + data.buffer + ); - const cmsSignedSchema = cmsSignedSimpl.toSchema(true); + const cmsSignedSchema = cmsSignedSimpl.toSchema(true); - const cmsContentSimp = new ContentInfo({ - contentType: "1.2.840.113549.1.7.2", - content: cmsSignedSchema - }); + const cmsContentSimp = new ContentInfo({ + contentType: "1.2.840.113549.1.7.2", + content: cmsSignedSchema + }); - const _cmsSignedSchema = cmsContentSimp.toSchema(); + const _cmsSignedSchema = cmsContentSimp.toSchema(); - //region Make length of some elements in "indefinite form" - _cmsSignedSchema.lenBlock.isIndefiniteForm = true; + //region Make length of some elements in "indefinite form" + _cmsSignedSchema.lenBlock.isIndefiniteForm = true; - const block1 = _cmsSignedSchema.valueBlock.value[1]; - block1.lenBlock.isIndefiniteForm = true; + const block1 = _cmsSignedSchema.valueBlock.value[1]; + block1.lenBlock.isIndefiniteForm = true; - const block2 = block1.valueBlock.value[0]; - block2.lenBlock.isIndefiniteForm = true; + const block2 = block1.valueBlock.value[0]; + block2.lenBlock.isIndefiniteForm = true; - //endregion + //endregion - let cmsSignedBuffer = _cmsSignedSchema.toBER(false); + let cmsSignedBuffer = _cmsSignedSchema.toBER(false); - const cmsSignedArray = new Uint8Array(cmsSignedBuffer); - const cmsSignedString = uint8ArrayToString(cmsSignedArray); - const hex = strHex(cmsSignedString); - return hex; + const cmsSignedArray = new Uint8Array(cmsSignedBuffer); + const cmsSignedString = uint8ArrayToString(cmsSignedArray); + const hex = strHex(cmsSignedString); + return hex; } - -async function signPdfObjects (pdfRaw, signingCert, certificateChain, privateKey, date) { +async function signPdfObjects( + pdfRaw, + signingCert, + certificateChain, + privateKey, + date +) { console.log("Calling signPdfObjects"); - date = ((typeof date !== 'undefined') && (date !== null)) ? date : new Date(); + date = typeof date !== "undefined" && date !== null ? date : new Date(); - if (pdfRaw instanceof Buffer) { - pdfRaw = new Uint8Array(pdfRaw); - } else - if(pdfRaw instanceof ArrayBuffer) { - pdfRaw = new Uint8Array(pdfRaw); - } + if (pdfRaw instanceof Buffer) { + pdfRaw = new Uint8Array(pdfRaw); + } else if (pdfRaw instanceof ArrayBuffer) { + pdfRaw = new Uint8Array(pdfRaw); + } console.log("Calling loadPdf"); - var pdf = loadPdf(pdfRaw); + var pdf = loadPdf(pdfRaw); console.log("Calling findRootEntry"); - var root = findRootEntry(pdf.xref); + var root = findRootEntry(pdf.xref); console.log("Calling findSuccessorEntry"); - var rootSuccessor = findSuccessorEntry(pdf.xref.entries, root); + var rootSuccessor = findSuccessorEntry(pdf.xref.entries, root); console.log("Calling isSigInRoot"); - if(!isSigInRoot(pdf)) { - console.log("Calling newSig"); - return await newSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey); - } else { - console.log("Calling appendSig"); - return await appendSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey); - } + if (!isSigInRoot(pdf)) { + console.log("Calling newSig"); + return await newSig( + pdf, + root, + rootSuccessor, + date, + signingCert, + certificateChain, + privateKey + ); + } else { + console.log("Calling appendSig"); + return await appendSig( + pdf, + root, + rootSuccessor, + date, + signingCert, + certificateChain, + privateKey + ); + } } - -export async function signPdf(pdfRaw, signingCert, certificateChain, privateKey) { - +export async function signPdf( + pdfRaw, + signingCert, + certificateChain, + privateKey +) { console.log("Calling signPdf"); const signingCertObj = parseCertificate(signingCert); const certificateChainObj = []; certificateChainObj[0] = parseCertificate(signingCert); for (let i = 0; i < certificateChain.length; i++) { - certificateChainObj[i + 1] = parseCertificate(certificateChain[i]) + certificateChainObj[i + 1] = parseCertificate(certificateChain[i]); } let privateKeyDecoded; - try { - console.log("Calling parsePrivateKey"); - privateKeyDecoded = await parsePrivateKey(privateKey); - } catch (e) { - console.log("Error decoding private key: ", e); - throw new Error('Error decoding private key: ' + e); - } + try { + console.log("Calling parsePrivateKey"); + privateKeyDecoded = await parsePrivateKey(privateKey); + } catch (e) { + console.log("Error decoding private key: ", e); + throw new Error("Error decoding private key: " + e); + } - return await signPdfObjects(pdfRaw, signingCertObj, certificateChainObj, privateKeyDecoded); -} \ No newline at end of file + return await signPdfObjects( + pdfRaw, + signingCertObj, + certificateChainObj, + privateKeyDecoded + ); +} diff --git a/javascript/src/utilities/signingUtilities.js b/javascript/src/utilities/signingUtilities.js index 760735c0fd79843b73453a638611dd1934332c72..27d23e4c4484f13701f2058cc1d4c974d0c507b2 100644 --- a/javascript/src/utilities/signingUtilities.js +++ b/javascript/src/utilities/signingUtilities.js @@ -1,13 +1,18 @@ -import {canTryPincode, failPincodeAttempt, getTimeLeftInLocalStorage, makeid} from './appUtility'; -import {bufferToHexCodes, stringToArrayBuffer} from 'pvutils'; -import {fromBER} from 'asn1js'; -import {ContentInfo, SignedData} from 'pkijs'; -import {algomap, rdnmap} from '../constants/certificates'; - -const libmime = require('libmime'); -const pkijs = require('pkijs'); -const asn1js = require('asn1js'); -const pvutils = require('pvutils'); +import { + canTryPincode, + failPincodeAttempt, + getTimeLeftInLocalStorage, + makeid +} from "./appUtility"; +import { bufferToHexCodes, stringToArrayBuffer } from "pvutils"; +import { fromBER } from "asn1js"; +import { ContentInfo, SignedData } from "pkijs"; +import { algomap, rdnmap } from "../constants/certificates"; + +const libmime = require("libmime"); +const pkijs = require("pkijs"); +const asn1js = require("asn1js"); +const pvutils = require("pvutils"); //********************************************************************************* @@ -15,36 +20,40 @@ const CERTIFIATE_Version_1 = 0; const CERTIFIATE_Version_3 = 2; //these bit fields are reversed, WTF! -const KEY_USAGE_DigitalSignature = 0x80;//01; -const KEY_USAGE_NonRepudiation = 0x40;//02; -const KEY_USAGE_KeyEncipherment = 0x20;//04; -const KEY_USAGE_DataEncipherment = 0x10;//08; -const KEY_USAGE_KeyAgreement = 0x08;//10; -const KEY_USAGE_KeyCertSign = 0x04;//20; -const KEY_USAGE_CRLSign = 0x02;//40; +const KEY_USAGE_DigitalSignature = 0x80; //01; +const KEY_USAGE_NonRepudiation = 0x40; //02; +const KEY_USAGE_KeyEncipherment = 0x20; //04; +const KEY_USAGE_DataEncipherment = 0x10; //08; +const KEY_USAGE_KeyAgreement = 0x08; //10; +const KEY_USAGE_KeyCertSign = 0x04; //20; +const KEY_USAGE_CRLSign = 0x02; //40; //const KEY_USAGE_EncipherOnly = 0x01;//80; // Not used for now. Must be used together with KEY_USAGE_KeyAgreement (maybe should be ORed as a constant directly?) //const KEY_USAGE_DecipherOnly = 0x80;//0100; // If used, modify "KeyUsage" extension array buffer size and appropriate bit operators to accomodate for extra byte -const KEY_USAGE_LeafCertificate = KEY_USAGE_DigitalSignature | KEY_USAGE_NonRepudiation | KEY_USAGE_KeyEncipherment | KEY_USAGE_DataEncipherment; -const KEY_USAGE_CertificateAuthority= KEY_USAGE_DigitalSignature | KEY_USAGE_KeyCertSign | KEY_USAGE_CRLSign; - - -const OID_EXT_KEY_USAGE_Any = "2.5.29.37.0"; -const OID_ID_PKIX_ServerAuth = "1.3.6.1.5.5.7.3.1"; -const OID_ID_PKIX_ClientAuth = "1.3.6.1.5.5.7.3.2"; -const OID_ID_PKIX_CodeSigning = "1.3.6.1.5.5.7.3.3"; -const OID_ID_PKIX_EmailProtection = "1.3.6.1.5.5.7.3.4"; -const OID_ID_PKIX_TimeStamping = "1.3.6.1.5.5.7.3.8"; -const OID_ID_PKIX_OCSPSigning = "1.3.6.1.5.5.7.3.9"; +const KEY_USAGE_LeafCertificate = + KEY_USAGE_DigitalSignature | + KEY_USAGE_NonRepudiation | + KEY_USAGE_KeyEncipherment | + KEY_USAGE_DataEncipherment; +const KEY_USAGE_CertificateAuthority = + KEY_USAGE_DigitalSignature | KEY_USAGE_KeyCertSign | KEY_USAGE_CRLSign; + +const OID_EXT_KEY_USAGE_Any = "2.5.29.37.0"; +const OID_ID_PKIX_ServerAuth = "1.3.6.1.5.5.7.3.1"; +const OID_ID_PKIX_ClientAuth = "1.3.6.1.5.5.7.3.2"; +const OID_ID_PKIX_CodeSigning = "1.3.6.1.5.5.7.3.3"; +const OID_ID_PKIX_EmailProtection = "1.3.6.1.5.5.7.3.4"; +const OID_ID_PKIX_TimeStamping = "1.3.6.1.5.5.7.3.8"; +const OID_ID_PKIX_OCSPSigning = "1.3.6.1.5.5.7.3.9"; // const OID_EXT_KEY_USAGE_MS... = "1.3.6.1.4.1.311.10.3.1"; // Microsoft Certificate Trust List signing // const OID_EXT_KEY_USAGE_MS... = "1.3.6.1.4.1.311.10.3.4"; // Microsoft Encrypted File System -const OID_PKCS7_Data = "1.2.840.113549.1.7.1"; -const OID_PKCS7_SignedData = "1.2.840.113549.1.7.2"; -const OID_PKCS7_EnvelopedData = "1.2.840.113549.1.7.3"; -const OID_PKCS9_EmailAddress = "1.2.840.113549.1.9.1"; -const OID_PKCS9_ContentType = "1.2.840.113549.1.9.3"; -const OID_PKCS9_MessageDigest = "1.2.840.113549.1.9.4"; -const OID_PKCS9_SigningTime = "1.2.840.113549.1.9.5"; +const OID_PKCS7_Data = "1.2.840.113549.1.7.1"; +const OID_PKCS7_SignedData = "1.2.840.113549.1.7.2"; +const OID_PKCS7_EnvelopedData = "1.2.840.113549.1.7.3"; +const OID_PKCS9_EmailAddress = "1.2.840.113549.1.9.1"; +const OID_PKCS9_ContentType = "1.2.840.113549.1.9.3"; +const OID_PKCS9_MessageDigest = "1.2.840.113549.1.9.4"; +const OID_PKCS9_SigningTime = "1.2.840.113549.1.9.5"; const defaultAlgorithms = { hashAlg: "SHA-256", @@ -52,7 +61,7 @@ const defaultAlgorithms = { keyLength: 2048 }; -const AES_encryptionVariant_Password = 2; +const AES_encryptionVariant_Password = 2; const encryptionAlgorithm = { name: "AES-CBC", length: 128 @@ -84,8 +93,11 @@ function generateKeys(algorithms) { } //region Get default algorithm parameters for key generation - const algorithm = pkijs.getAlgorithmParameters(algorithms.signAlg, "generatekey"); - if("hash" in algorithm.algorithm) { + const algorithm = pkijs.getAlgorithmParameters( + algorithms.signAlg, + "generatekey" + ); + if ("hash" in algorithm.algorithm) { algorithm.algorithm.hash.name = algorithms.hashAlg; } algorithm.algorithm.modulusLength = algorithms.keyLength; @@ -95,9 +107,7 @@ function generateKeys(algorithms) { } //********************************************************************************* -function createCertificate(certData, issuerData = null) -{ - +function createCertificate(certData, issuerData = null) { if (typeof certData === "undefined") { return Promise.reject("No Certificate data provided"); } @@ -106,7 +116,6 @@ function createCertificate(certData, issuerData = null) return Promise.reject("No Certificate subject data provided"); } - //region Get a "crypto" extension const crypto = pkijs.getCrypto(); @@ -122,35 +131,34 @@ function createCertificate(certData, issuerData = null) let publicKey; let privateKey; - let certificateBuffer;// = new ArrayBuffer(0); // ArrayBuffer with loaded or created CERT - let privateKeyBuffer;// = new ArrayBuffer(0); - let publicKeyBuffer;// = new ArrayBuffer(0); + let certificateBuffer; // = new ArrayBuffer(0); // ArrayBuffer with loaded or created CERT + let privateKeyBuffer; // = new ArrayBuffer(0); + let publicKeyBuffer; // = new ArrayBuffer(0); //endregion Initial variables if (certData.keyPair) { //region Create a new key pair - sequence = sequence.then(() => - { + sequence = sequence.then(() => { return certData.keyPair; }); //endregion Create a new key pair - } else { //region Create a new key pair - sequence = sequence.then(() => - { + sequence = sequence.then(() => { return generateKeys(certData.algorithms); }); //endregion Create a new key pair } //region Store new key in an interim variables - sequence = sequence.then(keyPair => - { - publicKey = keyPair.publicKey; - privateKey = keyPair.privateKey; - }, error => Promise.reject(`Error during key generation: ${error}`)); + sequence = sequence.then( + keyPair => { + publicKey = keyPair.publicKey; + privateKey = keyPair.privateKey; + }, + error => Promise.reject(`Error during key generation: ${error}`) + ); //endregion Store new key in an interim variables //region Exporting public key into "subjectPublicKeyInfo" value of certificate @@ -160,14 +168,16 @@ function createCertificate(certData, issuerData = null) //endregion Exporting public key into "subjectPublicKeyInfo" value of certificate sequence = sequence.then( - () => crypto.digest({ name: "SHA-1" }, certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex), + () => + crypto.digest( + { name: "SHA-1" }, + certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex + ), error => Promise.reject(`Error during importing public key: ${error}`) ); //region Fill in cert data - sequence = sequence.then(subjKeyIdBuffer => - { - + sequence = sequence.then(subjKeyIdBuffer => { //region Put a static values certificate.version = CERTIFIATE_Version_3; @@ -175,69 +185,92 @@ function createCertificate(certData, issuerData = null) const serialNumberView = new Uint8Array(serialNumberBuffer); pkijs.getRandomValues(serialNumberView); // noinspection JSUnresolvedFunction - certificate.serialNumber = new asn1js.Integer({ valueHex: serialNumberView }); + certificate.serialNumber = new asn1js.Integer({ + valueHex: serialNumberView + }); //endregion Put a static values //region Subject // For reference http://oidref.com/2.5.4.3 if (certData.subject.commonName) { // noinspection JSUnresolvedFunction - certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({ - type: "2.5.4.3", // Common name - value: new asn1js.PrintableString({ value: certData.subject.commonName }) - })); + certificate.subject.typesAndValues.push( + new pkijs.AttributeTypeAndValue({ + type: "2.5.4.3", // Common name + value: new asn1js.PrintableString({ + value: certData.subject.commonName + }) + }) + ); } if (certData.subject.country) { // noinspection JSUnresolvedFunction - certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({ - type: "2.5.4.6", // Country name - value: new asn1js.PrintableString({ value: certData.subject.country }) - })); + certificate.subject.typesAndValues.push( + new pkijs.AttributeTypeAndValue({ + type: "2.5.4.6", // Country name + value: new asn1js.PrintableString({ value: certData.subject.country }) + }) + ); } if (certData.subject.locality) { // noinspection JSUnresolvedFunction - certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({ - type: "2.5.4.7", // Locality Name - value: new asn1js.PrintableString({ value: certData.subject.locality }) - })); + certificate.subject.typesAndValues.push( + new pkijs.AttributeTypeAndValue({ + type: "2.5.4.7", // Locality Name + value: new asn1js.PrintableString({ + value: certData.subject.locality + }) + }) + ); } if (certData.subject.state) { // noinspection JSUnresolvedFunction - certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({ - type: "2.5.4.8", // State or Province name - value: new asn1js.PrintableString({ value: certData.subject.state }) - })); + certificate.subject.typesAndValues.push( + new pkijs.AttributeTypeAndValue({ + type: "2.5.4.8", // State or Province name + value: new asn1js.PrintableString({ value: certData.subject.state }) + }) + ); } if (certData.subject.organization) { // noinspection JSUnresolvedFunction - certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({ - type: "2.5.4.10", // Organization name - value: new asn1js.PrintableString({ value: certData.subject.organization }) - })); + certificate.subject.typesAndValues.push( + new pkijs.AttributeTypeAndValue({ + type: "2.5.4.10", // Organization name + value: new asn1js.PrintableString({ + value: certData.subject.organization + }) + }) + ); } if (certData.subject.organizationUnit) { // noinspection JSUnresolvedFunction - certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({ - type: "2.5.4.11", // Organization unit name - value: new asn1js.PrintableString({ value: certData.subject.organizationUnit }) - })); + certificate.subject.typesAndValues.push( + new pkijs.AttributeTypeAndValue({ + type: "2.5.4.11", // Organization unit name + value: new asn1js.PrintableString({ + value: certData.subject.organizationUnit + }) + }) + ); } if (certData.subject.email) { // noinspection JSUnresolvedFunction - certificate.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue({ - type: OID_PKCS9_EmailAddress, // Email, deprecated but still widely used - value: new asn1js.IA5String({ value: certData.subject.email }) - })); + certificate.subject.typesAndValues.push( + new pkijs.AttributeTypeAndValue({ + type: OID_PKCS9_EmailAddress, // Email, deprecated but still widely used + value: new asn1js.IA5String({ value: certData.subject.email }) + }) + ); } //endregion Subject - //region Issuer if (issuerData && issuerData.certificate) { certificate.issuer = issuerData.certificate.subject; @@ -248,14 +281,21 @@ function createCertificate(certData, issuerData = null) //region Validity if (!certData.validity) { - certData.validity = {} + certData.validity = {}; } if (certData.validity.notBefore) { certificate.notBefore.value = certData.validity.notBefore; //date } else { const tmp = new Date(); - certificate.notBefore.value = new Date(tmp.getFullYear(), tmp.getMonth(), tmp.getDate(), 0, 0, 0); + certificate.notBefore.value = new Date( + tmp.getFullYear(), + tmp.getMonth(), + tmp.getDate(), + 0, + 0, + 0 + ); } if (certData.validity.notAfter) { @@ -263,7 +303,14 @@ function createCertificate(certData, issuerData = null) } else { const tmp = certificate.notBefore.value; const validYears = certData.validity.validYears || 1; - certificate.notAfter.value = new Date(tmp.getFullYear() + validYears, tmp.getMonth(), tmp.getDate(), 23, 59, 59); + certificate.notAfter.value = new Date( + tmp.getFullYear() + validYears, + tmp.getMonth(), + tmp.getDate(), + 23, + 59, + 59 + ); } //endregion Validity @@ -272,94 +319,106 @@ function createCertificate(certData, issuerData = null) //region "BasicConstraints" extension const basicConstr = new pkijs.BasicConstraints({ - cA: !!certData.isCA, + cA: !!certData.isCA //pathLenConstraint: 0 //TODO add logic for leaf CA }); - certificate.extensions.push(new pkijs.Extension({ - extnID: "2.5.29.19", - critical: true, - extnValue: basicConstr.toSchema().toBER(false), - parsedValue: basicConstr // Parsed value for well-known extensions - })); + certificate.extensions.push( + new pkijs.Extension({ + extnID: "2.5.29.19", + critical: true, + extnValue: basicConstr.toSchema().toBER(false), + parsedValue: basicConstr // Parsed value for well-known extensions + }) + ); //endregion "BasicConstraints" extension //region "KeyUsage" extension const keyUsageBuffer = new ArrayBuffer(1); const keyUsageBitView = new Uint8Array(keyUsageBuffer); - keyUsageBitView[0] = !!certData.isCA ? KEY_USAGE_CertificateAuthority : KEY_USAGE_LeafCertificate; + keyUsageBitView[0] = !!certData.isCA + ? KEY_USAGE_CertificateAuthority + : KEY_USAGE_LeafCertificate; // noinspection JSUnresolvedFunction const keyUsage = new asn1js.BitString({ valueHex: keyUsageBuffer }); - certificate.extensions.push(new pkijs.Extension({ - extnID: "2.5.29.15", - critical: true, - extnValue: keyUsage.toBER(false), - parsedValue: keyUsage // Parsed value for well-known extensions - })); + certificate.extensions.push( + new pkijs.Extension({ + extnID: "2.5.29.15", + critical: true, + extnValue: keyUsage.toBER(false), + parsedValue: keyUsage // Parsed value for well-known extensions + }) + ); //endregion "KeyUsage" extension //region "ExtKeyUsage" extension if (!certData.isCA && certData.subject.email) { const extKeyUsage = new pkijs.ExtKeyUsage({ - keyPurposes: [ - OID_ID_PKIX_EmailProtection - ] + keyPurposes: [OID_ID_PKIX_EmailProtection] }); - certificate.extensions.push(new pkijs.Extension({ - extnID: "2.5.29.37", - critical: false, - extnValue: extKeyUsage.toSchema().toBER(false), - parsedValue: extKeyUsage // Parsed value for well-known extensions - })); + certificate.extensions.push( + new pkijs.Extension({ + extnID: "2.5.29.37", + critical: false, + extnValue: extKeyUsage.toSchema().toBER(false), + parsedValue: extKeyUsage // Parsed value for well-known extensions + }) + ); } //endregion "ExtKeyUsage" extension //region "SubjAltName" extension if (certData.subject.email || certData.subject.url) { - const names = []; if (certData.subject.email) { - names.push(new pkijs.GeneralName({ - type: 1, // rfc822Name - value: certData.subject.email - })); + names.push( + new pkijs.GeneralName({ + type: 1, // rfc822Name + value: certData.subject.email + }) + ); } if (certData.subject.url) { - names.push(new pkijs.GeneralName({ - type: 2, // dNSName - value: certData.subject.url - })); + names.push( + new pkijs.GeneralName({ + type: 2, // dNSName + value: certData.subject.url + }) + ); } const subjAltNames = new pkijs.GeneralNames({ names: names }); - certificate.extensions.push(new pkijs.Extension({ - extnID: "2.5.29.17", - critical: false, - extnValue: subjAltNames.toSchema().toBER(false), - parsedValue: subjAltNames // Parsed value for well-known extensions - })); + certificate.extensions.push( + new pkijs.Extension({ + extnID: "2.5.29.17", + critical: false, + extnValue: subjAltNames.toSchema().toBER(false), + parsedValue: subjAltNames // Parsed value for well-known extensions + }) + ); } //endregion "SubjAltName" extension - //region "SubjectKeyIdentifier" extension const subjKeyId = new asn1js.OctetString({ valueHex: subjKeyIdBuffer }); - certificate.extensions.push(new pkijs.Extension({ - extnID: "2.5.29.14", - critical: false, - extnValue: subjKeyId.toBER(false), - parsedValue: subjKeyId // Parsed value for well-known extensions - })); + certificate.extensions.push( + new pkijs.Extension({ + extnID: "2.5.29.14", + critical: false, + extnValue: subjKeyId.toBER(false), + parsedValue: subjKeyId // Parsed value for well-known extensions + }) + ); //endregion "SubjectKeyIdentifier" extension /* COULD NOT GET IT WORKING @@ -369,7 +428,7 @@ function createCertificate(certData, issuerData = null) let issuerSubjKeyExt = null; let extLength = issuerData.certificate.extensions.length; - for (var i = 0; i < extLength; i++) { + for (let i = 0; i < extLength; i++) { let ext = issuerData.certificate.extensions[i]; if (ext.extnID == "2.5.29.14") { issuerSubjKeyExt = ext; @@ -407,50 +466,60 @@ function createCertificate(certData, issuerData = null) }); //region Fill in cert data - //region Signing final certificate - sequence = sequence.then(() => { - let signerKey = (issuerData && issuerData.privateKey) ? issuerData.privateKey : privateKey; - return certificate.sign(signerKey, (certData.algorithms && certData.algorithms.hashAlg) ? certData.algorithms.hashAlg : defaultAlgorithms.hashAlg) + sequence = sequence.then( + () => { + let signerKey = + issuerData && issuerData.privateKey + ? issuerData.privateKey + : privateKey; + return certificate.sign( + signerKey, + certData.algorithms && certData.algorithms.hashAlg + ? certData.algorithms.hashAlg + : defaultAlgorithms.hashAlg + ); }, - error => Promise.reject(`Error during exporting public key: ${error}`)); + error => Promise.reject(`Error during exporting public key: ${error}`) + ); //endregion //region Encode and store certificate - sequence = sequence.then(() => - { - certificateBuffer = certificate.toSchema(true).toBER(false); - }, error => Promise.reject(`Error during signing: ${error}`)); + sequence = sequence.then( + () => { + certificateBuffer = certificate.toSchema(true).toBER(false); + }, + error => Promise.reject(`Error during signing: ${error}`) + ); //endregion //region Exporting public key - sequence = sequence.then(() => - crypto.exportKey("spki", publicKey) - ); + sequence = sequence.then(() => crypto.exportKey("spki", publicKey)); //endregion //region Store exported public key on Web page - sequence = sequence.then(result => - { - publicKeyBuffer = result; - }, error => Promise.reject(`Error during exporting of public key: ${error}`)); + sequence = sequence.then( + result => { + publicKeyBuffer = result; + }, + error => Promise.reject(`Error during exporting of public key: ${error}`) + ); //endregion //region Exporting private key - sequence = sequence.then(() => - crypto.exportKey("pkcs8", privateKey) - ); + sequence = sequence.then(() => crypto.exportKey("pkcs8", privateKey)); //endregion //region Store exported key on Web page - sequence = sequence.then(result => - { - privateKeyBuffer = result; - }, error => Promise.reject(`Error during exporting of private key: ${error}`)); + sequence = sequence.then( + result => { + privateKeyBuffer = result; + }, + error => Promise.reject(`Error during exporting of private key: ${error}`) + ); //endregion return sequence.then(() => { - const result = { certificate: certificate, certificatePEM: encodePEM(certificateBuffer, "CERTIFICATE"), @@ -470,7 +539,7 @@ function formatPEM(pemString) { let piece; while ((piece = pemString.substring(start, start + lineWidth)).length > 0) { start += lineWidth; - resultString += piece + '\r\n'; + resultString += piece + "\r\n"; } return resultString; } @@ -487,7 +556,10 @@ function encodePEM(buffer, label) { } function decodePEM(pemString) { - const pemStripped = pemString.replace(/(-----(BEGIN|END) [a-zA-Z ]*-----|\r|\n)/g, ''); + const pemStripped = pemString.replace( + /(-----(BEGIN|END) [a-zA-Z ]*-----|\r|\n)/g, + "" + ); const pemDecoded = window.atob(pemStripped); const buffer = pvutils.stringToArrayBuffer(pemDecoded); return buffer; @@ -507,18 +579,21 @@ export function encryptMessage(message, password, label) { const secret = pvutils.stringToArrayBuffer(password); const enveloped = new pkijs.EnvelopedData(); - enveloped.addRecipientByPreDefinedData(secret, {}, AES_encryptionVariant_Password); - return enveloped.encrypt(encryptionAlgorithm, buffer). - then( + enveloped.addRecipientByPreDefinedData( + secret, + {}, + AES_encryptionVariant_Password + ); + return enveloped.encrypt(encryptionAlgorithm, buffer).then( () => { const content = new pkijs.ContentInfo(); content.contentType = OID_PKCS7_EnvelopedData; content.content = enveloped.toSchema(); const ber = content.toSchema().toBER(false); - return encodePEM(ber, label) + return encodePEM(ber, label); }, error => Promise.reject(`encryption error: ${error}`) - ) + ); } //********************************************************************************* @@ -528,13 +603,16 @@ export function decryptMessage(message, password) { const buffer = decodePEM(message); const asn1 = asn1js.fromBER(buffer); - const content = new pkijs.ContentInfo({schema: asn1.result}); - const enveloped = new pkijs.EnvelopedData({schema: content.content}); - return enveloped.decrypt(0, {preDefinedData: secret}).then(result => { - return pvutils.arrayBufferToString(result); - }).catch(() => { - return Promise.reject(failPincodeAttempt(password)); - }); + const content = new pkijs.ContentInfo({ schema: asn1.result }); + const enveloped = new pkijs.EnvelopedData({ schema: content.content }); + return enveloped + .decrypt(0, { preDefinedData: secret }) + .then(result => { + return pvutils.arrayBufferToString(result); + }) + .catch(() => { + return Promise.reject(failPincodeAttempt(password)); + }); } else { return Promise.reject(getTimeLeftInLocalStorage()); } @@ -547,9 +625,10 @@ export function parsePrivateKey(privateKeyPEM) { const privateKeyPromise = crypto.importKey( "pkcs8", privateKeyBuffer, - { //these are the algorithm options + { + //these are the algorithm options name: "RSASSA-PKCS1-v1_5", - hash: {name: "SHA-256"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512" + hash: { name: "SHA-256" } //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512" }, true, ["sign"] @@ -557,7 +636,6 @@ export function parsePrivateKey(privateKeyPEM) { return privateKeyPromise; } - export function createPassportCertificate(commonNameArg) { const certData = { algorithms: { @@ -572,7 +650,7 @@ export function createPassportCertificate(commonNameArg) { locality: "Zug", //optional for leaf, recommended for CA state: "Zug", //optional for leaf, recommended for CA organization: "Vereign AG", //optional for leaf, recommended for CA - organizationUnit:"Business Dep", //optional for leaf, recommended for CA + organizationUnit: "Business Dep" //optional for leaf, recommended for CA //email: "damyan.mitev@vereign.com", // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection //url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension }, @@ -584,13 +662,18 @@ export function createPassportCertificate(commonNameArg) { isCA: true // optional flag denoting if this is CA certificate or leaf certificate, defaults to false }; - return createCertificate(certData, null) + return createCertificate(certData, null); } -export function createOneTimePassportCertificate(commonNameArg, emailArg, privateKeyIssuerArg, certicateIssuerArg) { - var certData = null; - if(emailArg != null && emailArg == "") { - emailArg = null; +export function createOneTimePassportCertificate( + commonNameArg, + emailArg, + privateKeyIssuerArg, + certicateIssuerArg +) { + let certData = null; + if (emailArg != null && emailArg == "") { + emailArg = null; } certData = { @@ -606,8 +689,8 @@ export function createOneTimePassportCertificate(commonNameArg, emailArg, privat locality: "Zug", //optional for leaf, recommended for CA state: "Zug", //optional for leaf, recommended for CA organization: "Vereign AG", //optional for leaf, recommended for CA - organizationUnit:"Business Dep", //optional for leaf, recommended for CA - email: emailArg, // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection + organizationUnit: "Business Dep", //optional for leaf, recommended for CA + email: emailArg // added to DN and Subject Alternative Name extension. Optional for CA. Mandatory for leaf certificate, used for email protection //url: "www.vereign.com" // optional url, recommended for CA, added to Subject Alternative Name extension }, validity: { @@ -616,12 +699,11 @@ export function createOneTimePassportCertificate(commonNameArg, emailArg, privat validYears: 5 //optional, defaults to 1 }, isCA: false // optional flag denoting if this is CA certificate or leaf certificate, defaults to false - } - + }; return parsePrivateKey(privateKeyIssuerArg).then(privateKeyDecoded => { const issuerData = { - certificate: parseCertificate(certicateIssuerArg),// vereignCACertPEM), + certificate: parseCertificate(certicateIssuerArg), // vereignCACertPEM), privateKey: privateKeyDecoded }; return createCertificate(certData, issuerData); @@ -639,16 +721,20 @@ export function signEmail(mime, signingCert, certificateChain, privateKey) { const certificateChainObj = []; certificateChainObj[0] = parseCertificate(signingCert); for (let i = 0; i < certificateChain.length; i++) { - certificateChainObj[i + 1] = parseCertificate(certificateChain[i]) + certificateChainObj[i + 1] = parseCertificate(certificateChain[i]); } return parsePrivateKey(privateKey).then(privateKeyDecoded => { - return signEmailObjects(mime, signingCertObj, certificateChainObj, privateKeyDecoded); + return signEmailObjects( + mime, + signingCertObj, + certificateChainObj, + privateKeyDecoded + ); }); } function signEmailObjects(mime, signingCert, certificateChain, privateKey) { - //region Get a "crypto" extension const crypto = pkijs.getCrypto(); if (typeof crypto === "undefined") { @@ -656,8 +742,7 @@ function signEmailObjects(mime, signingCert, certificateChain, privateKey) { } //endregion Get a "crypto" extension - let template = - `{{headers}}Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha-256; boundary="{{boundary}}" + let template = `{{headers}}Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg=sha-256; boundary="{{boundary}}" MIME-Version: 1.0 This is a cryptographically signed message in MIME format. @@ -674,14 +759,14 @@ Content-Description: S/MIME Cryptographic Signature --{{boundary}}-- Vereign - Authentic Communication -`.replace(newline, '\r\n'); +`.replace(newline, "\r\n"); const detachedSignature = true; const addExt = true; const hashAlg = "SHA-256"; let cmsSignedSimpl; - var mimeHeadersTitles = [ + let mimeHeadersTitles = [ "Content-Type", "Content-Transfer-Encoding", "Content-ID", @@ -691,71 +776,66 @@ Vereign - Authentic Communication "Content-Location" ]; - mime = mime.replace(newline, '\r\n'); + mime = mime.replace(newline, "\r\n"); let newHeaderLines = ""; - let headersEnd = mime.indexOf('\r\n\r\n'); //the first empty line - - if (headersEnd < 0 && mime.startsWith('\r\n')) { - - mime = mime.substring(2) //should not happen - - } else - if (headersEnd >= 0) { + let headersEnd = mime.indexOf("\r\n\r\n"); //the first empty line + if (headersEnd < 0 && mime.startsWith("\r\n")) { + mime = mime.substring(2); //should not happen + } else if (headersEnd >= 0) { let mimeHeaders = {}; let mimeBody = mime.substring(headersEnd + 4); let mimeHeadersStr = mime.substring(0, headersEnd); let headers = libmime.decodeHeaders(mimeHeadersStr); - for (var i = 0; i < mimeHeadersTitles.length; i++) { + for (let i = 0; i < mimeHeadersTitles.length; i++) { let key = mimeHeadersTitles[i].toLowerCase(); - if(key in headers) { + if (key in headers) { mimeHeaders[key] = headers[key]; - delete headers[key] + delete headers[key]; } } - for(let key in headers) { - if(!(key === "" || key === "MIME-Version".toLowerCase())) { //we have MIME-Version in the template - newHeaderLines += capitalizeHeader(key) + ": " + headers[key] + '\r\n'; + for (let key in headers) { + if (!(key === "" || key === "MIME-Version".toLowerCase())) { + //we have MIME-Version in the template + newHeaderLines += capitalizeHeader(key) + ": " + headers[key] + "\r\n"; } } let newMimeHeaderLines = ""; - for(let key in mimeHeaders) { - if(!(key === "")) { - newMimeHeaderLines += capitalizeHeader(key) + ": " + mimeHeaders[key] + '\r\n'; + for (let key in mimeHeaders) { + if (!(key === "")) { + newMimeHeaderLines += + capitalizeHeader(key) + ": " + mimeHeaders[key] + "\r\n"; } } if (newMimeHeaderLines === "") { - newMimeHeaderLines = 'Content-Type: text/plain\r\n' //should not happen + newMimeHeaderLines = "Content-Type: text/plain\r\n"; //should not happen } - mime = newMimeHeaderLines + '\r\n' + mimeBody + mime = newMimeHeaderLines + "\r\n" + mimeBody; } - let dataBuffer = Buffer.from(mime,'utf-8'); + let dataBuffer = Buffer.from(mime, "utf-8"); let sequence = Promise.resolve(); //region Check if user wants us to include signed extensions - if(addExt) - { + if (addExt) { //region Create a message digest - sequence = sequence.then( - () => crypto.digest({ name: hashAlg }, dataBuffer) + sequence = sequence.then(() => + crypto.digest({ name: hashAlg }, dataBuffer) ); //endregion //region Combine all signed extensions - sequence = sequence.then( - messageHash => - { - const signedAttr = []; - /* + sequence = sequence.then(messageHash => { + const signedAttr = []; + /* 1.2.840.113549.1.9.1 - e-mailAddress 1.2.840.113549.1.9.2 - PKCS-9 unstructuredName 1.2.840.113549.1.9.3 - contentType @@ -763,10 +843,11 @@ Vereign - Authentic Communication 1.2.840.113549.1.9.5 - Signing Time 1.2.840.113549.1.9.6 - counterSignature */ - signedAttr.push(new pkijs.Attribute({ + signedAttr.push( + new pkijs.Attribute({ type: OID_PKCS9_ContentType, //contentType values: [ - new asn1js.ObjectIdentifier({ value: OID_PKCS7_Data}) //data + new asn1js.ObjectIdentifier({ value: OID_PKCS7_Data }) //data ] /* 1.2.840.113549.1.7.1 - data @@ -776,78 +857,74 @@ Vereign - Authentic Communication 1.2.840.113549.1.7.5 - digestedData 1.2.840.113549.1.7.6 - encryptedData */ - })); // contentType + }) + ); // contentType - signedAttr.push(new pkijs.Attribute({ + signedAttr.push( + new pkijs.Attribute({ type: OID_PKCS9_SigningTime, //Signing Time - values: [ - new asn1js.UTCTime({ valueDate: new Date() }) - ] - })); // signingTime + values: [new asn1js.UTCTime({ valueDate: new Date() })] + }) + ); // signingTime - signedAttr.push(new pkijs.Attribute({ + signedAttr.push( + new pkijs.Attribute({ type: OID_PKCS9_MessageDigest, //messageDigest - values: [ - new asn1js.OctetString({ valueHex: messageHash }) - ] - })); // messageDigest + values: [new asn1js.OctetString({ valueHex: messageHash })] + }) + ); // messageDigest - return signedAttr; - } - ); + return signedAttr; + }); //endregion } //endregion //region Initialize CMS Signed Data structures and sign it - sequence = sequence.then( - signedAttr => - { - cmsSignedSimpl = new pkijs.SignedData({ - version: 1, - encapContentInfo: new pkijs.EncapsulatedContentInfo({ - eContentType: OID_PKCS7_Data // "data" content type - }), - signerInfos: [ - new pkijs.SignerInfo({ - version: 1, - sid: new pkijs.IssuerAndSerialNumber({ - issuer: signingCert.issuer, - serialNumber: signingCert.serialNumber - }) + sequence = sequence.then(signedAttr => { + cmsSignedSimpl = new pkijs.SignedData({ + version: 1, + encapContentInfo: new pkijs.EncapsulatedContentInfo({ + eContentType: OID_PKCS7_Data // "data" content type + }), + signerInfos: [ + new pkijs.SignerInfo({ + version: 1, + sid: new pkijs.IssuerAndSerialNumber({ + issuer: signingCert.issuer, + serialNumber: signingCert.serialNumber }) - ], - certificates: certificateChain //array - }); + }) + ], + certificates: certificateChain //array + }); - if(addExt) - { - cmsSignedSimpl.signerInfos[0].signedAttrs = new pkijs.SignedAndUnsignedAttributes({ + if (addExt) { + cmsSignedSimpl.signerInfos[0].signedAttrs = new pkijs.SignedAndUnsignedAttributes( + { type: 0, attributes: signedAttr - }); - } + } + ); + } - if(detachedSignature === false) - { - const contentInfo = new pkijs.EncapsulatedContentInfo({ - eContent: new asn1js.OctetString({ valueHex: dataBuffer }) - }); + if (detachedSignature === false) { + const contentInfo = new pkijs.EncapsulatedContentInfo({ + eContent: new asn1js.OctetString({ valueHex: dataBuffer }) + }); - cmsSignedSimpl.encapContentInfo.eContent = contentInfo.eContent; + cmsSignedSimpl.encapContentInfo.eContent = contentInfo.eContent; - return cmsSignedSimpl.sign(privateKey, 0, hashAlg); - } - - return cmsSignedSimpl.sign(privateKey, 0, hashAlg, dataBuffer); + return cmsSignedSimpl.sign(privateKey, 0, hashAlg); } - ); + + return cmsSignedSimpl.sign(privateKey, 0, hashAlg, dataBuffer); + }); //endregion //region Create final result sequence = sequence.then( - (result) => - { + result => { const cmsSignedSchema = cmsSignedSimpl.toSchema(true); const cmsContentSimp = new pkijs.ContentInfo({ @@ -866,8 +943,7 @@ Vereign - Authentic Communication const block2 = block1.valueBlock.value[0]; block2.lenBlock.isIndefiniteForm = true; - if(detachedSignature === false) - { + if (detachedSignature === false) { const block3 = block2.valueBlock.value[2]; block3.lenBlock.isIndefiniteForm = true; block3.valueBlock.value[1].lenBlock.isIndefiniteForm = true; @@ -882,34 +958,30 @@ Vereign - Authentic Communication ); //endregion - sequence = sequence.then( - (cmsSignedBuffer) => - { - let signature = arrayBufferToBase64Formatted(cmsSignedBuffer); - let boundary = makeBoundary(); + sequence = sequence.then(cmsSignedBuffer => { + let signature = arrayBufferToBase64Formatted(cmsSignedBuffer); + let boundary = makeBoundary(); - template = template.replace(/{{boundary}}/g, boundary); - template = template.replace("{{signature}}", signature); - template = template.replace("{{headers}}", newHeaderLines); - template = template.replace("{{mime}}", mime); + template = template.replace(/{{boundary}}/g, boundary); + template = template.replace("{{signature}}", signature); + template = template.replace("{{headers}}", newHeaderLines); + template = template.replace("{{mime}}", mime); - //template = template.replace(newline, '\r\n') - return template - } - ); + //template = template.replace(newline, '\r\n') + return template; + }); return sequence; } - const newline = /\r\n|\r|\n/g; function capitalizeFirstLetter(string) { - if(string === "id") { - return "ID" + if (string === "id") { + return "ID"; } - if(string === "mime") { + if (string === "mime") { return "MIME"; } @@ -929,11 +1001,9 @@ function capitalizeHeader(string) { return result; } - - function makeBoundary() { let len = 20 + Math.random() * 20; - return 'W0RyLiBEYW15YW4gTWl0ZXZd--' + makeid(len) + return "W0RyLiBEYW15YW4gTWl0ZXZd--" + makeid(len); } export const parseCertificates = signatureBase64 => { @@ -1005,7 +1075,9 @@ export const getCertificateChain = signatureBase64 => { // Go through all certificates to build a chain from first certificate to the root certificates.forEach(certificate => { - if (certificateChain[0].issuer.commonName === certificate.subject.commonName) { + if ( + certificateChain[0].issuer.commonName === certificate.subject.commonName + ) { certificateChain.unshift(certificate); } }); diff --git a/javascript/src/utilities/stringUtilities.js b/javascript/src/utilities/stringUtilities.js index 5c817a7a4eac86e9ca4d15c17269190c58e9092b..1e2e39d9655873a3a1779f7253505c6adb7c7851 100644 --- a/javascript/src/utilities/stringUtilities.js +++ b/javascript/src/utilities/stringUtilities.js @@ -1,53 +1,53 @@ -export const stringToUtf8ByteArray = (str) => { - if (typeof str !== 'string') { +export const stringToUtf8ByteArray = str => { + if (typeof str !== "string") { str = str.toString(); } - const res = Buffer.from(str, 'utf-8'); + const res = Buffer.from(str, "utf-8"); return res; -} +}; -export const utf8ByteArrayToString = (ba) => { +export const utf8ByteArrayToString = ba => { if (!Buffer.isBuffer(ba)) { ba = Buffer.from(ba); } - const res = ba.toString('utf-8'); + const res = ba.toString("utf-8"); return res; -} +}; -export const stringToUtf8Base64 = (str) => { +export const stringToUtf8Base64 = str => { if (!Buffer.isBuffer(str)) { - if (typeof str !== 'string') { + if (typeof str !== "string") { str = str.toString(); } - str = Buffer.from(str, 'utf-8'); + str = Buffer.from(str, "utf-8"); } - const res = str.toString('base64'); + const res = str.toString("base64"); return res; -} +}; -export const utf8Base64ToString = (strBase64) => { +export const utf8Base64ToString = strBase64 => { if (!Buffer.isBuffer(strBase64)) { - if (typeof strBase64 !== 'string') { + if (typeof strBase64 !== "string") { strBase64 = strBase64.toString(); } - strBase64 = Buffer.from(strBase64, 'base64'); + strBase64 = Buffer.from(strBase64, "base64"); } - const res = strBase64.toString('utf-8'); + const res = strBase64.toString("utf-8"); return res; -} +}; -export const base64ToByteArray = (strBase64) => { - if (typeof strBase64 !== 'string') { +export const base64ToByteArray = strBase64 => { + if (typeof strBase64 !== "string") { strBase64 = strBase64.toString(); } - const res = Buffer.from(strBase64, 'base64'); + const res = Buffer.from(strBase64, "base64"); return res; -} +}; -export const byteArrayToBase64 = (ba) => { +export const byteArrayToBase64 = ba => { if (!Buffer.isBuffer(ba)) { ba = Buffer.from(ba); } - const res = ba.toString('base64'); + const res = ba.toString("base64"); return res; -} +}; diff --git a/javascript/src/viamapi-client.js b/javascript/src/viamapi-client.js index 1fa645e73df4f0d1b9dd40e43050508df6b446a8..9c6e9cafe3a7d9d0c02cd1edee002f863589e2ce 100644 --- a/javascript/src/viamapi-client.js +++ b/javascript/src/viamapi-client.js @@ -1,4 +1,4 @@ -const Penpal = require('penpal').default; +const Penpal = require("penpal").default; /** * Sets up interaction with Vereign Restful API @@ -9,7 +9,14 @@ const Penpal = require('penpal').default; * @param wopiUrl - WOPI URL used to acces WopiAPI * @returns {*} */ -function setupViamAPI(divId, methods, iframeUrl, apiUrl, wopiUrl, collaboraUrl) { +function setupViamAPI( + divId, + methods, + iframeUrl, + apiUrl, + wopiUrl, + collaboraUrl +) { if (!iframeUrl) { iframeUrl = `${window.location.origin}/vcl/js/iframe`; console.warn(`Iframe URL not specified. Fall back to ${iframeUrl}`); // eslint-disable-line no-console @@ -24,9 +31,9 @@ function setupViamAPI(divId, methods, iframeUrl, apiUrl, wopiUrl, collaboraUrl) methods }); - return connection.promise - .then((child) => child.initialize(apiUrl, wopiUrl, collaboraUrl).then(() => child)); + return connection.promise.then(child => + child.initialize(apiUrl, wopiUrl, collaboraUrl).then(() => child) + ); } window.setupViamAPI = setupViamAPI; -