1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-21 15:48:06 +02:00

Enable auto-formatting of the entire code-base using Prettier (issue 11444)

Note that Prettier, purposely, has only limited [configuration options](https://prettier.io/docs/en/options.html). The configuration file is based on [the one in `mozilla central`](https://searchfox.org/mozilla-central/source/.prettierrc) with just a few additions (to avoid future breakage if the defaults ever changes).

Prettier is being used for a couple of reasons:

 - To be consistent with `mozilla-central`, where Prettier is already in use across the tree.

 - To ensure a *consistent* coding style everywhere, which is automatically enforced during linting (since Prettier is used as an ESLint plugin). This thus ends "all" formatting disussions once and for all, removing the need for review comments on most stylistic matters.

Many ESLint options are now redundant, and I've tried my best to remove all the now unnecessary options (but I may have missed some).
Note also that since Prettier considers the `printWidth` option as a guide, rather than a hard rule, this patch resorts to a small hack in the ESLint config to ensure that *comments* won't become too long.

*Please note:* This patch is generated automatically, by appending the `--fix` argument to the ESLint call used in the `gulp lint` task. It will thus require some additional clean-up, which will be done in a *separate* commit.

(On a more personal note, I'll readily admit that some of the changes Prettier makes are *extremely* ugly. However, in the name of consistency we'll probably have to live with that.)
This commit is contained in:
Jonas Jenwald 2019-12-25 15:59:37 +01:00
parent 8ec1dfde49
commit de36b2aaba
205 changed files with 40024 additions and 31859 deletions

View file

@ -1,15 +1,16 @@
const fs = require('fs');
const crypto = require('crypto');
const execSync = require('child_process').execSync;
const fs = require("fs");
const crypto = require("crypto");
const execSync = require("child_process").execSync;
const testManifest = 'test/test_manifest.json';
const pdfFolder = 'test/pdfs/';
const gitIgnore = 'test/pdfs/.gitignore';
const testManifest = "test/test_manifest.json";
const pdfFolder = "test/pdfs/";
const gitIgnore = "test/pdfs/.gitignore";
if (process.argv.length < 3) {
console.log('\nUsage: node add_test.js FILE\n');
console.log(' Add a PDF as a reference test. FILE must be located in ' +
`${pdfFolder}`);
console.log("\nUsage: node add_test.js FILE\n");
console.log(
" Add a PDF as a reference test. FILE must be located in " + `${pdfFolder}`
);
process.exit(1);
}
@ -22,16 +23,16 @@ if (!fs.existsSync(file)) {
}
function calculateMD5(file, callback) {
var hash = crypto.createHash('md5');
var hash = crypto.createHash("md5");
var stream = fs.createReadStream(file);
stream.on('data', function (data) {
stream.on("data", function(data) {
hash.update(data);
});
stream.on('error', function (err) {
stream.on("error", function(err) {
callback(err);
});
stream.on('end', function() {
var result = hash.digest('hex');
stream.on("end", function() {
var result = hash.digest("hex");
callback(null, result);
});
}
@ -44,26 +45,30 @@ calculateMD5(file, (err, md5) => {
if (err) {
throw new Error(err);
}
let contents = fs.readFileSync(gitIgnore, 'utf8').split('\n');
let contents = fs.readFileSync(gitIgnore, "utf8").split("\n");
let randomLine = getRandomArbitrary(10, contents.length - 2);
contents.splice(randomLine, 0,
'!' + file.substring(file.lastIndexOf('/') + 1));
fs.writeFileSync('test/pdfs/.gitignore', contents.join('\n'));
contents.splice(
randomLine,
0,
"!" + file.substring(file.lastIndexOf("/") + 1)
);
fs.writeFileSync("test/pdfs/.gitignore", contents.join("\n"));
contents = fs.readFileSync(testManifest, 'utf8');
let pdf = file.substring(file.lastIndexOf('/') + 1, file.length - 4);
contents = fs.readFileSync(testManifest, "utf8");
let pdf = file.substring(file.lastIndexOf("/") + 1, file.length - 4);
let randomPoint = getRandomArbitrary(100, contents.length - 20);
let bracket = contents.indexOf('},\n', randomPoint);
let out = contents.substring(0, bracket) +
'},\n' +
let bracket = contents.indexOf("},\n", randomPoint);
let out =
contents.substring(0, bracket) +
"},\n" +
` { "id": "${pdf}",\n` +
` "file": "pdfs/${pdf}.pdf",\n` +
` "md5": "${md5}",\n` +
' "rounds": 1,\n' +
' "type": "eq"\n' +
' ' +
" " +
contents.substring(bracket);
fs.writeFileSync('test/test_manifest.json', out);
fs.writeFileSync("test/test_manifest.json", out);
execSync(`git add ${testManifest} ${gitIgnore}`);
execSync(`git add ${file}`);
});

View file

@ -15,14 +15,14 @@
*/
/* eslint-disable object-shorthand */
'use strict';
"use strict";
var assert = require('assert');
var fs = require('fs');
var vm = require('vm');
var assert = require("assert");
var fs = require("fs");
var vm = require("vm");
var SRC_DIR = __dirname + '/../../';
var telemetryJsPath = 'extensions/chromium/telemetry.js';
var SRC_DIR = __dirname + "/../../";
var telemetryJsPath = "extensions/chromium/telemetry.js";
var telemetryJsSource = fs.readFileSync(SRC_DIR + telemetryJsPath);
var telemetryScript = new vm.Script(telemetryJsSource, {
filename: telemetryJsPath,
@ -36,7 +36,7 @@ function createExtensionGlobal() {
var window = {};
// Whenever a "request" was made, the extra headers are appended to this list.
var test_requests = window.test_requests = [];
var test_requests = (window.test_requests = []);
// Extension API mocks.
window.window = window;
@ -44,15 +44,15 @@ function createExtensionGlobal() {
window.chrome.extension = {};
window.chrome.extension.inIncognitoContext = false;
window.chrome.runtime = {};
window.chrome.runtime.id = 'oemmndcbldboiebfnladdacbdfmadadm';
window.chrome.runtime.id = "oemmndcbldboiebfnladdacbdfmadadm";
window.chrome.runtime.getManifest = function() {
return { version: '1.0.0', };
return { version: "1.0.0" };
};
function createStorageAPI() {
var storageArea = {};
storageArea.get = function(key, callback) {
assert.equal(key, 'disableTelemetry');
assert.equal(key, "disableTelemetry");
// chrome.storage.*. is async, but we make it synchronous to ease testing.
callback(storageArea.mock_data);
};
@ -67,8 +67,8 @@ function createExtensionGlobal() {
window.navigator = {};
window.navigator.onLine = true;
window.navigator.userAgent =
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ' +
'Chrome/50.0.2661.94 Safari/537.36';
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " +
"Chrome/50.0.2661.94 Safari/537.36";
window.localStorage = {};
var getRandomValues_state = 0;
@ -86,13 +86,13 @@ function createExtensionGlobal() {
window.Request = {};
window.Request.prototype = {
get mode() {
throw new TypeError('Illegal invocation');
throw new TypeError("Illegal invocation");
},
};
window.fetch = function(url, options) {
assert.equal(url, LOG_URL);
assert.equal(options.method, 'POST');
assert.equal(options.mode, 'cors');
assert.equal(options.method, "POST");
assert.equal(options.mode, "cors");
assert.ok(!options.body);
test_requests.push(options.headers);
};
@ -113,7 +113,7 @@ function createExtensionGlobal() {
open: function(method, url) {
assert.equal(invoked.open, false);
invoked.open = true;
assert.equal(method, 'POST');
assert.equal(method, "POST");
assert.equal(url, LOG_URL);
},
setRequestHeader: function(k, v) {
@ -133,7 +133,7 @@ function createExtensionGlobal() {
// Time-related logic.
var timers = [];
window.setInterval = function(callback, ms) {
assert.equal(typeof callback, 'function');
assert.equal(typeof callback, "function");
timers.push(callback);
};
window.Date = {
@ -154,10 +154,12 @@ function createExtensionGlobal() {
// Simulate an update of the browser.
function updateBrowser(window) {
window.navigator.userAgent =
window.navigator.userAgent.replace(/Chrome\/(\d+)/, function(_, v) {
return 'Chrome/' + (parseInt(v) + 1);
});
window.navigator.userAgent = window.navigator.userAgent.replace(
/Chrome\/(\d+)/,
function(_, v) {
return "Chrome/" + (parseInt(v) + 1);
}
);
}
var tests = [
@ -165,10 +167,12 @@ var tests = [
// Default settings, run extension for the first time.
var window = createExtensionGlobal();
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
},
function test_first_run_incognito() {
@ -183,10 +187,12 @@ var tests = [
var window = createExtensionGlobal();
delete window.chrome.storage.managed;
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
},
function test_managed_pref() {
@ -216,10 +222,12 @@ var tests = [
disableTelemetry: false,
};
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
},
function test_run_extension_again() {
@ -227,59 +235,73 @@ var tests = [
telemetryScript.runInNewContext(window);
telemetryScript.runInNewContext(window);
// Only one request should be sent because of rate-limiting.
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
// Simulate that quite some hours passed, but it's still rate-limited.
window.Date.test_now_value += 11 * 36E5;
window.Date.test_now_value += 11 * 36e5;
telemetryScript.runInNewContext(window);
// Only one request should be sent because of rate-limiting.
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
// Another hour passes and the request should not be rate-limited any more.
window.Date.test_now_value += 1 * 36E5;
window.Date.test_now_value += 1 * 36e5;
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}, {
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
},
function test_running_for_a_while() {
var window = createExtensionGlobal();
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
// Simulate that the timer fired 11 hours since the last ping. The request
// should still be rate-limited.
window.Date.test_now_value += 11 * 36E5;
window.Date.test_now_value += 11 * 36e5;
window.test_fireTimers();
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
// Another hour passes and the request should not be rate-limited any more.
window.Date.test_now_value += 1 * 36E5;
window.Date.test_now_value += 1 * 36e5;
window.test_fireTimers();
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}, {
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
},
function test_browser_update() {
@ -287,14 +309,17 @@ var tests = [
telemetryScript.runInNewContext(window);
updateBrowser(window);
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}, {
// Generate a new ID for better privacy.
'Deduplication-Id': '4343434343',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
{
// Generate a new ID for better privacy.
"Deduplication-Id": "4343434343",
"Extension-Version": "1.0.0",
},
]);
},
function test_browser_update_between_pref_toggle() {
@ -309,33 +334,39 @@ var tests = [
disableTelemetry: false,
};
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}, {
// Generate a new ID for better privacy, even if the update happened
// while telemetry was disabled.
'Deduplication-Id': '4343434343',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
{
// Generate a new ID for better privacy, even if the update happened
// while telemetry was disabled.
"Deduplication-Id": "4343434343",
"Extension-Version": "1.0.0",
},
]);
},
function test_extension_update() {
var window = createExtensionGlobal();
telemetryScript.runInNewContext(window);
window.chrome.runtime.getManifest = function() {
return { version: '1.0.1', };
return { version: "1.0.1" };
};
window.Date.test_now_value += 12 * 36E5;
window.Date.test_now_value += 12 * 36e5;
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}, {
// The ID did not change because the browser version did not change.
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.1',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
{
// The ID did not change because the browser version did not change.
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.1",
},
]);
},
function test_unofficial_build() {
@ -345,7 +376,7 @@ var tests = [
window.console.warn = function() {
didWarn = true;
};
window.chrome.runtime.id = 'abcdefghijklmnopabcdefghijklmnop';
window.chrome.runtime.id = "abcdefghijklmnopabcdefghijklmnop";
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, []);
assert.ok(didWarn);
@ -357,10 +388,12 @@ var tests = [
// the XMLHttpRequest API should not change behavior.
delete window.XMLHttpRequest;
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
},
function test_fetch_not_supported() {
@ -369,23 +402,27 @@ var tests = [
delete window.Request;
delete window.Headers;
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
},
function test_fetch_mode_not_supported() {
var window = createExtensionGlobal();
delete window.Request.prototype.mode;
window.fetch = function() {
throw new Error('Unexpected call to fetch!');
throw new Error("Unexpected call to fetch!");
};
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
},
function test_network_offline() {
@ -398,10 +435,12 @@ var tests = [
// Simulate that the network might be up.
window.navigator.onLine = true;
telemetryScript.runInNewContext(window);
assert.deepEqual(window.test_requests, [{
'Deduplication-Id': '4242424242',
'Extension-Version': '1.0.0',
}]);
assert.deepEqual(window.test_requests, [
{
"Deduplication-Id": "4242424242",
"Extension-Version": "1.0.0",
},
]);
},
];
var test_i = 0;
@ -409,10 +448,10 @@ var test_i = 0;
(function next() {
var test = tests[test_i++];
if (!test) {
console.log('All tests completed.');
console.log("All tests completed.");
return;
}
console.log('Running test ' + test_i + '/' + tests.length + ': ' + test.name);
console.log("Running test " + test_i + "/" + tests.length + ": " + test.name);
test();
process.nextTick(next);
})();

View file

@ -15,22 +15,21 @@
*/
/* eslint-disable object-shorthand */
'use strict';
"use strict";
var fs = require('fs');
var crypto = require('crypto');
var http = require('http');
var https = require('https');
var fs = require("fs");
var crypto = require("crypto");
var http = require("http");
var https = require("https");
function rewriteWebArchiveUrl(url) {
// Web Archive URLs need to be transformed to add `if_` after the ID.
// Without this, an HTML page containing an iframe with the PDF file
// will be served instead (issue 8920).
var webArchiveRegex =
/(^https?:\/\/web\.archive\.org\/web\/)(\d+)(\/https?:\/\/.+)/g;
var webArchiveRegex = /(^https?:\/\/web\.archive\.org\/web\/)(\d+)(\/https?:\/\/.+)/g;
var urlParts = webArchiveRegex.exec(url);
if (urlParts) {
return urlParts[1] + (urlParts[2] + 'if_') + urlParts[3];
return urlParts[1] + (urlParts[2] + "if_") + urlParts[3];
}
return url;
}
@ -40,60 +39,69 @@ function downloadFile(file, url, callback, redirects) {
var completed = false;
var protocol = /^https:\/\//.test(url) ? https : http;
protocol.get(url, function (response) {
var redirectTo;
if (response.statusCode === 301 || response.statusCode === 302 ||
response.statusCode === 307 || response.statusCode === 308) {
if (redirects > 10) {
callback('Too many redirects');
protocol
.get(url, function(response) {
var redirectTo;
if (
response.statusCode === 301 ||
response.statusCode === 302 ||
response.statusCode === 307 ||
response.statusCode === 308
) {
if (redirects > 10) {
callback("Too many redirects");
}
redirectTo = response.headers.location;
redirectTo = require("url").resolve(url, redirectTo);
downloadFile(file, redirectTo, callback, (redirects || 0) + 1);
return;
}
if (response.statusCode === 404 && !url.includes("web.archive.org")) {
// trying waybackmachine
redirectTo = "http://web.archive.org/web/" + url;
downloadFile(file, redirectTo, callback, (redirects || 0) + 1);
return;
}
redirectTo = response.headers.location;
redirectTo = require('url').resolve(url, redirectTo);
downloadFile(file, redirectTo, callback, (redirects || 0) + 1);
return;
}
if (response.statusCode === 404 && !url.includes('web.archive.org')) {
// trying waybackmachine
redirectTo = 'http://web.archive.org/web/' + url;
downloadFile(file, redirectTo, callback, (redirects || 0) + 1);
return;
}
if (response.statusCode !== 200) {
if (!completed) {
completed = true;
callback('HTTP ' + response.statusCode);
if (response.statusCode !== 200) {
if (!completed) {
completed = true;
callback("HTTP " + response.statusCode);
}
return;
}
return;
}
var stream = fs.createWriteStream(file);
stream.on('error', function (err) {
var stream = fs.createWriteStream(file);
stream.on("error", function(err) {
if (!completed) {
completed = true;
callback(err);
}
});
response.pipe(stream);
stream.on("finish", function() {
stream.end();
if (!completed) {
completed = true;
callback();
}
});
})
.on("error", function(err) {
if (!completed) {
if (
typeof err === "object" &&
err.errno === "ENOTFOUND" &&
!url.includes("web.archive.org")
) {
// trying waybackmachine
var redirectTo = "http://web.archive.org/web/" + url;
downloadFile(file, redirectTo, callback, (redirects || 0) + 1);
return;
}
completed = true;
callback(err);
}
});
response.pipe(stream);
stream.on('finish', function() {
stream.end();
if (!completed) {
completed = true;
callback();
}
});
}).on('error', function (err) {
if (!completed) {
if (typeof err === 'object' && err.errno === 'ENOTFOUND' &&
!url.includes('web.archive.org')) {
// trying waybackmachine
var redirectTo = 'http://web.archive.org/web/' + url;
downloadFile(file, redirectTo, callback, (redirects || 0) + 1);
return;
}
completed = true;
callback(err);
}
});
}
function downloadManifestFiles(manifest, callback) {
@ -104,43 +112,45 @@ function downloadManifestFiles(manifest, callback) {
}
var file = links[i].file;
var url = links[i].url;
console.log('Downloading ' + url + ' to ' + file + '...');
downloadFile(file, url, function (err) {
console.log("Downloading " + url + " to " + file + "...");
downloadFile(file, url, function(err) {
if (err) {
console.error('Error during downloading of ' + url + ': ' + err);
fs.writeFileSync(file, ''); // making it empty file
fs.writeFileSync(file + '.error', err);
console.error("Error during downloading of " + url + ": " + err);
fs.writeFileSync(file, ""); // making it empty file
fs.writeFileSync(file + ".error", err);
}
i++;
downloadNext();
});
}
var links = manifest.filter(function (item) {
return item.link && !fs.existsSync(item.file);
}).map(function (item) {
var file = item.file;
var linkfile = file + '.link';
var url = fs.readFileSync(linkfile).toString();
url = url.replace(/\s+$/, '');
return { file: file, url: url, };
});
var links = manifest
.filter(function(item) {
return item.link && !fs.existsSync(item.file);
})
.map(function(item) {
var file = item.file;
var linkfile = file + ".link";
var url = fs.readFileSync(linkfile).toString();
url = url.replace(/\s+$/, "");
return { file: file, url: url };
});
var i = 0;
downloadNext();
}
function calculateMD5(file, callback) {
var hash = crypto.createHash('md5');
var hash = crypto.createHash("md5");
var stream = fs.createReadStream(file);
stream.on('data', function (data) {
stream.on("data", function(data) {
hash.update(data);
});
stream.on('error', function (err) {
stream.on("error", function(err) {
callback(err);
});
stream.on('end', function() {
var result = hash.digest('hex');
stream.on("end", function() {
var result = hash.digest("hex");
callback(null, result);
});
}
@ -152,26 +162,39 @@ function verifyManifestFiles(manifest, callback) {
return;
}
var item = manifest[i];
if (fs.existsSync(item.file + '.error')) {
console.error('WARNING: File was not downloaded. See "' +
item.file + '.error" file.');
if (fs.existsSync(item.file + ".error")) {
console.error(
'WARNING: File was not downloaded. See "' + item.file + '.error" file.'
);
error = true;
i++;
verifyNext();
return;
}
calculateMD5(item.file, function (err, md5) {
calculateMD5(item.file, function(err, md5) {
if (err) {
console.log('WARNING: Unable to open file for reading "' + err + '".');
error = true;
} else if (!item.md5) {
console.error('WARNING: Missing md5 for file "' + item.file + '". ' +
'Hash for current file is "' + md5 + '"');
console.error(
'WARNING: Missing md5 for file "' +
item.file +
'". ' +
'Hash for current file is "' +
md5 +
'"'
);
error = true;
} else if (md5 !== item.md5) {
console.error('WARNING: MD5 of file "' + item.file +
'" does not match file. Expected "' +
item.md5 + '" computed "' + md5 + '"');
console.error(
'WARNING: MD5 of file "' +
item.file +
'" does not match file. Expected "' +
item.md5 +
'" computed "' +
md5 +
'"'
);
error = true;
}
i++;

View file

@ -14,30 +14,30 @@
*/
/* globals pdfjsLib, pdfjsViewer */
'use strict';
"use strict";
const WAITING_TIME = 100; // ms
const PDF_TO_CSS_UNITS = 96.0 / 72.0;
const CMAP_URL = '../external/bcmaps/';
const CMAP_URL = "../external/bcmaps/";
const CMAP_PACKED = true;
const IMAGE_RESOURCES_PATH = '/web/images/';
const WORKER_SRC = '../build/generic/build/pdf.worker.js';
const IMAGE_RESOURCES_PATH = "/web/images/";
const WORKER_SRC = "../build/generic/build/pdf.worker.js";
/**
* @class
*/
var rasterizeTextLayer = (function rasterizeTextLayerClosure() {
var SVG_NS = 'http://www.w3.org/2000/svg';
var SVG_NS = "http://www.w3.org/2000/svg";
var textLayerStylePromise = null;
function getTextLayerStyle() {
if (textLayerStylePromise) {
return textLayerStylePromise;
}
textLayerStylePromise = new Promise(function (resolve) {
textLayerStylePromise = new Promise(function(resolve) {
var xhr = new XMLHttpRequest();
xhr.open('GET', './text_layer_test.css');
xhr.onload = function () {
xhr.open("GET", "./text_layer_test.css");
xhr.onload = function() {
resolve(xhr.responseText);
};
xhr.send(null);
@ -45,27 +45,31 @@ var rasterizeTextLayer = (function rasterizeTextLayerClosure() {
return textLayerStylePromise;
}
function rasterizeTextLayer(ctx, viewport, textContent,
enhanceTextSelection) {
return new Promise(function (resolve, reject) {
function rasterizeTextLayer(
ctx,
viewport,
textContent,
enhanceTextSelection
) {
return new Promise(function(resolve, reject) {
// Building SVG with size of the viewport.
var svg = document.createElementNS(SVG_NS, 'svg:svg');
svg.setAttribute('width', viewport.width + 'px');
svg.setAttribute('height', viewport.height + 'px');
var svg = document.createElementNS(SVG_NS, "svg:svg");
svg.setAttribute("width", viewport.width + "px");
svg.setAttribute("height", viewport.height + "px");
// items are transformed to have 1px font size
svg.setAttribute('font-size', 1);
svg.setAttribute("font-size", 1);
// Adding element to host our HTML (style + text layer div).
var foreignObject = document.createElementNS(SVG_NS, 'svg:foreignObject');
foreignObject.setAttribute('x', '0');
foreignObject.setAttribute('y', '0');
foreignObject.setAttribute('width', viewport.width + 'px');
foreignObject.setAttribute('height', viewport.height + 'px');
var style = document.createElement('style');
var foreignObject = document.createElementNS(SVG_NS, "svg:foreignObject");
foreignObject.setAttribute("x", "0");
foreignObject.setAttribute("y", "0");
foreignObject.setAttribute("width", viewport.width + "px");
foreignObject.setAttribute("height", viewport.height + "px");
var style = document.createElement("style");
var stylePromise = getTextLayerStyle();
foreignObject.appendChild(style);
var div = document.createElement('div');
div.className = 'textLayer';
var div = document.createElement("div");
div.className = "textLayer";
foreignObject.appendChild(div);
// Rendering text layer as HTML.
@ -75,22 +79,23 @@ var rasterizeTextLayer = (function rasterizeTextLayerClosure() {
viewport,
enhanceTextSelection,
});
Promise.all([stylePromise, task.promise]).then(function (results) {
Promise.all([stylePromise, task.promise]).then(function(results) {
task.expandTextDivs(true);
style.textContent = results[0];
svg.appendChild(foreignObject);
// We need to have UTF-8 encoded XML.
var svg_xml = unescape(encodeURIComponent(
(new XMLSerializer()).serializeToString(svg)));
var svg_xml = unescape(
encodeURIComponent(new XMLSerializer().serializeToString(svg))
);
var img = new Image();
img.src = 'data:image/svg+xml;base64,' + btoa(svg_xml);
img.onload = function () {
img.src = "data:image/svg+xml;base64," + btoa(svg_xml);
img.onload = function() {
ctx.drawImage(img, 0, 0);
resolve();
};
img.onerror = function(e) {
reject(new Error('Error rasterizing text layer ' + e));
reject(new Error("Error rasterizing text layer " + e));
};
});
});
@ -103,7 +108,7 @@ var rasterizeTextLayer = (function rasterizeTextLayerClosure() {
* @class
*/
var rasterizeAnnotationLayer = (function rasterizeAnnotationLayerClosure() {
const SVG_NS = 'http://www.w3.org/2000/svg';
const SVG_NS = "http://www.w3.org/2000/svg";
/**
* For the reference tests, the entire annotation layer must be visible. To
@ -116,11 +121,11 @@ var rasterizeAnnotationLayer = (function rasterizeAnnotationLayerClosure() {
*/
let styles = {
common: {
file: '../web/annotation_layer_builder.css',
file: "../web/annotation_layer_builder.css",
promise: null,
},
overrides: {
file: './annotation_layer_builder_overrides.css',
file: "./annotation_layer_builder_overrides.css",
promise: null,
},
};
@ -135,12 +140,12 @@ var rasterizeAnnotationLayer = (function rasterizeAnnotationLayerClosure() {
for (let key in styles) {
styles[key].promise = new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
xhr.open('GET', styles[key].file);
xhr.open("GET", styles[key].file);
xhr.onload = function() {
resolve(xhr.responseText);
};
xhr.onerror = function(e) {
reject(new Error('Error fetching annotation style ' + e));
reject(new Error("Error fetching annotation style " + e));
};
xhr.send(null);
});
@ -154,7 +159,7 @@ var rasterizeAnnotationLayer = (function rasterizeAnnotationLayerClosure() {
for (var i = 0, ii = images.length; i < ii; i++) {
var imagePromise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.responseType = "blob";
xhr.onload = function() {
var reader = new FileReader();
reader.onloadend = function() {
@ -163,9 +168,9 @@ var rasterizeAnnotationLayer = (function rasterizeAnnotationLayerClosure() {
reader.readAsDataURL(xhr.response);
};
xhr.onerror = function(e) {
reject(new Error('Error fetching inline annotation image ' + e));
reject(new Error("Error fetching inline annotation image " + e));
};
xhr.open('GET', images[i].src);
xhr.open("GET", images[i].src);
xhr.send();
});
imagePromises.push(imagePromise);
@ -173,32 +178,37 @@ var rasterizeAnnotationLayer = (function rasterizeAnnotationLayerClosure() {
return imagePromises;
}
function rasterizeAnnotationLayer(ctx, viewport, annotations, page,
imageResourcesPath,
renderInteractiveForms) {
return new Promise(function (resolve, reject) {
function rasterizeAnnotationLayer(
ctx,
viewport,
annotations,
page,
imageResourcesPath,
renderInteractiveForms
) {
return new Promise(function(resolve, reject) {
// Building SVG with size of the viewport.
var svg = document.createElementNS(SVG_NS, 'svg:svg');
svg.setAttribute('width', viewport.width + 'px');
svg.setAttribute('height', viewport.height + 'px');
var svg = document.createElementNS(SVG_NS, "svg:svg");
svg.setAttribute("width", viewport.width + "px");
svg.setAttribute("height", viewport.height + "px");
// Adding element to host our HTML (style + annotation layer div).
var foreignObject = document.createElementNS(SVG_NS, 'svg:foreignObject');
foreignObject.setAttribute('x', '0');
foreignObject.setAttribute('y', '0');
foreignObject.setAttribute('width', viewport.width + 'px');
foreignObject.setAttribute('height', viewport.height + 'px');
var style = document.createElement('style');
var foreignObject = document.createElementNS(SVG_NS, "svg:foreignObject");
foreignObject.setAttribute("x", "0");
foreignObject.setAttribute("y", "0");
foreignObject.setAttribute("width", viewport.width + "px");
foreignObject.setAttribute("height", viewport.height + "px");
var style = document.createElement("style");
var stylePromise = getAnnotationLayerStyle();
foreignObject.appendChild(style);
var div = document.createElement('div');
div.className = 'annotationLayer';
var div = document.createElement("div");
div.className = "annotationLayer";
// Rendering annotation layer as HTML.
stylePromise.then(function (common, overrides) {
stylePromise.then(function(common, overrides) {
style.textContent = common + overrides;
var annotation_viewport = viewport.clone({ dontFlip: true, });
var annotation_viewport = viewport.clone({ dontFlip: true });
var parameters = {
viewport: annotation_viewport,
div,
@ -211,18 +221,20 @@ var rasterizeAnnotationLayer = (function rasterizeAnnotationLayerClosure() {
pdfjsLib.AnnotationLayer.render(parameters);
// Inline SVG images from text annotations.
var images = div.getElementsByTagName('img');
var images = div.getElementsByTagName("img");
var imagePromises = inlineAnnotationImages(images);
var converted = Promise.all(imagePromises).then(function(data) {
var loadedPromises = [];
for (var i = 0, ii = data.length; i < ii; i++) {
images[i].src = data[i];
loadedPromises.push(new Promise(function (resolve, reject) {
images[i].onload = resolve;
images[i].onerror = function(e) {
reject(new Error('Error loading image ' + e));
};
}));
loadedPromises.push(
new Promise(function(resolve, reject) {
images[i].onload = resolve;
images[i].onerror = function(e) {
reject(new Error("Error loading image " + e));
};
})
);
}
return loadedPromises;
});
@ -232,16 +244,17 @@ var rasterizeAnnotationLayer = (function rasterizeAnnotationLayerClosure() {
// We need to have UTF-8 encoded XML.
converted.then(function() {
var svg_xml = unescape(encodeURIComponent(
(new XMLSerializer()).serializeToString(svg)));
var svg_xml = unescape(
encodeURIComponent(new XMLSerializer().serializeToString(svg))
);
var img = new Image();
img.src = 'data:image/svg+xml;base64,' + btoa(svg_xml);
img.onload = function () {
img.src = "data:image/svg+xml;base64," + btoa(svg_xml);
img.onload = function() {
ctx.drawImage(img, 0, 0);
resolve();
};
img.onerror = function(e) {
reject(new Error('Error rasterizing annotation layer ' + e));
reject(new Error("Error rasterizing annotation layer " + e));
};
});
});
@ -264,7 +277,8 @@ var rasterizeAnnotationLayer = (function rasterizeAnnotationLayerClosure() {
/**
* @class
*/
var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
var Driver = (function DriverClosure() {
// eslint-disable-line no-unused-vars
/**
* @constructs Driver
* @param {DriverOptions} options
@ -284,22 +298,23 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
this.browser = parameters.browser;
this.manifestFile = parameters.manifestFile;
this.appPath = parameters.path;
this.delay = (parameters.delay | 0) || 0;
this.delay = parameters.delay | 0 || 0;
this.inFlightRequests = 0;
this.testFilter = parameters.testFilter ?
JSON.parse(parameters.testFilter) : [];
this.testFilter = parameters.testFilter
? JSON.parse(parameters.testFilter)
: [];
// Create a working canvas
this.canvas = document.createElement('canvas');
this.canvas = document.createElement("canvas");
}
Driver.prototype = {
_getQueryStringParameters: function Driver_getQueryStringParameters() {
var queryString = window.location.search.substring(1);
var values = queryString.split('&');
var values = queryString.split("&");
var parameters = {};
for (var i = 0, ii = values.length; i < ii; i++) {
var value = values[i].split('=');
var value = values[i].split("=");
parameters[unescape(value[0])] = unescape(value[1]);
}
return parameters;
@ -308,19 +323,34 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
run: function Driver_run() {
var self = this;
window.onerror = function(message, source, line, column, error) {
self._info('Error: ' + message + ' Script: ' + source + ' Line: ' +
line + ' Column: ' + column + ' StackTrace: ' + error);
self._info(
"Error: " +
message +
" Script: " +
source +
" Line: " +
line +
" Column: " +
column +
" StackTrace: " +
error
);
};
this._info('User agent: ' + navigator.userAgent);
this._log('Harness thinks this browser is "' + this.browser +
'" with path "' + this.appPath + '"\n');
this._info("User agent: " + navigator.userAgent);
this._log(
'Harness thinks this browser is "' +
this.browser +
'" with path "' +
this.appPath +
'"\n'
);
this._log('Fetching manifest "' + this.manifestFile + '"... ');
var r = new XMLHttpRequest();
r.open('GET', this.manifestFile, false);
r.open("GET", this.manifestFile, false);
r.onreadystatechange = function() {
if (r.readyState === 4) {
self._log('done\n');
self._log("done\n");
self.manifest = JSON.parse(r.responseText);
if (self.testFilter && self.testFilter.length) {
self.manifest = self.manifest.filter(function(item) {
@ -332,7 +362,7 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
}
};
if (this.delay > 0) {
this._log('\nDelaying for ' + this.delay + ' ms...\n');
this._log("\nDelaying for " + this.delay + " ms...\n");
}
// When gathering the stats the numbers seem to be more reliable
// if the browser is given more time to start.
@ -342,7 +372,7 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
},
_nextTask() {
let failure = '';
let failure = "";
this._cleanup().then(() => {
if (this.currentTask === this.manifest.length) {
@ -352,7 +382,7 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
let task = this.manifest[this.currentTask];
task.round = 0;
task.pageNum = task.firstPage || 1;
task.stats = { times: [], };
task.stats = { times: [] };
this._log('Loading file "' + task.file + '"\n');
@ -368,16 +398,19 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
disableAutoFetch: !task.enableAutoFetch,
pdfBug: true,
});
loadingTask.promise.then((doc) => {
task.pdfDoc = doc;
this._nextPage(task, failure);
}, (err) => {
failure = 'Loading PDF document: ' + err;
this._nextPage(task, failure);
});
loadingTask.promise.then(
doc => {
task.pdfDoc = doc;
this._nextPage(task, failure);
},
err => {
failure = "Loading PDF document: " + err;
this._nextPage(task, failure);
}
);
return;
} catch (e) {
failure = 'Loading PDF document: ' + this._exceptionToString(e);
failure = "Loading PDF document: " + this._exceptionToString(e);
}
this._nextPage(task, failure);
});
@ -409,13 +442,13 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
},
_exceptionToString: function Driver_exceptionToString(e) {
if (typeof e !== 'object') {
if (typeof e !== "object") {
return String(e);
}
if (!('message' in e)) {
if (!("message" in e)) {
return JSON.stringify(e);
}
return e.message + ('stack' in e ? ' at ' + e.stack.split('\n')[0] : '');
return e.message + ("stack" in e ? " at " + e.stack.split("\n")[0] : "");
},
_getLastPageNumber: function Driver_getLastPageNumber(task) {
@ -431,14 +464,15 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
_nextPage: function Driver_nextPage(task, loadError) {
var self = this;
var failure = loadError || '';
var failure = loadError || "";
var ctx;
if (!task.pdfDoc) {
var dataUrl = this.canvas.toDataURL('image/png');
this._sendResult(dataUrl, task, failure, function () {
self._log('done' + (failure ? ' (failed !: ' + failure + ')' : '') +
'\n');
var dataUrl = this.canvas.toDataURL("image/png");
this._sendResult(dataUrl, task, failure, function() {
self._log(
"done" + (failure ? " (failed !: " + failure + ")" : "") + "\n"
);
self.currentTask++;
self._nextTask();
});
@ -447,7 +481,7 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
if (task.pageNum > this._getLastPageNumber(task)) {
if (++task.round < task.rounds) {
this._log(' Round ' + (1 + task.round) + '\n');
this._log(" Round " + (1 + task.round) + "\n");
task.pageNum = task.firstPage || 1;
} else {
this.currentTask++;
@ -457,8 +491,13 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
}
if (task.skipPages && task.skipPages.includes(task.pageNum)) {
this._log(' Skipping page ' + task.pageNum + '/' +
task.pdfDoc.numPages + '...\n');
this._log(
" Skipping page " +
task.pageNum +
"/" +
task.pdfDoc.numPages +
"...\n"
);
task.pageNum++;
this._nextPage(task);
return;
@ -466,148 +505,178 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
if (!failure) {
try {
this._log(' Loading page ' + task.pageNum + '/' +
task.pdfDoc.numPages + '... ');
this._log(
" Loading page " +
task.pageNum +
"/" +
task.pdfDoc.numPages +
"... "
);
this.canvas.mozOpaque = true;
ctx = this.canvas.getContext('2d', { alpha: false, });
task.pdfDoc.getPage(task.pageNum).then(function(page) {
var viewport = page.getViewport({ scale: PDF_TO_CSS_UNITS, });
self.canvas.width = viewport.width;
self.canvas.height = viewport.height;
self._clearCanvas();
ctx = this.canvas.getContext("2d", { alpha: false });
task.pdfDoc.getPage(task.pageNum).then(
function(page) {
var viewport = page.getViewport({ scale: PDF_TO_CSS_UNITS });
self.canvas.width = viewport.width;
self.canvas.height = viewport.height;
self._clearCanvas();
// Initialize various `eq` test subtypes, see comment below.
var renderAnnotations = false, renderForms = false;
// Initialize various `eq` test subtypes, see comment below.
var renderAnnotations = false,
renderForms = false;
var textLayerCanvas, annotationLayerCanvas;
var initPromise;
if (task.type === 'text') {
// Using a dummy canvas for PDF context drawing operations
textLayerCanvas = self.textLayerCanvas;
if (!textLayerCanvas) {
textLayerCanvas = document.createElement('canvas');
self.textLayerCanvas = textLayerCanvas;
}
textLayerCanvas.width = viewport.width;
textLayerCanvas.height = viewport.height;
var textLayerContext = textLayerCanvas.getContext('2d');
textLayerContext.clearRect(0, 0,
textLayerCanvas.width, textLayerCanvas.height);
var enhanceText = !!task.enhance;
// The text builder will draw its content on the test canvas
initPromise = page.getTextContent({
normalizeWhitespace: true,
}).then(function(textContent) {
return rasterizeTextLayer(textLayerContext, viewport,
textContent, enhanceText);
});
} else {
textLayerCanvas = null;
// We fetch the `eq` specific test subtypes here, to avoid
// accidentally changing the behaviour for other types of tests.
renderAnnotations = !!task.annotations;
renderForms = !!task.forms;
// Render the annotation layer if necessary.
if (renderAnnotations || renderForms) {
// Create a dummy canvas for the drawing operations.
annotationLayerCanvas = self.annotationLayerCanvas;
if (!annotationLayerCanvas) {
annotationLayerCanvas = document.createElement('canvas');
self.annotationLayerCanvas = annotationLayerCanvas;
var textLayerCanvas, annotationLayerCanvas;
var initPromise;
if (task.type === "text") {
// Using a dummy canvas for PDF context drawing operations
textLayerCanvas = self.textLayerCanvas;
if (!textLayerCanvas) {
textLayerCanvas = document.createElement("canvas");
self.textLayerCanvas = textLayerCanvas;
}
annotationLayerCanvas.width = viewport.width;
annotationLayerCanvas.height = viewport.height;
var annotationLayerContext =
annotationLayerCanvas.getContext('2d');
annotationLayerContext.clearRect(0, 0,
annotationLayerCanvas.width, annotationLayerCanvas.height);
// The annotation builder will draw its content on the canvas.
initPromise =
page.getAnnotations({ intent: 'display', }).then(
function(annotations) {
return rasterizeAnnotationLayer(annotationLayerContext,
viewport, annotations,
page,
IMAGE_RESOURCES_PATH,
renderForms);
textLayerCanvas.width = viewport.width;
textLayerCanvas.height = viewport.height;
var textLayerContext = textLayerCanvas.getContext("2d");
textLayerContext.clearRect(
0,
0,
textLayerCanvas.width,
textLayerCanvas.height
);
var enhanceText = !!task.enhance;
// The text builder will draw its content on the test canvas
initPromise = page
.getTextContent({
normalizeWhitespace: true,
})
.then(function(textContent) {
return rasterizeTextLayer(
textLayerContext,
viewport,
textContent,
enhanceText
);
});
} else {
annotationLayerCanvas = null;
initPromise = Promise.resolve();
}
}
textLayerCanvas = null;
// We fetch the `eq` specific test subtypes here, to avoid
// accidentally changing the behaviour for other types of tests.
renderAnnotations = !!task.annotations;
renderForms = !!task.forms;
var renderContext = {
canvasContext: ctx,
viewport,
renderInteractiveForms: renderForms,
};
var completeRender = (function(error) {
// if text layer is present, compose it on top of the page
if (textLayerCanvas) {
ctx.save();
ctx.globalCompositeOperation = 'screen';
ctx.fillStyle = 'rgb(128, 255, 128)'; // making it green
ctx.fillRect(0, 0, viewport.width, viewport.height);
ctx.restore();
ctx.drawImage(textLayerCanvas, 0, 0);
// Render the annotation layer if necessary.
if (renderAnnotations || renderForms) {
// Create a dummy canvas for the drawing operations.
annotationLayerCanvas = self.annotationLayerCanvas;
if (!annotationLayerCanvas) {
annotationLayerCanvas = document.createElement("canvas");
self.annotationLayerCanvas = annotationLayerCanvas;
}
annotationLayerCanvas.width = viewport.width;
annotationLayerCanvas.height = viewport.height;
var annotationLayerContext = annotationLayerCanvas.getContext(
"2d"
);
annotationLayerContext.clearRect(
0,
0,
annotationLayerCanvas.width,
annotationLayerCanvas.height
);
// The annotation builder will draw its content on the canvas.
initPromise = page
.getAnnotations({ intent: "display" })
.then(function(annotations) {
return rasterizeAnnotationLayer(
annotationLayerContext,
viewport,
annotations,
page,
IMAGE_RESOURCES_PATH,
renderForms
);
});
} else {
annotationLayerCanvas = null;
initPromise = Promise.resolve();
}
}
// If we have annotation layer, compose it on top of the page.
if (annotationLayerCanvas) {
ctx.drawImage(annotationLayerCanvas, 0, 0);
}
if (page.stats) { // Get the page stats *before* running cleanup.
task.stats = page.stats;
}
page.cleanup(/* resetStats = */ true);
self._snapshot(task, error);
});
initPromise.then(function () {
return page.render(renderContext).promise.then(function() {
completeRender(false);
});
}).catch(function (error) {
completeRender('render : ' + error);
});
},
function(error) {
self._snapshot(task, 'render : ' + error);
});
var renderContext = {
canvasContext: ctx,
viewport,
renderInteractiveForms: renderForms,
};
var completeRender = function(error) {
// if text layer is present, compose it on top of the page
if (textLayerCanvas) {
ctx.save();
ctx.globalCompositeOperation = "screen";
ctx.fillStyle = "rgb(128, 255, 128)"; // making it green
ctx.fillRect(0, 0, viewport.width, viewport.height);
ctx.restore();
ctx.drawImage(textLayerCanvas, 0, 0);
}
// If we have annotation layer, compose it on top of the page.
if (annotationLayerCanvas) {
ctx.drawImage(annotationLayerCanvas, 0, 0);
}
if (page.stats) {
// Get the page stats *before* running cleanup.
task.stats = page.stats;
}
page.cleanup(/* resetStats = */ true);
self._snapshot(task, error);
};
initPromise
.then(function() {
return page.render(renderContext).promise.then(function() {
completeRender(false);
});
})
.catch(function(error) {
completeRender("render : " + error);
});
},
function(error) {
self._snapshot(task, "render : " + error);
}
);
} catch (e) {
failure = 'page setup : ' + this._exceptionToString(e);
failure = "page setup : " + this._exceptionToString(e);
this._snapshot(task, failure);
}
}
},
_clearCanvas: function Driver_clearCanvas() {
var ctx = this.canvas.getContext('2d', { alpha: false, });
var ctx = this.canvas.getContext("2d", { alpha: false });
ctx.beginPath();
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
_snapshot: function Driver_snapshot(task, failure) {
var self = this;
this._log('Snapshotting... ');
this._log("Snapshotting... ");
var dataUrl = this.canvas.toDataURL('image/png');
this._sendResult(dataUrl, task, failure, function () {
self._log('done' + (failure ? ' (failed !: ' + failure + ')' : '') +
'\n');
var dataUrl = this.canvas.toDataURL("image/png");
this._sendResult(dataUrl, task, failure, function() {
self._log(
"done" + (failure ? " (failed !: " + failure + ")" : "") + "\n"
);
task.pageNum++;
self._nextPage(task);
});
},
_quit: function Driver_quit() {
this._log('Done !');
this.end.textContent = 'Tests finished. Close this window!';
this._log("Done !");
this.end.textContent = "Tests finished. Close this window!";
// Send the quit request
var r = new XMLHttpRequest();
r.open('POST', '/tellMeToQuit?path=' + escape(this.appPath), false);
r.open("POST", "/tellMeToQuit?path=" + escape(this.appPath), false);
r.onreadystatechange = function(e) {
if (r.readyState === 4) {
window.close();
@ -617,10 +686,13 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
},
_info: function Driver_info(message) {
this._send('/info', JSON.stringify({
browser: this.browser,
message,
}));
this._send(
"/info",
JSON.stringify({
browser: this.browser,
message,
})
);
},
_log: function Driver_log(message) {
@ -628,12 +700,12 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
// reduces runtime significantly.
if (this.output.insertAdjacentHTML) {
// eslint-disable-next-line no-unsanitized/method
this.output.insertAdjacentHTML('BeforeEnd', message);
this.output.insertAdjacentHTML("BeforeEnd", message);
} else {
this.output.textContent += message;
}
if (message.lastIndexOf('\n') >= 0 && !this.disableScrolling.checked) {
if (message.lastIndexOf("\n") >= 0 && !this.disableScrolling.checked) {
// Scroll to the bottom of the page
this.output.scrollTop = this.output.scrollHeight;
}
@ -648,13 +720,11 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
}
},
_sendResult: function Driver_sendResult(snapshot, task, failure,
callback) {
_sendResult: function Driver_sendResult(snapshot, task, failure, callback) {
var result = JSON.stringify({
browser: this.browser,
id: task.id,
numPages: task.pdfDoc ?
(task.lastPage || task.pdfDoc.numPages) : 0,
numPages: task.pdfDoc ? task.lastPage || task.pdfDoc.numPages : 0,
lastPageNum: this._getLastPageNumber(task),
failure,
file: task.file,
@ -663,14 +733,14 @@ var Driver = (function DriverClosure() { // eslint-disable-line no-unused-vars
snapshot,
stats: task.stats.times,
});
this._send('/submit_task_results', result, callback);
this._send("/submit_task_results", result, callback);
},
_send: function Driver_send(url, message, callback) {
var self = this;
var r = new XMLHttpRequest();
r.open('POST', url, true);
r.setRequestHeader('Content-Type', 'application/json');
r.open("POST", url, true);
r.setRequestHeader("Content-Type", "application/json");
r.onreadystatechange = function(e) {
if (r.readyState === 4) {
self.inFlightRequests--;

View file

@ -14,23 +14,25 @@
* limitations under the License.
*/
'use strict';
"use strict";
var base64alphabet =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function decodeFontData(base64) { // eslint-disable-line no-unused-vars
function decodeFontData(base64) {
// eslint-disable-line no-unused-vars
var result = [];
var bits = 0, bitsLength = 0;
var bits = 0,
bitsLength = 0;
for (var i = 0, ii = base64.length; i < ii; i++) {
var ch = base64[i];
if (ch <= ' ') {
if (ch <= " ") {
continue;
}
var index = base64alphabet.indexOf(ch);
if (index < 0) {
throw new Error('Invalid character');
throw new Error("Invalid character");
}
if (index >= 64) {
break;
@ -39,7 +41,7 @@ function decodeFontData(base64) { // eslint-disable-line no-unused-vars
bitsLength += 6;
if (bitsLength >= 8) {
bitsLength -= 8;
var code = (bits >> bitsLength) & 0xFF;
var code = (bits >> bitsLength) & 0xff;
result.push(code);
}
}
@ -47,42 +49,48 @@ function decodeFontData(base64) { // eslint-disable-line no-unused-vars
}
function encodeFontData(data) {
var buffer = '';
var buffer = "";
var i, n;
for (i = 0, n = data.length; i < n; 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 < n ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;
var d4 = i + 2 < n ? (b3 & 0x3F) : 64;
buffer += (base64alphabet.charAt(d1) + base64alphabet.charAt(d2) +
base64alphabet.charAt(d3) + base64alphabet.charAt(d4));
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 < n ? ((b2 & 0xf) << 2) | (b3 >> 6) : 64;
var d4 = i + 2 < n ? b3 & 0x3f : 64;
buffer +=
base64alphabet.charAt(d1) +
base64alphabet.charAt(d2) +
base64alphabet.charAt(d3) +
base64alphabet.charAt(d4);
}
return buffer;
}
function ttx(data, callback) { // eslint-disable-line no-unused-vars
function ttx(data, callback) {
// eslint-disable-line no-unused-vars
var xhr = new XMLHttpRequest();
xhr.open('POST', '/ttx');
xhr.open("POST", "/ttx");
var encodedData = encodeFontData(data);
xhr.setRequestHeader('Content-type', 'text/plain');
xhr.setRequestHeader('Content-length', encodedData.length);
xhr.setRequestHeader("Content-type", "text/plain");
xhr.setRequestHeader("Content-length", encodedData.length);
xhr.onreadystatechange = function getPdfOnreadystatechange(e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
callback(xhr.responseText);
} else {
callback('<error>Transport error: ' + xhr.statusText + '</error>');
callback("<error>Transport error: " + xhr.statusText + "</error>");
}
}
};
xhr.send(encodedData);
}
function verifyTtxOutput(output) { // eslint-disable-line no-unused-vars
function verifyTtxOutput(output) {
// eslint-disable-line no-unused-vars
var m = /^<error>(.*?)<\/error>/.exec(output);
if (m) {
throw m[1];

View file

@ -38,16 +38,19 @@
// Modified jasmine's boot.js file to load PDF.js libraries async.
'use strict';
"use strict";
function initializePDFJS(callback) {
Promise.all([SystemJS.import('pdfjs/core/fonts'),
SystemJS.import('pdfjs/core/stream'),
SystemJS.import('pdfjs/core/primitives'),
SystemJS.import('pdfjs/core/cmap')])
.then(function (modules) {
var fonts = modules[0], stream = modules[1],
primitives = modules[2], cmap = modules[3];
Promise.all([
SystemJS.import("pdfjs/core/fonts"),
SystemJS.import("pdfjs/core/stream"),
SystemJS.import("pdfjs/core/primitives"),
SystemJS.import("pdfjs/core/cmap"),
]).then(function(modules) {
var fonts = modules[0],
stream = modules[1],
primitives = modules[2],
cmap = modules[3];
// Expose some of the PDFJS members to global scope for tests.
window.Font = fonts.Font;
window.ToUnicodeMap = fonts.ToUnicodeMap;
@ -77,17 +80,17 @@ function initializePDFJS(callback) {
});
var config = {
failFast: queryString.getParam('failFast'),
oneFailurePerSpec: queryString.getParam('oneFailurePerSpec'),
hideDisabled: queryString.getParam('hideDisabled'),
failFast: queryString.getParam("failFast"),
oneFailurePerSpec: queryString.getParam("oneFailurePerSpec"),
hideDisabled: queryString.getParam("hideDisabled"),
};
var random = queryString.getParam('random');
if (random !== undefined && random !== '') {
var random = queryString.getParam("random");
if (random !== undefined && random !== "") {
config.random = random;
}
var seed = queryString.getParam('seed');
var seed = queryString.getParam("seed");
if (seed) {
config.seed = seed;
}
@ -115,9 +118,11 @@ function initializePDFJS(callback) {
env.addReporter(htmlReporter);
if (queryString.getParam('browser')) {
var testReporter = new TestReporter(queryString.getParam('browser'),
queryString.getParam('path'));
if (queryString.getParam("browser")) {
var testReporter = new TestReporter(
queryString.getParam("browser"),
queryString.getParam("path")
);
env.addReporter(testReporter);
}
@ -125,7 +130,7 @@ function initializePDFJS(callback) {
// against the `spec` query param.
var specFilter = new jasmine.HtmlSpecFilter({
filterString() {
return queryString.getParam('spec');
return queryString.getParam("spec");
},
});
@ -148,7 +153,7 @@ function initializePDFJS(callback) {
currentWindowOnload();
}
initializePDFJS(function () {
initializePDFJS(function() {
htmlReporter.initialize();
env.execute();
});
@ -160,4 +165,4 @@ function initializePDFJS(callback) {
}
return destination;
}
}());
})();

View file

@ -14,43 +14,46 @@
* limitations under the License.
*/
'use strict';
"use strict";
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;
var fs = require("fs");
var path = require("path");
var spawn = require("child_process").spawn;
var ttxResourcesHome = path.join(__dirname, '..', 'ttx');
var ttxResourcesHome = path.join(__dirname, "..", "ttx");
var nextTTXTaskId = Date.now();
function runTtx(ttxResourcesHome, fontPath, registerOnCancel, callback) {
fs.realpath(ttxResourcesHome, function (err, ttxResourcesHome) {
var fontToolsHome = path.join(ttxResourcesHome, 'fonttools-code');
fs.realpath(fontPath, function (err, fontPath) {
var ttxPath = path.join('Tools', 'ttx');
fs.realpath(ttxResourcesHome, function(err, ttxResourcesHome) {
var fontToolsHome = path.join(ttxResourcesHome, "fonttools-code");
fs.realpath(fontPath, function(err, fontPath) {
var ttxPath = path.join("Tools", "ttx");
if (!fs.existsSync(path.join(fontToolsHome, ttxPath))) {
callback('TTX was not found, please checkout PDF.js submodules');
callback("TTX was not found, please checkout PDF.js submodules");
return;
}
var ttxEnv = {
'PYTHONPATH': path.join(fontToolsHome, 'Lib'),
'PYTHONDONTWRITEBYTECODE': true,
PYTHONPATH: path.join(fontToolsHome, "Lib"),
PYTHONDONTWRITEBYTECODE: true,
};
var ttxStdioMode = 'ignore';
var ttx = spawn('python', [ttxPath, fontPath],
{ cwd: fontToolsHome, stdio: ttxStdioMode, env: ttxEnv, });
var ttxStdioMode = "ignore";
var ttx = spawn("python", [ttxPath, fontPath], {
cwd: fontToolsHome,
stdio: ttxStdioMode,
env: ttxEnv,
});
var ttxRunError;
registerOnCancel(function (reason) {
registerOnCancel(function(reason) {
ttxRunError = reason;
callback(reason);
ttx.kill();
});
ttx.on('error', function (err) {
ttx.on("error", function(err) {
ttxRunError = err;
callback('Unable to execute ttx');
callback("Unable to execute ttx");
});
ttx.on('close', function (code) {
ttx.on("close", function(code) {
if (ttxRunError) {
return;
}
@ -60,21 +63,24 @@ function runTtx(ttxResourcesHome, fontPath, registerOnCancel, callback) {
});
}
exports.translateFont = function translateFont(content, registerOnCancel,
callback) {
var buffer = Buffer.from(content, 'base64');
exports.translateFont = function translateFont(
content,
registerOnCancel,
callback
) {
var buffer = Buffer.from(content, "base64");
var taskId = (nextTTXTaskId++).toString();
var fontPath = path.join(ttxResourcesHome, taskId + '.otf');
var resultPath = path.join(ttxResourcesHome, taskId + '.ttx');
var fontPath = path.join(ttxResourcesHome, taskId + ".otf");
var resultPath = path.join(ttxResourcesHome, taskId + ".ttx");
fs.writeFileSync(fontPath, buffer);
runTtx(ttxResourcesHome, fontPath, registerOnCancel, function (err) {
runTtx(ttxResourcesHome, fontPath, registerOnCancel, function(err) {
fs.unlinkSync(fontPath);
if (err) {
console.error(err);
callback(err);
} else if (!fs.existsSync(resultPath)) {
callback('Output was not generated');
callback("Output was not generated");
} else {
callback(null, fs.readFileSync(resultPath));
fs.unlinkSync(resultPath);

View file

@ -1,25 +1,30 @@
'use strict';
"use strict";
var fs = require('fs');
var fs = require("fs");
try {
var ttest = require('ttest');
var ttest = require("ttest");
} catch (e) {
console.log('\nttest is not installed -- to intall, run "npm install ttest"');
console.log('Continuing without significance test...\n');
console.log("Continuing without significance test...\n");
}
var VALID_GROUP_BYS = ['browser', 'pdf', 'page', 'round', 'stat'];
var VALID_GROUP_BYS = ["browser", "pdf", "page", "round", "stat"];
function parseOptions() {
var yargs = require('yargs')
.usage('Compare the results of two stats files.\n' +
'Usage:\n $0 <BASELINE> <CURRENT> [options]')
var yargs = require("yargs")
.usage(
"Compare the results of two stats files.\n" +
"Usage:\n $0 <BASELINE> <CURRENT> [options]"
)
.demand(2)
.string(['groupBy'])
.describe('groupBy', 'How statistics should grouped. Valid options: ' +
VALID_GROUP_BYS.join(' '))
.default('groupBy', 'browser,stat');
.string(["groupBy"])
.describe(
"groupBy",
"How statistics should grouped. Valid options: " +
VALID_GROUP_BYS.join(" ")
)
.default("groupBy", "browser,stat");
var result = yargs.argv;
result.baseline = result._[0];
result.current = result._[1];
@ -37,11 +42,11 @@ function group(stats, groupBy) {
for (var j = 0; j < groupBy.length; j++) {
keyArr.push(stat[groupBy[j]]);
}
var key = keyArr.join(',');
var key = keyArr.join(",");
if (vals[key] === undefined) {
vals[key] = [];
}
vals[key].push(stat['time']);
vals[key].push(stat["time"]);
}
return vals;
}
@ -53,30 +58,30 @@ function group(stats, groupBy) {
function flatten(stats) {
var rows = [];
stats.forEach(function(stat) {
stat['stats'].forEach(function(s) {
stat["stats"].forEach(function(s) {
rows.push({
browser: stat['browser'],
page: stat['page'],
pdf: stat['pdf'],
round: stat['round'],
stat: s['name'],
time: s['end'] - s['start'],
browser: stat["browser"],
page: stat["page"],
pdf: stat["pdf"],
round: stat["round"],
stat: s["name"],
time: s["end"] - s["start"],
});
});
});
// Use only overall results if not grouped by 'stat'
if (!options.groupBy.includes('stat')) {
if (!options.groupBy.includes("stat")) {
rows = rows.filter(function(s) {
return s.stat === 'Overall';
return s.stat === "Overall";
});
}
return rows;
}
function pad(s, length, dir /* default: 'right' */) {
s = '' + s;
var spaces = new Array(Math.max(0, length - s.length + 1)).join(' ');
return dir === 'left' ? spaces + s : s + spaces;
s = "" + s;
var spaces = new Array(Math.max(0, length - s.length + 1)).join(" ");
return dir === "left" ? spaces + s : s + spaces;
}
function mean(array) {
@ -88,8 +93,8 @@ function mean(array) {
/* Comparator for row key sorting. */
function compareRow(a, b) {
a = a.split(',');
b = b.split(',');
a = a.split(",");
b = b.split(",");
for (var i = 0; i < Math.min(a.length, b.length); i++) {
var intA = parseInt(a[i], 10);
var intB = parseInt(b[i], 10);
@ -125,11 +130,13 @@ function stat(baseline, current) {
keys.sort(compareRow);
var labels = options.groupBy.slice(0);
labels.push('Count', 'Baseline(ms)', 'Current(ms)', '+/-', '% ');
labels.push("Count", "Baseline(ms)", "Current(ms)", "+/-", "% ");
if (ttest) {
labels.push('Result(P<.05)');
labels.push("Result(P<.05)");
}
var i, row, rows = [];
var i,
row,
rows = [];
// collect rows and measure column widths
var width = labels.map(function(s) {
return s.length;
@ -139,19 +146,23 @@ function stat(baseline, current) {
var key = keys[k];
var baselineMean = mean(baselineGroup[key]);
var currentMean = mean(currentGroup[key]);
row = key.split(',');
row.push('' + baselineGroup[key].length,
'' + Math.round(baselineMean),
'' + Math.round(currentMean),
'' + Math.round(currentMean - baselineMean),
(100 * (currentMean - baselineMean) / baselineMean).toFixed(2));
row = key.split(",");
row.push(
"" + baselineGroup[key].length,
"" + Math.round(baselineMean),
"" + Math.round(currentMean),
"" + Math.round(currentMean - baselineMean),
((100 * (currentMean - baselineMean)) / baselineMean).toFixed(2)
);
if (ttest) {
var p = (baselineGroup[key].length < 2) ? 1 :
ttest(baselineGroup[key], currentGroup[key]).pValue();
var p =
baselineGroup[key].length < 2
? 1
: ttest(baselineGroup[key], currentGroup[key]).pValue();
if (p < 0.05) {
row.push(currentMean < baselineMean ? 'faster' : 'slower');
row.push(currentMean < baselineMean ? "faster" : "slower");
} else {
row.push('');
row.push("");
}
}
for (i = 0; i < row.length; i++) {
@ -162,19 +173,19 @@ function stat(baseline, current) {
// add horizontal line
var hline = width.map(function(w) {
return new Array(w + 1).join('-');
return new Array(w + 1).join("-");
});
rows.splice(1, 0, hline);
// print output
console.log('-- Grouped By ' + options.groupBy.join(', ') + ' --');
console.log("-- Grouped By " + options.groupBy.join(", ") + " --");
var groupCount = options.groupBy.length;
for (var r = 0; r < rows.length; r++) {
row = rows[r];
for (i = 0; i < row.length; i++) {
row[i] = pad(row[i], width[i], (i < groupCount) ? 'right' : 'left');
row[i] = pad(row[i], width[i], i < groupCount ? "right" : "left");
}
console.log(row.join(' | '));
console.log(row.join(" | "));
}
}

View file

@ -15,89 +15,132 @@
*/
/* eslint-disable object-shorthand */
'use strict';
"use strict";
var WebServer = require('./webserver.js').WebServer;
var WebBrowser = require('./webbrowser.js').WebBrowser;
var path = require('path');
var fs = require('fs');
var os = require('os');
var url = require('url');
var testUtils = require('./testutils.js');
var WebServer = require("./webserver.js").WebServer;
var WebBrowser = require("./webbrowser.js").WebBrowser;
var path = require("path");
var fs = require("fs");
var os = require("os");
var url = require("url");
var testUtils = require("./testutils.js");
function parseOptions() {
function describeCheck(fn, text) {
fn.toString = function () {
fn.toString = function() {
return text;
};
return fn;
}
var yargs = require('yargs')
.usage('Usage: $0')
.boolean(['help', 'masterMode', 'reftest', 'unitTest', 'fontTest',
'noPrompts', 'noDownload', 'downloadOnly', 'strictVerify'])
.string(['manifestFile', 'browser', 'browserManifestFile',
'port', 'statsFile', 'statsDelay', 'testfilter'])
.alias('browser', 'b').alias('help', 'h').alias('masterMode', 'm')
.alias('testfilter', 't')
.describe('help', 'Show this help message')
.describe('masterMode', 'Run the script in master mode.')
.describe('noPrompts',
'Uses default answers (intended for CLOUD TESTS only!).')
.describe('manifestFile',
'A path to JSON file in the form of test_manifest.json')
.default('manifestFile', 'test_manifest.json')
.describe('browser', 'The path to a single browser ')
.describe('browserManifestFile', 'A path to JSON file in the form of ' +
'those found in resources/browser_manifests/')
.describe('reftest', 'Automatically start reftest showing comparison ' +
'test failures, if there are any.')
.describe('testfilter', 'Run specific reftest(s).')
.default('testfilter', [])
.example('$0 --b=firefox -t=issue5567 -t=issue5909',
'Run the reftest identified by issue5567 and issue5909 in Firefox.')
.describe('port', 'The port the HTTP server should listen on.')
.default('port', 0)
.describe('unitTest', 'Run the unit tests.')
.describe('fontTest', 'Run the font tests.')
.describe('noDownload', 'Skips test PDFs downloading.')
.describe('downloadOnly', 'Download test PDFs without running the tests.')
.describe('strictVerify', 'Error if verifying the manifest files fails.')
.describe('statsFile', 'The file where to store stats.')
.describe('statsDelay', 'The amount of time in milliseconds the browser ' +
'should wait before starting stats.')
.default('statsDelay', 0)
.check(describeCheck(function (argv) {
return +argv.reftest + argv.unitTest + argv.fontTest +
argv.masterMode <= 1;
}, '--reftest, --unitTest, --fontTest and --masterMode must not be ' +
'specified at the same time.'))
.check(describeCheck(function (argv) {
return !argv.noDownload || !argv.downloadOnly;
}, '--noDownload and --downloadOnly cannot be used together.'))
.check(describeCheck(function (argv) {
return !argv.masterMode || argv.manifestFile === 'test_manifest.json';
}, 'when --masterMode is specified --manifestFile shall be equal ' +
'test_manifest.json'))
.check(describeCheck(function (argv) {
return !argv.browser || !argv.browserManifestFile;
}, '--browser and --browserManifestFile must not be specified at the ' +
'same time.'));
var yargs = require("yargs")
.usage("Usage: $0")
.boolean([
"help",
"masterMode",
"reftest",
"unitTest",
"fontTest",
"noPrompts",
"noDownload",
"downloadOnly",
"strictVerify",
])
.string([
"manifestFile",
"browser",
"browserManifestFile",
"port",
"statsFile",
"statsDelay",
"testfilter",
])
.alias("browser", "b")
.alias("help", "h")
.alias("masterMode", "m")
.alias("testfilter", "t")
.describe("help", "Show this help message")
.describe("masterMode", "Run the script in master mode.")
.describe(
"noPrompts",
"Uses default answers (intended for CLOUD TESTS only!)."
)
.describe(
"manifestFile",
"A path to JSON file in the form of test_manifest.json"
)
.default("manifestFile", "test_manifest.json")
.describe("browser", "The path to a single browser ")
.describe(
"browserManifestFile",
"A path to JSON file in the form of " +
"those found in resources/browser_manifests/"
)
.describe(
"reftest",
"Automatically start reftest showing comparison " +
"test failures, if there are any."
)
.describe("testfilter", "Run specific reftest(s).")
.default("testfilter", [])
.example(
"$0 --b=firefox -t=issue5567 -t=issue5909",
"Run the reftest identified by issue5567 and issue5909 in Firefox."
)
.describe("port", "The port the HTTP server should listen on.")
.default("port", 0)
.describe("unitTest", "Run the unit tests.")
.describe("fontTest", "Run the font tests.")
.describe("noDownload", "Skips test PDFs downloading.")
.describe("downloadOnly", "Download test PDFs without running the tests.")
.describe("strictVerify", "Error if verifying the manifest files fails.")
.describe("statsFile", "The file where to store stats.")
.describe(
"statsDelay",
"The amount of time in milliseconds the browser " +
"should wait before starting stats."
)
.default("statsDelay", 0)
.check(
describeCheck(function(argv) {
return (
+argv.reftest + argv.unitTest + argv.fontTest + argv.masterMode <= 1
);
}, "--reftest, --unitTest, --fontTest and --masterMode must not be " +
"specified at the same time.")
)
.check(
describeCheck(function(argv) {
return !argv.noDownload || !argv.downloadOnly;
}, "--noDownload and --downloadOnly cannot be used together.")
)
.check(
describeCheck(function(argv) {
return !argv.masterMode || argv.manifestFile === "test_manifest.json";
}, "when --masterMode is specified --manifestFile shall be equal " +
"test_manifest.json")
)
.check(
describeCheck(function(argv) {
return !argv.browser || !argv.browserManifestFile;
}, "--browser and --browserManifestFile must not be specified at the " +
"same time.")
);
var result = yargs.argv;
if (result.help) {
yargs.showHelp();
process.exit(0);
}
result.testfilter = Array.isArray(result.testfilter) ?
result.testfilter : [result.testfilter];
result.testfilter = Array.isArray(result.testfilter)
? result.testfilter
: [result.testfilter];
return result;
}
var refsTmpDir = 'tmp';
var testResultDir = 'test_snapshots';
var refsDir = 'ref';
var eqLog = 'eq.log';
var refsTmpDir = "tmp";
var testResultDir = "test_snapshots";
var refsDir = "ref";
var eqLog = "eq.log";
var browserTimeout = 120;
function monitorBrowserTimeout(session, onTimeout) {
@ -108,41 +151,47 @@ function monitorBrowserTimeout(session, onTimeout) {
session.timeoutMonitor = null;
return;
}
session.timeoutMonitor = setTimeout(function () {
session.timeoutMonitor = setTimeout(function() {
onTimeout(session);
}, browserTimeout * 1000);
}
function updateRefImages() {
function sync(removeTmp) {
console.log(' Updating ref/ ... ');
console.log(" Updating ref/ ... ");
testUtils.copySubtreeSync(refsTmpDir, refsDir);
if (removeTmp) {
testUtils.removeDirSync(refsTmpDir);
}
console.log('done');
console.log("done");
}
if (options.noPrompts) {
sync(false); // don't remove tmp/ for botio
return;
}
testUtils.confirm('Would you like to update the master copy in ref/? [yn] ',
function (confirmed) {
testUtils.confirm(
"Would you like to update the master copy in ref/? [yn] ",
function(confirmed) {
if (confirmed) {
sync(true);
} else {
console.log(' OK, not updating.');
console.log(" OK, not updating.");
}
});
}
);
}
function examineRefImages() {
startServer();
var startUrl = 'http://' + server.host + ':' + server.port +
'/test/resources/reftest-analyzer.html#web=/test/eq.log';
var startUrl =
"http://" +
server.host +
":" +
server.port +
"/test/resources/reftest-analyzer.html#web=/test/eq.log";
var config = Object.assign({}, sessions[0].config);
config['headless'] = false;
config["headless"] = false;
var browser = WebBrowser.create(config);
browser.start(startUrl);
}
@ -154,7 +203,7 @@ function startRefTest(masterMode, showRefImages) {
var numFBFFailures = 0;
var numEqFailures = 0;
var numEqNoSnapshot = 0;
sessions.forEach(function (session) {
sessions.forEach(function(session) {
numErrors += session.numErrors;
numFBFFailures += session.numFBFFailures;
numEqFailures += session.numEqFailures;
@ -163,21 +212,21 @@ function startRefTest(masterMode, showRefImages) {
var numFatalFailures = numErrors + numFBFFailures;
console.log();
if (numFatalFailures + numEqFailures > 0) {
console.log('OHNOES! Some tests failed!');
console.log("OHNOES! Some tests failed!");
if (numErrors > 0) {
console.log(' errors: ' + numErrors);
console.log(" errors: " + numErrors);
}
if (numEqFailures > 0) {
console.log(' different ref/snapshot: ' + numEqFailures);
console.log(" different ref/snapshot: " + numEqFailures);
}
if (numFBFFailures > 0) {
console.log(' different first/second rendering: ' + numFBFFailures);
console.log(" different first/second rendering: " + numFBFFailures);
}
} else {
console.log('All regression tests passed.');
console.log("All regression tests passed.");
}
var runtime = (Date.now() - startTime) / 1000;
console.log('Runtime was ' + runtime.toFixed(1) + ' seconds');
console.log("Runtime was " + runtime.toFixed(1) + " seconds");
if (options.statsFile) {
fs.writeFileSync(options.statsFile, JSON.stringify(stats, null, 2));
@ -185,27 +234,31 @@ function startRefTest(masterMode, showRefImages) {
if (masterMode) {
if (numEqFailures + numEqNoSnapshot > 0) {
console.log();
console.log('Some eq tests failed or didn\'t have snapshots.');
console.log('Checking to see if master references can be updated...');
console.log("Some eq tests failed or didn't have snapshots.");
console.log("Checking to see if master references can be updated...");
if (numFatalFailures > 0) {
console.log(' No. Some non-eq tests failed.');
console.log(" No. Some non-eq tests failed.");
} else {
console.log(
' Yes! The references in tmp/ can be synced with ref/.');
" Yes! The references in tmp/ can be synced with ref/."
);
updateRefImages();
}
}
} else if (showRefImages && numEqFailures > 0) {
console.log();
console.log('Starting reftest harness to examine ' + numEqFailures +
' eq test failures.');
console.log(
"Starting reftest harness to examine " +
numEqFailures +
" eq test failures."
);
examineRefImages(numEqFailures);
}
}
function setup() {
if (fs.existsSync(refsTmpDir)) {
console.error('tmp/ exists -- unable to proceed with testing');
console.error("tmp/ exists -- unable to proceed with testing");
process.exit(1);
}
@ -218,15 +271,15 @@ function startRefTest(masterMode, showRefImages) {
startTime = Date.now();
startServer();
server.hooks['POST'].push(refTestPostHandler);
server.hooks["POST"].push(refTestPostHandler);
onAllSessionsClosed = finalize;
startBrowsers('/test/test_slave.html', function (session) {
startBrowsers("/test/test_slave.html", function(session) {
session.masterMode = masterMode;
session.taskResults = {};
session.tasks = {};
session.remaining = manifest.length;
manifest.forEach(function (item) {
manifest.forEach(function(item) {
var rounds = item.rounds || 1;
var roundsResults = [];
roundsResults.length = rounds;
@ -247,15 +300,17 @@ function startRefTest(masterMode, showRefImages) {
setup();
return;
}
console.log('Temporary snapshot dir tmp/ is still around.');
console.log('tmp/ can be removed if it has nothing you need.');
testUtils.confirm('SHOULD THIS SCRIPT REMOVE tmp/? THINK CAREFULLY [yn] ',
function (confirmed) {
console.log("Temporary snapshot dir tmp/ is still around.");
console.log("tmp/ can be removed if it has nothing you need.");
testUtils.confirm(
"SHOULD THIS SCRIPT REMOVE tmp/? THINK CAREFULLY [yn] ",
function(confirmed) {
if (confirmed) {
testUtils.removeDirSync(refsTmpDir);
}
setup();
});
}
);
} else {
setup();
}
@ -278,8 +333,13 @@ function handleSessionTimeout(session) {
return;
}
var browser = session.name;
console.log('TEST-UNEXPECTED-FAIL | test failed ' + browser +
' has not responded in ' + browserTimeout + 's');
console.log(
"TEST-UNEXPECTED-FAIL | test failed " +
browser +
" has not responded in " +
browserTimeout +
"s"
);
session.numErrors += session.remaining;
session.remaining = 0;
closeSession(browser);
@ -299,7 +359,7 @@ function getTestManifest() {
return false;
});
if (testFilter.length) {
console.error('Unrecognized test IDs: ' + testFilter.join(' '));
console.error("Unrecognized test IDs: " + testFilter.join(" "));
return undefined;
}
}
@ -309,8 +369,12 @@ function getTestManifest() {
function checkEq(task, results, browser, masterMode) {
var taskId = task.id;
var refSnapshotDir = path.join(refsDir, os.platform(), browser, taskId);
var testSnapshotDir = path.join(testResultDir, os.platform(), browser,
taskId);
var testSnapshotDir = path.join(
testResultDir,
os.platform(),
browser,
taskId
);
var pageResults = results[0];
var taskType = task.type;
@ -321,51 +385,79 @@ function checkEq(task, results, browser, masterMode) {
continue;
}
var testSnapshot = pageResults[page].snapshot;
if (testSnapshot && testSnapshot.startsWith('data:image/png;base64,')) {
testSnapshot = Buffer.from(testSnapshot.substring(22), 'base64');
if (testSnapshot && testSnapshot.startsWith("data:image/png;base64,")) {
testSnapshot = Buffer.from(testSnapshot.substring(22), "base64");
} else {
console.error('Valid snapshot was not found.');
console.error("Valid snapshot was not found.");
}
var refSnapshot = null;
var eq = false;
var refPath = path.join(refSnapshotDir, (page + 1) + '.png');
var refPath = path.join(refSnapshotDir, page + 1 + ".png");
if (!fs.existsSync(refPath)) {
numEqNoSnapshot++;
if (!masterMode) {
console.log('WARNING: no reference snapshot ' + refPath);
console.log("WARNING: no reference snapshot " + refPath);
}
} else {
refSnapshot = fs.readFileSync(refPath);
eq = (refSnapshot.toString('hex') === testSnapshot.toString('hex'));
eq = refSnapshot.toString("hex") === testSnapshot.toString("hex");
if (!eq) {
console.log('TEST-UNEXPECTED-FAIL | ' + taskType + ' ' + taskId +
' | in ' + browser + ' | rendering of page ' + (page + 1) +
' != reference rendering');
console.log(
"TEST-UNEXPECTED-FAIL | " +
taskType +
" " +
taskId +
" | in " +
browser +
" | rendering of page " +
(page + 1) +
" != reference rendering"
);
testUtils.ensureDirSync(testSnapshotDir);
fs.writeFileSync(path.join(testSnapshotDir, (page + 1) + '.png'),
testSnapshot);
fs.writeFileSync(path.join(testSnapshotDir, (page + 1) + '_ref.png'),
refSnapshot);
fs.writeFileSync(
path.join(testSnapshotDir, page + 1 + ".png"),
testSnapshot
);
fs.writeFileSync(
path.join(testSnapshotDir, page + 1 + "_ref.png"),
refSnapshot
);
// NB: this follows the format of Mozilla reftest output so that
// we can reuse its reftest-analyzer script
fs.appendFileSync(eqLog, 'REFTEST TEST-UNEXPECTED-FAIL | ' + browser +
'-' + taskId + '-page' + (page + 1) + ' | image comparison (==)\n' +
'REFTEST IMAGE 1 (TEST): ' +
path.join(testSnapshotDir, (page + 1) + '.png') + '\n' +
'REFTEST IMAGE 2 (REFERENCE): ' +
path.join(testSnapshotDir, (page + 1) + '_ref.png') + '\n');
fs.appendFileSync(
eqLog,
"REFTEST TEST-UNEXPECTED-FAIL | " +
browser +
"-" +
taskId +
"-page" +
(page + 1) +
" | image comparison (==)\n" +
"REFTEST IMAGE 1 (TEST): " +
path.join(testSnapshotDir, page + 1 + ".png") +
"\n" +
"REFTEST IMAGE 2 (REFERENCE): " +
path.join(testSnapshotDir, page + 1 + "_ref.png") +
"\n"
);
numEqFailures++;
}
}
if (masterMode && (!refSnapshot || !eq)) {
var tmpSnapshotDir = path.join(refsTmpDir, os.platform(), browser,
taskId);
var tmpSnapshotDir = path.join(
refsTmpDir,
os.platform(),
browser,
taskId
);
testUtils.ensureDirSync(tmpSnapshotDir);
fs.writeFileSync(path.join(tmpSnapshotDir, (page + 1) + '.png'),
testSnapshot);
fs.writeFileSync(
path.join(tmpSnapshotDir, page + 1 + ".png"),
testSnapshot
);
}
}
@ -374,27 +466,36 @@ function checkEq(task, results, browser, masterMode) {
if (numEqFailures > 0) {
session.numEqFailures += numEqFailures;
} else {
console.log('TEST-PASS | ' + taskType + ' test ' + taskId + ' | in ' +
browser);
console.log(
"TEST-PASS | " + taskType + " test " + taskId + " | in " + browser
);
}
}
function checkFBF(task, results, browser) {
var numFBFFailures = 0;
var round0 = results[0], round1 = results[1];
var round0 = results[0],
round1 = results[1];
if (round0.length !== round1.length) {
console.error('round 1 and 2 sizes are different');
console.error("round 1 and 2 sizes are different");
}
for (var page = 0; page < round1.length; page++) {
var r0Page = round0[page], r1Page = round1[page];
var r0Page = round0[page],
r1Page = round1[page];
if (!r0Page) {
continue;
}
if (r0Page.snapshot !== r1Page.snapshot) {
console.log('TEST-UNEXPECTED-FAIL | forward-back-forward test ' +
task.id + ' | in ' + browser + ' | first rendering of page ' +
(page + 1) + ' != second');
console.log(
"TEST-UNEXPECTED-FAIL | forward-back-forward test " +
task.id +
" | in " +
browser +
" | first rendering of page " +
(page + 1) +
" != second"
);
numFBFFailures++;
}
}
@ -402,37 +503,56 @@ function checkFBF(task, results, browser) {
if (numFBFFailures > 0) {
getSession(browser).numFBFFailures += numFBFFailures;
} else {
console.log('TEST-PASS | forward-back-forward test ' + task.id +
' | in ' + browser);
console.log(
"TEST-PASS | forward-back-forward test " + task.id + " | in " + browser
);
}
}
function checkLoad(task, results, browser) {
// Load just checks for absence of failure, so if we got here the
// test has passed
console.log('TEST-PASS | load test ' + task.id + ' | in ' + browser);
console.log("TEST-PASS | load test " + task.id + " | in " + browser);
}
function checkRefTestResults(browser, id, results) {
var failed = false;
var session = getSession(browser);
var task = session.tasks[id];
results.forEach(function (roundResults, round) {
roundResults.forEach(function (pageResult, page) {
results.forEach(function(roundResults, round) {
roundResults.forEach(function(pageResult, page) {
if (!pageResult) {
return; // no results
}
if (pageResult.failure) {
failed = true;
if (fs.existsSync(task.file + '.error')) {
console.log('TEST-SKIPPED | PDF was not downloaded ' + id + ' | in ' +
browser + ' | page' + (page + 1) + ' round ' +
(round + 1) + ' | ' + pageResult.failure);
if (fs.existsSync(task.file + ".error")) {
console.log(
"TEST-SKIPPED | PDF was not downloaded " +
id +
" | in " +
browser +
" | page" +
(page + 1) +
" round " +
(round + 1) +
" | " +
pageResult.failure
);
} else {
session.numErrors++;
console.log('TEST-UNEXPECTED-FAIL | test failed ' + id + ' | in ' +
browser + ' | page' + (page + 1) + ' round ' +
(round + 1) + ' | ' + pageResult.failure);
console.log(
"TEST-UNEXPECTED-FAIL | test failed " +
id +
" | in " +
browser +
" | page" +
(page + 1) +
" round " +
(round + 1) +
" | " +
pageResult.failure
);
}
}
});
@ -441,22 +561,22 @@ function checkRefTestResults(browser, id, results) {
return;
}
switch (task.type) {
case 'eq':
case 'text':
case "eq":
case "text":
checkEq(task, results, browser, session.masterMode);
break;
case 'fbf':
case "fbf":
checkFBF(task, results, browser);
break;
case 'load':
case "load":
checkLoad(task, results, browser);
break;
default:
throw new Error('Unknown test type');
throw new Error("Unknown test type");
}
// clear memory
results.forEach(function (roundResults, round) {
roundResults.forEach(function (pageResult, page) {
results.forEach(function(roundResults, round) {
roundResults.forEach(function(pageResult, page) {
pageResult.snapshot = null;
});
});
@ -465,25 +585,27 @@ function checkRefTestResults(browser, id, results) {
function refTestPostHandler(req, res) {
var parsedUrl = url.parse(req.url, true);
var pathname = parsedUrl.pathname;
if (pathname !== '/tellMeToQuit' &&
pathname !== '/info' &&
pathname !== '/submit_task_results') {
if (
pathname !== "/tellMeToQuit" &&
pathname !== "/info" &&
pathname !== "/submit_task_results"
) {
return false;
}
var body = '';
req.on('data', function (data) {
var body = "";
req.on("data", function(data) {
body += data;
});
req.on('end', function () {
res.writeHead(200, { 'Content-Type': 'text/plain', });
req.on("end", function() {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end();
var session;
if (pathname === '/tellMeToQuit') {
if (pathname === "/tellMeToQuit") {
// finding by path
var browserPath = parsedUrl.query.path;
session = sessions.filter(function (session) {
session = sessions.filter(function(session) {
return session.config.path === browserPath;
})[0];
monitorBrowserTimeout(session, null);
@ -492,7 +614,7 @@ function refTestPostHandler(req, res) {
}
var data = JSON.parse(body);
if (pathname === '/info') {
if (pathname === "/info") {
console.log(data.message);
return;
}
@ -514,8 +636,17 @@ function refTestPostHandler(req, res) {
}
if (taskResults[round][page]) {
console.error('Results for ' + browser + ':' + id + ':' + round +
':' + page + ' were already submitted');
console.error(
"Results for " +
browser +
":" +
id +
":" +
round +
":" +
page +
" were already submitted"
);
// TODO abort testing here?
}
@ -525,16 +656,17 @@ function refTestPostHandler(req, res) {
};
if (stats) {
stats.push({
'browser': browser,
'pdf': id,
'page': page,
'round': round,
'stats': data.stats,
browser: browser,
pdf: id,
page: page,
round: round,
stats: data.stats,
});
}
var isDone = taskResults[taskResults.length - 1] &&
taskResults[taskResults.length - 1][lastPageNum - 1];
var isDone =
taskResults[taskResults.length - 1] &&
taskResults[taskResults.length - 1][lastPageNum - 1];
if (isDone) {
checkRefTestResults(browser, id, taskResults);
session.remaining--;
@ -546,26 +678,27 @@ function refTestPostHandler(req, res) {
function startUnitTest(url, name) {
var startTime = Date.now();
startServer();
server.hooks['POST'].push(unitTestPostHandler);
onAllSessionsClosed = function () {
server.hooks["POST"].push(unitTestPostHandler);
onAllSessionsClosed = function() {
stopServer();
var numRuns = 0, numErrors = 0;
sessions.forEach(function (session) {
var numRuns = 0,
numErrors = 0;
sessions.forEach(function(session) {
numRuns += session.numRuns;
numErrors += session.numErrors;
});
console.log();
console.log('Run ' + numRuns + ' tests');
console.log("Run " + numRuns + " tests");
if (numErrors > 0) {
console.log('OHNOES! Some ' + name + ' tests failed!');
console.log(' ' + numErrors + ' of ' + numRuns + ' failed');
console.log("OHNOES! Some " + name + " tests failed!");
console.log(" " + numErrors + " of " + numRuns + " failed");
} else {
console.log('All ' + name + ' tests passed.');
console.log("All " + name + " tests passed.");
}
var runtime = (Date.now() - startTime) / 1000;
console.log(name + ' tests runtime was ' + runtime.toFixed(1) + ' seconds');
console.log(name + " tests runtime was " + runtime.toFixed(1) + " seconds");
};
startBrowsers(url, function (session) {
startBrowsers(url, function(session) {
session.numRuns = 0;
session.numErrors = 0;
});
@ -574,56 +707,63 @@ function startUnitTest(url, name) {
function unitTestPostHandler(req, res) {
var parsedUrl = url.parse(req.url);
var pathname = parsedUrl.pathname;
if (pathname !== '/tellMeToQuit' &&
pathname !== '/info' &&
pathname !== '/ttx' &&
pathname !== '/submit_task_results') {
if (
pathname !== "/tellMeToQuit" &&
pathname !== "/info" &&
pathname !== "/ttx" &&
pathname !== "/submit_task_results"
) {
return false;
}
var body = '';
req.on('data', function (data) {
var body = "";
req.on("data", function(data) {
body += data;
});
req.on('end', function () {
if (pathname === '/ttx') {
var translateFont = require('./font/ttxdriver.js').translateFont;
var onCancel = null, ttxTimeout = 10000;
var timeoutId = setTimeout(function () {
req.on("end", function() {
if (pathname === "/ttx") {
var translateFont = require("./font/ttxdriver.js").translateFont;
var onCancel = null,
ttxTimeout = 10000;
var timeoutId = setTimeout(function() {
if (onCancel) {
onCancel('TTX timeout');
onCancel("TTX timeout");
}
}, ttxTimeout);
translateFont(body, function (fn) {
translateFont(
body,
function(fn) {
onCancel = fn;
}, function (err, xml) {
},
function(err, xml) {
clearTimeout(timeoutId);
res.writeHead(200, { 'Content-Type': 'text/xml', });
res.end(err ? '<error>' + err + '</error>' : xml);
});
res.writeHead(200, { "Content-Type": "text/xml" });
res.end(err ? "<error>" + err + "</error>" : xml);
}
);
return;
}
res.writeHead(200, { 'Content-Type': 'text/plain', });
res.writeHead(200, { "Content-Type": "text/plain" });
res.end();
var data = JSON.parse(body);
if (pathname === '/tellMeToQuit') {
if (pathname === "/tellMeToQuit") {
closeSession(data.browser);
return;
}
if (pathname === '/info') {
if (pathname === "/info") {
console.log(data.message);
return;
}
var session = getSession(data.browser);
session.numRuns++;
var message = data.status + ' | ' + data.description;
if (data.status === 'TEST-UNEXPECTED-FAIL') {
var message = data.status + " | " + data.description;
if (data.status === "TEST-UNEXPECTED-FAIL") {
session.numErrors++;
}
if (data.error) {
message += ' | ' + data.error;
message += " | " + data.error;
}
console.log(message);
});
@ -637,21 +777,29 @@ function startBrowsers(url, initSessionCallback) {
} else if (options.browser) {
var browserPath = options.browser;
var name = path.basename(browserPath, path.extname(browserPath));
browsers = [{ name: name, path: browserPath, }];
browsers = [{ name: name, path: browserPath }];
} else {
console.error('Specify either browser or browserManifestFile.');
console.error("Specify either browser or browserManifestFile.");
process.exit(1);
}
sessions = [];
browsers.forEach(function (b) {
browsers.forEach(function(b) {
var browser = WebBrowser.create(b);
var startUrl = getServerBaseAddress() + url +
'?browser=' + encodeURIComponent(b.name) +
'&manifestFile=' + encodeURIComponent('/test/' + options.manifestFile) +
'&testFilter=' + JSON.stringify(options.testfilter) +
'&path=' + encodeURIComponent(b.path) +
'&delay=' + options.statsDelay +
'&masterMode=' + options.masterMode;
var startUrl =
getServerBaseAddress() +
url +
"?browser=" +
encodeURIComponent(b.name) +
"&manifestFile=" +
encodeURIComponent("/test/" + options.manifestFile) +
"&testFilter=" +
JSON.stringify(options.testfilter) +
"&path=" +
encodeURIComponent(b.path) +
"&delay=" +
options.statsDelay +
"&masterMode=" +
options.masterMode;
browser.start(startUrl);
var session = {
name: b.name,
@ -667,14 +815,14 @@ function startBrowsers(url, initSessionCallback) {
}
function getServerBaseAddress() {
return 'http://' + host + ':' + server.port;
return "http://" + host + ":" + server.port;
}
function startServer() {
server = new WebServer();
server.host = host;
server.port = options.port;
server.root = '..';
server.root = "..";
server.cacheExpirationTime = 3600;
server.start();
}
@ -684,7 +832,7 @@ function stopServer() {
}
function getSession(browser) {
return sessions.filter(function (session) {
return sessions.filter(function(session) {
return session.name === browser;
})[0];
}
@ -696,9 +844,9 @@ function closeSession(browser) {
}
if (i < sessions.length) {
var session = sessions[i];
session.browser.stop(function () {
session.browser.stop(function() {
session.closed = true;
var allClosed = sessions.every(function (s) {
var allClosed = sessions.every(function(s) {
return s.closed;
});
if (allClosed && onAllSessionsClosed) {
@ -709,15 +857,19 @@ function closeSession(browser) {
}
function ensurePDFsDownloaded(callback) {
var downloadUtils = require('./downloadutils.js');
var downloadUtils = require("./downloadutils.js");
var manifest = getTestManifest();
downloadUtils.downloadManifestFiles(manifest, function () {
downloadUtils.verifyManifestFiles(manifest, function (hasErrors) {
downloadUtils.downloadManifestFiles(manifest, function() {
downloadUtils.verifyManifestFiles(manifest, function(hasErrors) {
if (hasErrors) {
console.log('Unable to verify the checksum for the files that are ' +
'used for testing.');
console.log('Please re-download the files, or adjust the MD5 ' +
'checksum in the manifest for the files listed above.\n');
console.log(
"Unable to verify the checksum for the files that are " +
"used for testing."
);
console.log(
"Please re-download the files, or adjust the MD5 " +
"checksum in the manifest for the files listed above.\n"
);
if (options.strictVerify) {
process.exit(1);
}
@ -737,11 +889,12 @@ function main() {
} else if (!options.browser && !options.browserManifestFile) {
startServer();
} else if (options.unitTest) {
ensurePDFsDownloaded(function() { // Allows linked PDF files in unit-tests.
startUnitTest('/test/unit/unit_test.html', 'unit');
ensurePDFsDownloaded(function() {
// Allows linked PDF files in unit-tests.
startUnitTest("/test/unit/unit_test.html", "unit");
});
} else if (options.fontTest) {
startUnitTest('/test/font/font_test.html', 'font');
startUnitTest("/test/font/font_test.html", "font");
} else {
startRefTest(options.masterMode, options.reftest);
}
@ -750,7 +903,7 @@ function main() {
var server;
var sessions;
var onAllSessionsClosed;
var host = '127.0.0.1';
var host = "127.0.0.1";
var options = parseOptions();
var stats;

View file

@ -14,11 +14,11 @@
* limitations under the License.
*/
'use strict';
"use strict";
var fs = require('fs');
var path = require('path');
var rimrafSync = require('rimraf').sync;
var fs = require("fs");
var path = require("path");
var rimrafSync = require("rimraf").sync;
exports.removeDirSync = function removeDirSync(dir) {
fs.readdirSync(dir); // Will throw if dir is not a directory
@ -32,7 +32,7 @@ exports.copySubtreeSync = function copySubtreeSync(src, dest) {
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest);
}
files.forEach(function (filename) {
files.forEach(function(filename) {
var srcFile = path.join(src, filename);
var file = path.join(dest, filename);
var stats = fs.statSync(srcFile);
@ -48,7 +48,8 @@ exports.ensureDirSync = function ensureDirSync(dir) {
if (fs.existsSync(dir)) {
return;
}
var parts = dir.split(path.sep), i = parts.length;
var parts = dir.split(path.sep),
i = parts.length;
while (i > 1 && !fs.existsSync(parts.slice(0, i - 1).join(path.sep))) {
i--;
}
@ -62,7 +63,9 @@ exports.ensureDirSync = function ensureDirSync(dir) {
}
};
var stdinBuffer = '', endOfStdin = false, stdinInitialized = false;
var stdinBuffer = "",
endOfStdin = false,
stdinInitialized = false;
var stdinOnLineCallbacks = [];
function handleStdinBuffer() {
@ -80,7 +83,7 @@ function handleStdinBuffer() {
return;
}
while (stdinOnLineCallbacks.length > 0) {
var i = stdinBuffer.indexOf('\n');
var i = stdinBuffer.indexOf("\n");
if (i < 0) {
return;
}
@ -94,14 +97,14 @@ function handleStdinBuffer() {
}
function initStdin() {
process.stdin.setEncoding('utf8');
process.stdin.setEncoding("utf8");
process.stdin.on('data', function(chunk) {
process.stdin.on("data", function(chunk) {
stdinBuffer += chunk;
handleStdinBuffer();
});
process.stdin.on('end', function() {
process.stdin.on("end", function() {
endOfStdin = true;
handleStdinBuffer();
});
@ -122,14 +125,14 @@ exports.prompt = function prompt(message, callback) {
};
exports.confirm = function confirm(message, callback) {
exports.prompt(message, function (answer) {
exports.prompt(message, function(answer) {
if (answer === undefined) {
callback();
return;
}
if (answer[0].toLowerCase() === 'y') {
if (answer[0].toLowerCase() === "y") {
callback(true);
} else if (answer[0].toLowerCase() === 'n') {
} else if (answer[0].toLowerCase() === "n") {
callback(false);
} else {
confirm(message, callback);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -13,25 +13,25 @@
* limitations under the License.
*/
import { bidi } from '../../src/core/bidi';
import { bidi } from "../../src/core/bidi";
describe('bidi', function () {
it('should mark text as RTL if more than 30% of text is RTL', function() {
describe("bidi", function() {
it("should mark text as RTL if more than 30% of text is RTL", function() {
// 33% of test text are RTL characters
var test = '\u0645\u0635\u0631 Egypt';
var result = 'Egypt \u0631\u0635\u0645';
var test = "\u0645\u0635\u0631 Egypt";
var result = "Egypt \u0631\u0635\u0645";
var bidiText = bidi(test, -1, false);
expect(bidiText.str).toEqual(result);
expect(bidiText.dir).toEqual('rtl');
expect(bidiText.dir).toEqual("rtl");
});
it('should mark text as LTR if less than 30% of text is RTL', function() {
var test = 'Egypt is known as \u0645\u0635\u0631 in Arabic.';
var result = 'Egypt is known as \u0631\u0635\u0645 in Arabic.';
it("should mark text as LTR if less than 30% of text is RTL", function() {
var test = "Egypt is known as \u0645\u0635\u0631 in Arabic.";
var result = "Egypt is known as \u0631\u0635\u0645 in Arabic.";
var bidiText = bidi(test, -1, false);
expect(bidiText.str).toEqual(result);
expect(bidiText.dir).toEqual('ltr');
expect(bidiText.dir).toEqual("ltr");
});
});

View file

@ -14,12 +14,16 @@
*/
import {
CFFCharset, CFFCompiler, CFFFDSelect, CFFParser, CFFStrings
} from '../../src/core/cff_parser';
import { SEAC_ANALYSIS_ENABLED } from '../../src/core/fonts';
import { Stream } from '../../src/core/stream';
CFFCharset,
CFFCompiler,
CFFFDSelect,
CFFParser,
CFFStrings,
} from "../../src/core/cff_parser";
import { SEAC_ANALYSIS_ENABLED } from "../../src/core/fonts";
import { Stream } from "../../src/core/stream";
describe('CFFParser', function() {
describe("CFFParser", function() {
function createWithNullProto(obj) {
var result = Object.create(null);
for (var i in obj) {
@ -37,19 +41,20 @@ describe('CFFParser', function() {
var fontData, parser, cff;
beforeAll(function (done) {
beforeAll(function(done) {
// This example font comes from the CFF spec:
// http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
var exampleFont = '0100040100010101134142434445462b' +
'54696d65732d526f6d616e000101011f' +
'f81b00f81c02f81d03f819041c6f000d' +
'fb3cfb6efa7cfa1605e911b8f1120003' +
'01010813183030312e30303754696d65' +
'7320526f6d616e54696d657300000002' +
'010102030e0e7d99f92a99fb7695f773' +
'8b06f79a93fc7c8c077d99f85695f75e' +
'9908fb6e8cf87393f7108b09a70adf0b' +
'f78e14';
var exampleFont =
"0100040100010101134142434445462b" +
"54696d65732d526f6d616e000101011f" +
"f81b00f81c02f81d03f819041c6f000d" +
"fb3cfb6efa7cfa1605e911b8f1120003" +
"01010813183030312e30303754696d65" +
"7320526f6d616e54696d657300000002" +
"010102030e0e7d99f92a99fb7695f773" +
"8b06f79a93fc7c8c077d99f85695f75e" +
"9908fb6e8cf87393f7108b09a70adf0b" +
"f78e14";
var fontArr = [];
for (var i = 0, ii = exampleFont.length; i < ii; i += 2) {
var hex = exampleFont.substring(i, i + 2);
@ -59,22 +64,22 @@ describe('CFFParser', function() {
done();
});
afterAll(function () {
afterAll(function() {
fontData = null;
});
beforeEach(function (done) {
beforeEach(function(done) {
parser = new CFFParser(fontData, {}, SEAC_ANALYSIS_ENABLED);
cff = parser.parse();
done();
});
afterEach(function (done) {
afterEach(function(done) {
parser = cff = null;
done();
});
it('parses header', function() {
it("parses header", function() {
var header = cff.header;
expect(header.major).toEqual(1);
expect(header.minor).toEqual(0);
@ -82,62 +87,64 @@ describe('CFFParser', function() {
expect(header.offSize).toEqual(1);
});
it('parses name index', function() {
it("parses name index", function() {
var names = cff.names;
expect(names.length).toEqual(1);
expect(names[0]).toEqual('ABCDEF+Times-Roman');
expect(names[0]).toEqual("ABCDEF+Times-Roman");
});
it('parses string index', function() {
it("parses string index", function() {
var strings = cff.strings;
expect(strings.count).toEqual(3);
expect(strings.get(0)).toEqual('.notdef');
expect(strings.get(391)).toEqual('001.007');
expect(strings.get(0)).toEqual(".notdef");
expect(strings.get(391)).toEqual("001.007");
});
it('parses top dict', function() {
it("parses top dict", function() {
var topDict = cff.topDict;
// 391 version 392 FullName 393 FamilyName 389 Weight 28416 UniqueID
// -168 -218 1000 898 FontBBox 94 CharStrings 45 102 Private
expect(topDict.getByName('version')).toEqual(391);
expect(topDict.getByName('FullName')).toEqual(392);
expect(topDict.getByName('FamilyName')).toEqual(393);
expect(topDict.getByName('Weight')).toEqual(389);
expect(topDict.getByName('UniqueID')).toEqual(28416);
expect(topDict.getByName('FontBBox')).toEqual([-168, -218, 1000, 898]);
expect(topDict.getByName('CharStrings')).toEqual(94);
expect(topDict.getByName('Private')).toEqual([45, 102]);
expect(topDict.getByName("version")).toEqual(391);
expect(topDict.getByName("FullName")).toEqual(392);
expect(topDict.getByName("FamilyName")).toEqual(393);
expect(topDict.getByName("Weight")).toEqual(389);
expect(topDict.getByName("UniqueID")).toEqual(28416);
expect(topDict.getByName("FontBBox")).toEqual([-168, -218, 1000, 898]);
expect(topDict.getByName("CharStrings")).toEqual(94);
expect(topDict.getByName("Private")).toEqual([45, 102]);
});
it('refuses to add topDict key with invalid value (bug 1068432)',
function () {
it("refuses to add topDict key with invalid value (bug 1068432)", function() {
var topDict = cff.topDict;
var defaultValue = topDict.getByName('UnderlinePosition');
var defaultValue = topDict.getByName("UnderlinePosition");
topDict.setByKey(/* [12, 3] = */ 3075, [NaN]);
expect(topDict.getByName('UnderlinePosition')).toEqual(defaultValue);
expect(topDict.getByName("UnderlinePosition")).toEqual(defaultValue);
});
it('ignores reserved commands in parseDict, and refuses to add privateDict ' +
'keys with invalid values (bug 1308536)', function () {
// prettier-ignore
var bytes = new Uint8Array([
it(
"ignores reserved commands in parseDict, and refuses to add privateDict " +
"keys with invalid values (bug 1308536)",
function() {
// prettier-ignore
var bytes = new Uint8Array([
64, 39, 31, 30, 252, 114, 137, 115, 79, 30, 197, 119, 2, 99, 127, 6
]);
parser.bytes = bytes;
var topDict = cff.topDict;
topDict.setByName('Private', [bytes.length, 0]);
parser.bytes = bytes;
var topDict = cff.topDict;
topDict.setByName("Private", [bytes.length, 0]);
var parsePrivateDict = function () {
parser.parsePrivateDict(topDict);
};
expect(parsePrivateDict).not.toThrow();
var parsePrivateDict = function() {
parser.parsePrivateDict(topDict);
};
expect(parsePrivateDict).not.toThrow();
var privateDict = topDict.privateDict;
expect(privateDict.getByName('BlueValues')).toBeNull();
});
var privateDict = topDict.privateDict;
expect(privateDict.getByName("BlueValues")).toBeNull();
}
);
it('parses a CharString having cntrmask', function() {
it("parses a CharString having cntrmask", function() {
// prettier-ignore
var bytes = new Uint8Array([0, 1, // count
1, // offsetSize
@ -164,9 +171,8 @@ describe('CFFParser', function() {
expect(charStrings.get(0).length).toEqual(38);
});
it('parses a CharString endchar with 4 args w/seac enabled', function() {
var parser = new CFFParser(fontData, {},
/* seacAnalysisEnabled = */ true);
it("parses a CharString endchar with 4 args w/seac enabled", function() {
var parser = new CFFParser(fontData, {}, /* seacAnalysisEnabled = */ true);
parser.parse(); // cff
// prettier-ignore
@ -190,9 +196,8 @@ describe('CFFParser', function() {
expect(result.seacs[0][3]).toEqual(194);
});
it('parses a CharString endchar with 4 args w/seac disabled', function() {
var parser = new CFFParser(fontData, {},
/* seacAnalysisEnabled = */ false);
it("parses a CharString endchar with 4 args w/seac disabled", function() {
var parser = new CFFParser(fontData, {}, /* seacAnalysisEnabled = */ false);
parser.parse(); // cff
// prettier-ignore
@ -211,7 +216,7 @@ describe('CFFParser', function() {
expect(result.seacs.length).toEqual(0);
});
it('parses a CharString endchar no args', function() {
it("parses a CharString endchar no args", function() {
// prettier-ignore
var bytes = new Uint8Array([0, 1, // count
1, // offsetSize
@ -228,12 +233,12 @@ describe('CFFParser', function() {
expect(result.seacs.length).toEqual(0);
});
it('parses predefined charsets', function() {
it("parses predefined charsets", function() {
var charset = parser.parseCharsets(0, 0, null, true);
expect(charset.predefined).toEqual(true);
});
it('parses charset format 0', function() {
it("parses charset format 0", function() {
// The first three bytes make the offset large enough to skip predefined.
// prettier-ignore
var bytes = new Uint8Array([0x00, 0x00, 0x00,
@ -242,14 +247,14 @@ describe('CFFParser', function() {
]);
parser.bytes = bytes;
var charset = parser.parseCharsets(3, 2, new CFFStrings(), false);
expect(charset.charset[1]).toEqual('exclam');
expect(charset.charset[1]).toEqual("exclam");
// CID font
charset = parser.parseCharsets(3, 2, new CFFStrings(), true);
expect(charset.charset[1]).toEqual(2);
});
it('parses charset format 1', function() {
it("parses charset format 1", function() {
// The first three bytes make the offset large enough to skip predefined.
// prettier-ignore
var bytes = new Uint8Array([0x00, 0x00, 0x00,
@ -259,14 +264,14 @@ describe('CFFParser', function() {
]);
parser.bytes = bytes;
var charset = parser.parseCharsets(3, 2, new CFFStrings(), false);
expect(charset.charset).toEqual(['.notdef', 'quoteright', 'parenleft']);
expect(charset.charset).toEqual([".notdef", "quoteright", "parenleft"]);
// CID font
charset = parser.parseCharsets(3, 2, new CFFStrings(), true);
expect(charset.charset).toEqual(['.notdef', 8, 9]);
expect(charset.charset).toEqual([".notdef", 8, 9]);
});
it('parses charset format 2', function() {
it("parses charset format 2", function() {
// format 2 is the same as format 1 but the left is card16
// The first three bytes make the offset large enough to skip predefined.
// prettier-ignore
@ -277,14 +282,14 @@ describe('CFFParser', function() {
]);
parser.bytes = bytes;
var charset = parser.parseCharsets(3, 2, new CFFStrings(), false);
expect(charset.charset).toEqual(['.notdef', 'quoteright', 'parenleft']);
expect(charset.charset).toEqual([".notdef", "quoteright", "parenleft"]);
// CID font
charset = parser.parseCharsets(3, 2, new CFFStrings(), true);
expect(charset.charset).toEqual(['.notdef', 8, 9]);
expect(charset.charset).toEqual([".notdef", 8, 9]);
});
it('parses encoding format 0', function() {
it("parses encoding format 0", function() {
// The first two bytes make the offset large enough to skip predefined.
// prettier-ignore
var bytes = new Uint8Array([0x00, 0x00,
@ -294,10 +299,10 @@ describe('CFFParser', function() {
]);
parser.bytes = bytes;
var encoding = parser.parseEncoding(2, {}, new CFFStrings(), null);
expect(encoding.encoding).toEqual(createWithNullProto({ 0x8: 1, }));
expect(encoding.encoding).toEqual(createWithNullProto({ 0x8: 1 }));
});
it('parses encoding format 1', function() {
it("parses encoding format 1", function() {
// The first two bytes make the offset large enough to skip predefined.
// prettier-ignore
var bytes = new Uint8Array([0x00, 0x00,
@ -309,10 +314,11 @@ describe('CFFParser', function() {
parser.bytes = bytes;
var encoding = parser.parseEncoding(2, {}, new CFFStrings(), null);
expect(encoding.encoding).toEqual(
createWithNullProto({ 0x7: 0x01, 0x08: 0x02, }));
createWithNullProto({ 0x7: 0x01, 0x08: 0x02 })
);
});
it('parses fdselect format 0', function() {
it("parses fdselect format 0", function() {
// prettier-ignore
var bytes = new Uint8Array([0x00, // format
0x00, // gid: 0 fd: 0
@ -325,7 +331,7 @@ describe('CFFParser', function() {
expect(fdSelect.format).toEqual(0);
});
it('parses fdselect format 3', function() {
it("parses fdselect format 3", function() {
// prettier-ignore
var bytes = new Uint8Array([0x03, // format
0x00, 0x02, // range count
@ -342,7 +348,7 @@ describe('CFFParser', function() {
expect(fdSelect.format).toEqual(3);
});
it('parses invalid fdselect format 3 (bug 1146106)', function() {
it("parses invalid fdselect format 3 (bug 1146106)", function() {
// prettier-ignore
var bytes = new Uint8Array([0x03, // format
0x00, 0x02, // range count
@ -362,18 +368,21 @@ describe('CFFParser', function() {
// TODO fdArray
});
describe('CFFCompiler', function() {
describe("CFFCompiler", function() {
function testParser(bytes) {
bytes = new Uint8Array(bytes);
return new CFFParser({
getBytes: () => {
return bytes;
return new CFFParser(
{
getBytes: () => {
return bytes;
},
},
}, {}, SEAC_ANALYSIS_ENABLED);
{},
SEAC_ANALYSIS_ENABLED
);
}
it('encodes integers', function() {
it("encodes integers", function() {
var c = new CFFCompiler();
// all the examples from the spec
expect(c.encodeInteger(0)).toEqual([0x8b]);
@ -387,23 +396,23 @@ describe('CFFCompiler', function() {
expect(c.encodeInteger(-100000)).toEqual([0x1d, 0xff, 0xfe, 0x79, 0x60]);
});
it('encodes floats', function() {
it("encodes floats", function() {
var c = new CFFCompiler();
expect(c.encodeFloat(-2.25)).toEqual([0x1e, 0xe2, 0xa2, 0x5f]);
expect(c.encodeFloat(5e-11)).toEqual([0x1e, 0x5c, 0x11, 0xff]);
});
it('sanitizes name index', function() {
it("sanitizes name index", function() {
var c = new CFFCompiler();
var nameIndexCompiled = c.compileNameIndex(['[a']);
var nameIndexCompiled = c.compileNameIndex(["[a"]);
var parser = testParser(nameIndexCompiled);
var nameIndex = parser.parseIndex(0);
var names = parser.parseNameIndex(nameIndex.obj);
expect(names).toEqual(['_a']);
expect(names).toEqual(["_a"]);
var longName = '';
var longName = "";
for (var i = 0; i < 129; i++) {
longName += '_';
longName += "_";
}
nameIndexCompiled = c.compileNameIndex([longName]);
parser = testParser(nameIndexCompiled);
@ -412,7 +421,7 @@ describe('CFFCompiler', function() {
expect(names[0].length).toEqual(127);
});
it('compiles fdselect format 0', function() {
it("compiles fdselect format 0", function() {
var fdSelect = new CFFFDSelect(0, [3, 2, 1]);
var c = new CFFCompiler();
var out = c.compileFDSelect(fdSelect);
@ -424,7 +433,7 @@ describe('CFFCompiler', function() {
]);
});
it('compiles fdselect format 3', function() {
it("compiles fdselect format 3", function() {
var fdSelect = new CFFFDSelect(3, [0, 0, 1, 1]);
var c = new CFFCompiler();
var out = c.compileFDSelect(fdSelect);
@ -443,7 +452,7 @@ describe('CFFCompiler', function() {
]);
});
it('compiles fdselect format 3, single range', function() {
it("compiles fdselect format 3, single range", function() {
var fdSelect = new CFFFDSelect(3, [0, 0]);
var c = new CFFCompiler();
var out = c.compileFDSelect(fdSelect);
@ -459,7 +468,7 @@ describe('CFFCompiler', function() {
]);
});
it('compiles charset of CID font', function() {
it("compiles charset of CID font", function() {
var charset = new CFFCharset();
var c = new CFFCompiler();
var numGlyphs = 7;
@ -474,8 +483,8 @@ describe('CFFCompiler', function() {
]);
});
it('compiles charset of non CID font', function() {
var charset = new CFFCharset(false, 0, ['space', 'exclam']);
it("compiles charset of non CID font", function() {
var charset = new CFFCharset(false, 0, ["space", "exclam"]);
var c = new CFFCompiler();
var numGlyphs = 3;
var out = c.compileCharset(charset, numGlyphs, new CFFStrings(), false);

View file

@ -13,15 +13,17 @@
* limitations under the License.
*/
import { setVerbosityLevel, VerbosityLevel } from '../../src/shared/util';
import { isNodeJS } from '../../src/shared/is_node';
import { PDFNodeStream } from '../../src/display/node_stream';
import { setPDFNetworkStreamFactory } from '../../src/display/api';
import { setVerbosityLevel, VerbosityLevel } from "../../src/shared/util";
import { isNodeJS } from "../../src/shared/is_node";
import { PDFNodeStream } from "../../src/display/node_stream";
import { setPDFNetworkStreamFactory } from "../../src/display/api";
// Ensure that this script only runs in Node.js environments.
if (!isNodeJS) {
throw new Error('The `gulp unittestcli` command can only be used in ' +
'Node.js environments.');
throw new Error(
"The `gulp unittestcli` command can only be used in " +
"Node.js environments."
);
}
// Reduce the amount of console "spam", by ignoring `info`/`warn` calls,

View file

@ -13,23 +13,23 @@
* limitations under the License.
*/
import { CMap, CMapFactory, IdentityCMap } from '../../src/core/cmap';
import { DOMCMapReaderFactory } from '../../src/display/display_utils';
import { isNodeJS } from '../../src/shared/is_node';
import { Name } from '../../src/core/primitives';
import { NodeCMapReaderFactory } from './test_utils';
import { StringStream } from '../../src/core/stream';
import { CMap, CMapFactory, IdentityCMap } from "../../src/core/cmap";
import { DOMCMapReaderFactory } from "../../src/display/display_utils";
import { isNodeJS } from "../../src/shared/is_node";
import { Name } from "../../src/core/primitives";
import { NodeCMapReaderFactory } from "./test_utils";
import { StringStream } from "../../src/core/stream";
var cMapUrl = {
dom: '../../external/bcmaps/',
node: './external/bcmaps/',
dom: "../../external/bcmaps/",
node: "./external/bcmaps/",
};
var cMapPacked = true;
describe('cmap', function() {
describe("cmap", function() {
var fetchBuiltInCMap;
beforeAll(function (done) {
beforeAll(function(done) {
// Allow CMap testing in Node.js, e.g. for Travis.
var CMapReaderFactory;
if (isNodeJS) {
@ -44,7 +44,7 @@ describe('cmap', function() {
});
}
fetchBuiltInCMap = function (name) {
fetchBuiltInCMap = function(name) {
return CMapReaderFactory.fetch({
name,
});
@ -52,251 +52,281 @@ describe('cmap', function() {
done();
});
afterAll(function () {
afterAll(function() {
fetchBuiltInCMap = null;
});
it('parses beginbfchar', function(done) {
it("parses beginbfchar", function(done) {
// prettier-ignore
var str = '2 beginbfchar\n' +
'<03> <00>\n' +
'<04> <01>\n' +
'endbfchar\n';
var str = "2 beginbfchar\n" +
"<03> <00>\n" +
"<04> <01>\n" +
"endbfchar\n";
var stream = new StringStream(str);
var cmapPromise = CMapFactory.create({ encoding: stream, });
cmapPromise.then(function (cmap) {
expect(cmap.lookup(0x03)).toEqual(String.fromCharCode(0x00));
expect(cmap.lookup(0x04)).toEqual(String.fromCharCode(0x01));
expect(cmap.lookup(0x05)).toBeUndefined();
done();
}).catch(function (reason) {
done.fail(reason);
});
var cmapPromise = CMapFactory.create({ encoding: stream });
cmapPromise
.then(function(cmap) {
expect(cmap.lookup(0x03)).toEqual(String.fromCharCode(0x00));
expect(cmap.lookup(0x04)).toEqual(String.fromCharCode(0x01));
expect(cmap.lookup(0x05)).toBeUndefined();
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('parses beginbfrange with range', function(done) {
it("parses beginbfrange with range", function(done) {
// prettier-ignore
var str = '1 beginbfrange\n' +
'<06> <0B> 0\n' +
'endbfrange\n';
var str = "1 beginbfrange\n" +
"<06> <0B> 0\n" +
"endbfrange\n";
var stream = new StringStream(str);
var cmapPromise = CMapFactory.create({ encoding: stream, });
cmapPromise.then(function (cmap) {
expect(cmap.lookup(0x05)).toBeUndefined();
expect(cmap.lookup(0x06)).toEqual(String.fromCharCode(0x00));
expect(cmap.lookup(0x0B)).toEqual(String.fromCharCode(0x05));
expect(cmap.lookup(0x0C)).toBeUndefined();
done();
}).catch(function (reason) {
done.fail(reason);
});
var cmapPromise = CMapFactory.create({ encoding: stream });
cmapPromise
.then(function(cmap) {
expect(cmap.lookup(0x05)).toBeUndefined();
expect(cmap.lookup(0x06)).toEqual(String.fromCharCode(0x00));
expect(cmap.lookup(0x0b)).toEqual(String.fromCharCode(0x05));
expect(cmap.lookup(0x0c)).toBeUndefined();
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('parses beginbfrange with array', function(done) {
it("parses beginbfrange with array", function(done) {
// prettier-ignore
var str = '1 beginbfrange\n' +
'<0D> <12> [ 0 1 2 3 4 5 ]\n' +
'endbfrange\n';
var str = "1 beginbfrange\n" +
"<0D> <12> [ 0 1 2 3 4 5 ]\n" +
"endbfrange\n";
var stream = new StringStream(str);
var cmapPromise = CMapFactory.create({ encoding: stream, });
cmapPromise.then(function (cmap) {
expect(cmap.lookup(0x0C)).toBeUndefined();
expect(cmap.lookup(0x0D)).toEqual(0x00);
expect(cmap.lookup(0x12)).toEqual(0x05);
expect(cmap.lookup(0x13)).toBeUndefined();
done();
}).catch(function (reason) {
done.fail(reason);
});
var cmapPromise = CMapFactory.create({ encoding: stream });
cmapPromise
.then(function(cmap) {
expect(cmap.lookup(0x0c)).toBeUndefined();
expect(cmap.lookup(0x0d)).toEqual(0x00);
expect(cmap.lookup(0x12)).toEqual(0x05);
expect(cmap.lookup(0x13)).toBeUndefined();
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('parses begincidchar', function(done) {
it("parses begincidchar", function(done) {
// prettier-ignore
var str = '1 begincidchar\n' +
'<14> 0\n' +
'endcidchar\n';
var str = "1 begincidchar\n" +
"<14> 0\n" +
"endcidchar\n";
var stream = new StringStream(str);
var cmapPromise = CMapFactory.create({ encoding: stream, });
cmapPromise.then(function (cmap) {
expect(cmap.lookup(0x14)).toEqual(0x00);
expect(cmap.lookup(0x15)).toBeUndefined();
done();
}).catch(function (reason) {
done.fail(reason);
});
var cmapPromise = CMapFactory.create({ encoding: stream });
cmapPromise
.then(function(cmap) {
expect(cmap.lookup(0x14)).toEqual(0x00);
expect(cmap.lookup(0x15)).toBeUndefined();
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('parses begincidrange', function(done) {
it("parses begincidrange", function(done) {
// prettier-ignore
var str = '1 begincidrange\n' +
'<0016> <001B> 0\n' +
'endcidrange\n';
var str = "1 begincidrange\n" +
"<0016> <001B> 0\n" +
"endcidrange\n";
var stream = new StringStream(str);
var cmapPromise = CMapFactory.create({ encoding: stream, });
cmapPromise.then(function (cmap) {
expect(cmap.lookup(0x15)).toBeUndefined();
expect(cmap.lookup(0x16)).toEqual(0x00);
expect(cmap.lookup(0x1B)).toEqual(0x05);
expect(cmap.lookup(0x1C)).toBeUndefined();
done();
}).catch(function (reason) {
done.fail(reason);
});
var cmapPromise = CMapFactory.create({ encoding: stream });
cmapPromise
.then(function(cmap) {
expect(cmap.lookup(0x15)).toBeUndefined();
expect(cmap.lookup(0x16)).toEqual(0x00);
expect(cmap.lookup(0x1b)).toEqual(0x05);
expect(cmap.lookup(0x1c)).toBeUndefined();
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('decodes codespace ranges', function(done) {
it("decodes codespace ranges", function(done) {
// prettier-ignore
var str = '1 begincodespacerange\n' +
'<01> <02>\n' +
'<00000003> <00000004>\n' +
'endcodespacerange\n';
var str = "1 begincodespacerange\n" +
"<01> <02>\n" +
"<00000003> <00000004>\n" +
"endcodespacerange\n";
var stream = new StringStream(str);
var cmapPromise = CMapFactory.create({ encoding: stream, });
cmapPromise.then(function (cmap) {
var c = {};
cmap.readCharCode(String.fromCharCode(1), 0, c);
expect(c.charcode).toEqual(1);
expect(c.length).toEqual(1);
cmap.readCharCode(String.fromCharCode(0, 0, 0, 3), 0, c);
expect(c.charcode).toEqual(3);
expect(c.length).toEqual(4);
done();
}).catch(function (reason) {
done.fail(reason);
});
var cmapPromise = CMapFactory.create({ encoding: stream });
cmapPromise
.then(function(cmap) {
var c = {};
cmap.readCharCode(String.fromCharCode(1), 0, c);
expect(c.charcode).toEqual(1);
expect(c.length).toEqual(1);
cmap.readCharCode(String.fromCharCode(0, 0, 0, 3), 0, c);
expect(c.charcode).toEqual(3);
expect(c.length).toEqual(4);
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('decodes 4 byte codespace ranges', function(done) {
it("decodes 4 byte codespace ranges", function(done) {
// prettier-ignore
var str = '1 begincodespacerange\n' +
'<8EA1A1A1> <8EA1FEFE>\n' +
'endcodespacerange\n';
var str = "1 begincodespacerange\n" +
"<8EA1A1A1> <8EA1FEFE>\n" +
"endcodespacerange\n";
var stream = new StringStream(str);
var cmapPromise = CMapFactory.create({ encoding: stream, });
cmapPromise.then(function (cmap) {
var c = {};
cmap.readCharCode(String.fromCharCode(0x8E, 0xA1, 0xA1, 0xA1), 0, c);
expect(c.charcode).toEqual(0x8EA1A1A1);
expect(c.length).toEqual(4);
done();
}).catch(function (reason) {
done.fail(reason);
});
var cmapPromise = CMapFactory.create({ encoding: stream });
cmapPromise
.then(function(cmap) {
var c = {};
cmap.readCharCode(String.fromCharCode(0x8e, 0xa1, 0xa1, 0xa1), 0, c);
expect(c.charcode).toEqual(0x8ea1a1a1);
expect(c.length).toEqual(4);
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('read usecmap', function(done) {
var str = '/Adobe-Japan1-1 usecmap\n';
it("read usecmap", function(done) {
var str = "/Adobe-Japan1-1 usecmap\n";
var stream = new StringStream(str);
var cmapPromise = CMapFactory.create({
encoding: stream,
fetchBuiltInCMap,
useCMap: null,
});
cmapPromise.then(function (cmap) {
expect(cmap instanceof CMap).toEqual(true);
expect(cmap.useCMap).not.toBeNull();
expect(cmap.builtInCMap).toBeFalsy();
expect(cmap.length).toEqual(0x20A7);
expect(cmap.isIdentityCMap).toEqual(false);
done();
}).catch(function (reason) {
done.fail(reason);
});
cmapPromise
.then(function(cmap) {
expect(cmap instanceof CMap).toEqual(true);
expect(cmap.useCMap).not.toBeNull();
expect(cmap.builtInCMap).toBeFalsy();
expect(cmap.length).toEqual(0x20a7);
expect(cmap.isIdentityCMap).toEqual(false);
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('parses cmapname', function(done) {
var str = '/CMapName /Identity-H def\n';
it("parses cmapname", function(done) {
var str = "/CMapName /Identity-H def\n";
var stream = new StringStream(str);
var cmapPromise = CMapFactory.create({ encoding: stream, });
cmapPromise.then(function (cmap) {
expect(cmap.name).toEqual('Identity-H');
done();
}).catch(function (reason) {
done.fail(reason);
});
var cmapPromise = CMapFactory.create({ encoding: stream });
cmapPromise
.then(function(cmap) {
expect(cmap.name).toEqual("Identity-H");
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('parses wmode', function(done) {
var str = '/WMode 1 def\n';
it("parses wmode", function(done) {
var str = "/WMode 1 def\n";
var stream = new StringStream(str);
var cmapPromise = CMapFactory.create({ encoding: stream, });
cmapPromise.then(function (cmap) {
expect(cmap.vertical).toEqual(true);
done();
}).catch(function (reason) {
done.fail(reason);
});
var cmapPromise = CMapFactory.create({ encoding: stream });
cmapPromise
.then(function(cmap) {
expect(cmap.vertical).toEqual(true);
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('loads built in cmap', function(done) {
it("loads built in cmap", function(done) {
var cmapPromise = CMapFactory.create({
encoding: Name.get('Adobe-Japan1-1'),
encoding: Name.get("Adobe-Japan1-1"),
fetchBuiltInCMap,
useCMap: null,
});
cmapPromise.then(function (cmap) {
expect(cmap instanceof CMap).toEqual(true);
expect(cmap.useCMap).toBeNull();
expect(cmap.builtInCMap).toBeTruthy();
expect(cmap.length).toEqual(0x20A7);
expect(cmap.isIdentityCMap).toEqual(false);
done();
}).catch(function (reason) {
done.fail(reason);
});
cmapPromise
.then(function(cmap) {
expect(cmap instanceof CMap).toEqual(true);
expect(cmap.useCMap).toBeNull();
expect(cmap.builtInCMap).toBeTruthy();
expect(cmap.length).toEqual(0x20a7);
expect(cmap.isIdentityCMap).toEqual(false);
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('loads built in identity cmap', function(done) {
it("loads built in identity cmap", function(done) {
var cmapPromise = CMapFactory.create({
encoding: Name.get('Identity-H'),
encoding: Name.get("Identity-H"),
fetchBuiltInCMap,
useCMap: null,
});
cmapPromise.then(function (cmap) {
expect(cmap instanceof IdentityCMap).toEqual(true);
expect(cmap.vertical).toEqual(false);
expect(cmap.length).toEqual(0x10000);
expect(function() {
return cmap.isIdentityCMap;
}).toThrow(new Error('should not access .isIdentityCMap'));
done();
}).catch(function (reason) {
done.fail(reason);
});
cmapPromise
.then(function(cmap) {
expect(cmap instanceof IdentityCMap).toEqual(true);
expect(cmap.vertical).toEqual(false);
expect(cmap.length).toEqual(0x10000);
expect(function() {
return cmap.isIdentityCMap;
}).toThrow(new Error("should not access .isIdentityCMap"));
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('attempts to load a non-existent built-in CMap', function(done) {
it("attempts to load a non-existent built-in CMap", function(done) {
var cmapPromise = CMapFactory.create({
encoding: Name.get('null'),
encoding: Name.get("null"),
fetchBuiltInCMap,
useCMap: null,
});
cmapPromise.then(function () {
done.fail('No CMap should be loaded');
}, function (reason) {
expect(reason instanceof Error).toEqual(true);
expect(reason.message).toEqual('Unknown CMap name: null');
done();
});
cmapPromise.then(
function() {
done.fail("No CMap should be loaded");
},
function(reason) {
expect(reason instanceof Error).toEqual(true);
expect(reason.message).toEqual("Unknown CMap name: null");
done();
}
);
});
it('attempts to load a built-in CMap without the necessary API parameters',
function(done) {
it("attempts to load a built-in CMap without the necessary API parameters", function(done) {
function tmpFetchBuiltInCMap(name) {
var CMapReaderFactory = isNodeJS ?
new NodeCMapReaderFactory({ }) : new DOMCMapReaderFactory({ });
var CMapReaderFactory = isNodeJS
? new NodeCMapReaderFactory({})
: new DOMCMapReaderFactory({});
return CMapReaderFactory.fetch({
name,
});
}
var cmapPromise = CMapFactory.create({
encoding: Name.get('Adobe-Japan1-1'),
encoding: Name.get("Adobe-Japan1-1"),
fetchBuiltInCMap: tmpFetchBuiltInCMap,
useCMap: null,
});
cmapPromise.then(function () {
done.fail('No CMap should be loaded');
}, function (reason) {
expect(reason instanceof Error).toEqual(true);
expect(reason.message).toEqual(
'The CMap "baseUrl" parameter must be specified, ensure that ' +
'the "cMapUrl" and "cMapPacked" API parameters are provided.');
done();
});
cmapPromise.then(
function() {
done.fail("No CMap should be loaded");
},
function(reason) {
expect(reason instanceof Error).toEqual(true);
expect(reason.message).toEqual(
'The CMap "baseUrl" parameter must be specified, ensure that ' +
'the "cMapUrl" and "cMapPacked" API parameters are provided.'
);
done();
}
);
});
it('attempts to load a built-in CMap with inconsistent API parameters',
function(done) {
it("attempts to load a built-in CMap with inconsistent API parameters", function(done) {
function tmpFetchBuiltInCMap(name) {
let CMapReaderFactory;
if (isNodeJS) {
@ -316,18 +346,23 @@ describe('cmap', function() {
}
let cmapPromise = CMapFactory.create({
encoding: Name.get('Adobe-Japan1-1'),
encoding: Name.get("Adobe-Japan1-1"),
fetchBuiltInCMap: tmpFetchBuiltInCMap,
useCMap: null,
});
cmapPromise.then(function () {
done.fail('No CMap should be loaded');
}, function (reason) {
expect(reason instanceof Error).toEqual(true);
let message = reason.message;
expect(message.startsWith('Unable to load CMap at: ')).toEqual(true);
expect(message.endsWith('/external/bcmaps/Adobe-Japan1-1')).toEqual(true);
done();
});
cmapPromise.then(
function() {
done.fail("No CMap should be loaded");
},
function(reason) {
expect(reason instanceof Error).toEqual(true);
let message = reason.message;
expect(message.startsWith("Unable to load CMap at: ")).toEqual(true);
expect(message.endsWith("/external/bcmaps/Adobe-Japan1-1")).toEqual(
true
);
done();
}
);
});
});

View file

@ -13,24 +13,22 @@
* limitations under the License.
*/
import { Dict, Name, Ref } from '../../src/core/primitives';
import { Stream, StringStream } from '../../src/core/stream';
import { ColorSpace } from '../../src/core/colorspace';
import { PDFFunctionFactory } from '../../src/core/function';
import { XRefMock } from './test_utils';
import { Dict, Name, Ref } from "../../src/core/primitives";
import { Stream, StringStream } from "../../src/core/stream";
import { ColorSpace } from "../../src/core/colorspace";
import { PDFFunctionFactory } from "../../src/core/function";
import { XRefMock } from "./test_utils";
describe('colorspace', function () {
describe('ColorSpace', function () {
it('should be true if decode is not an array', function () {
expect(ColorSpace.isDefaultDecode('string', 0)).toBeTruthy();
describe("colorspace", function() {
describe("ColorSpace", function() {
it("should be true if decode is not an array", function() {
expect(ColorSpace.isDefaultDecode("string", 0)).toBeTruthy();
});
it('should be true if length of decode array is not correct',
function () {
it("should be true if length of decode array is not correct", function() {
expect(ColorSpace.isDefaultDecode([0], 1)).toBeTruthy();
expect(ColorSpace.isDefaultDecode([0, 1, 0], 1)).toBeTruthy();
});
it('should be true if decode map matches the default decode map',
function () {
it("should be true if decode map matches the default decode map", function() {
expect(ColorSpace.isDefaultDecode([], 0)).toBeTruthy();
expect(ColorSpace.isDefaultDecode([0, 0], 1)).toBeFalsy();
@ -39,20 +37,24 @@ describe('colorspace', function () {
expect(ColorSpace.isDefaultDecode([0, 1, 0, 1, 0, 1], 3)).toBeTruthy();
expect(ColorSpace.isDefaultDecode([0, 1, 0, 1, 1, 1], 3)).toBeFalsy();
expect(ColorSpace.isDefaultDecode([0, 1, 0, 1, 0, 1, 0, 1], 4))
.toBeTruthy();
expect(ColorSpace.isDefaultDecode([1, 0, 0, 1, 0, 1, 0, 1], 4))
.toBeFalsy();
expect(
ColorSpace.isDefaultDecode([0, 1, 0, 1, 0, 1, 0, 1], 4)
).toBeTruthy();
expect(
ColorSpace.isDefaultDecode([1, 0, 0, 1, 0, 1, 0, 1], 4)
).toBeFalsy();
});
});
describe('DeviceGrayCS', function () {
it('should handle the case when cs is a Name object', function () {
let cs = Name.get('DeviceGray');
let xref = new XRefMock([{
ref: Ref.get(10, 0),
data: new Dict(),
}]);
describe("DeviceGrayCS", function() {
it("should handle the case when cs is a Name object", function() {
let cs = Name.get("DeviceGray");
let xref = new XRefMock([
{
ref: Ref.get(10, 0),
data: new Dict(),
},
]);
let res = new Dict();
let pdfFunctionFactory = new PDFFunctionFactory({
@ -83,18 +85,21 @@ describe('colorspace', function () {
]);
colorSpace.fillRgb(testDest, 2, 2, 4, 4, 4, 8, testSrc, 0);
expect(colorSpace.getRgb(new Float32Array([0.1]), 0))
.toEqual(new Uint8ClampedArray([26, 26, 26]));
expect(colorSpace.getRgb(new Float32Array([0.1]), 0)).toEqual(
new Uint8ClampedArray([26, 26, 26])
);
expect(colorSpace.getOutputLength(2, 0)).toEqual(6);
expect(colorSpace.isPassthrough(8)).toBeFalsy();
expect(testDest).toEqual(expectedDest);
});
it('should handle the case when cs is an indirect object', function () {
it("should handle the case when cs is an indirect object", function() {
let cs = Ref.get(10, 0);
let xref = new XRefMock([{
ref: cs,
data: Name.get('DeviceGray'),
}]);
let xref = new XRefMock([
{
ref: cs,
data: Name.get("DeviceGray"),
},
]);
let res = new Dict();
let pdfFunctionFactory = new PDFFunctionFactory({
@ -118,21 +123,24 @@ describe('colorspace', function () {
]);
colorSpace.fillRgb(testDest, 2, 2, 3, 3, 3, 8, testSrc, 0);
expect(colorSpace.getRgb(new Float32Array([0.2]), 0))
.toEqual(new Uint8ClampedArray([51, 51, 51]));
expect(colorSpace.getRgb(new Float32Array([0.2]), 0)).toEqual(
new Uint8ClampedArray([51, 51, 51])
);
expect(colorSpace.getOutputLength(3, 1)).toEqual(12);
expect(colorSpace.isPassthrough(8)).toBeFalsy();
expect(testDest).toEqual(expectedDest);
});
});
describe('DeviceRgbCS', function () {
it('should handle the case when cs is a Name object', function () {
let cs = Name.get('DeviceRGB');
let xref = new XRefMock([{
ref: Ref.get(10, 0),
data: new Dict(),
}]);
describe("DeviceRgbCS", function() {
it("should handle the case when cs is a Name object", function() {
let cs = Name.get("DeviceRGB");
let xref = new XRefMock([
{
ref: Ref.get(10, 0),
data: new Dict(),
},
]);
let res = new Dict();
let pdfFunctionFactory = new PDFFunctionFactory({
@ -169,18 +177,21 @@ describe('colorspace', function () {
]);
colorSpace.fillRgb(testDest, 2, 2, 4, 4, 4, 8, testSrc, 0);
expect(colorSpace.getRgb(new Float32Array([0.1, 0.2, 0.3]), 0))
.toEqual(new Uint8ClampedArray([26, 51, 77]));
expect(colorSpace.getRgb(new Float32Array([0.1, 0.2, 0.3]), 0)).toEqual(
new Uint8ClampedArray([26, 51, 77])
);
expect(colorSpace.getOutputLength(4, 0)).toEqual(4);
expect(colorSpace.isPassthrough(8)).toBeTruthy();
expect(testDest).toEqual(expectedDest);
});
it('should handle the case when cs is an indirect object', function () {
it("should handle the case when cs is an indirect object", function() {
let cs = Ref.get(10, 0);
let xref = new XRefMock([{
ref: cs,
data: Name.get('DeviceRGB'),
}]);
let xref = new XRefMock([
{
ref: cs,
data: Name.get("DeviceRGB"),
},
]);
let res = new Dict();
let pdfFunctionFactory = new PDFFunctionFactory({
@ -210,21 +221,24 @@ describe('colorspace', function () {
]);
colorSpace.fillRgb(testDest, 2, 2, 3, 3, 3, 8, testSrc, 0);
expect(colorSpace.getRgb(new Float32Array([0.1, 0.2, 0.3]), 0))
.toEqual(new Uint8ClampedArray([26, 51, 77]));
expect(colorSpace.getRgb(new Float32Array([0.1, 0.2, 0.3]), 0)).toEqual(
new Uint8ClampedArray([26, 51, 77])
);
expect(colorSpace.getOutputLength(4, 1)).toEqual(5);
expect(colorSpace.isPassthrough(8)).toBeTruthy();
expect(testDest).toEqual(expectedDest);
});
});
describe('DeviceCmykCS', function () {
it('should handle the case when cs is a Name object', function () {
let cs = Name.get('DeviceCMYK');
let xref = new XRefMock([{
ref: Ref.get(10, 0),
data: new Dict(),
}]);
describe("DeviceCmykCS", function() {
it("should handle the case when cs is a Name object", function() {
let cs = Name.get("DeviceCMYK");
let xref = new XRefMock([
{
ref: Ref.get(10, 0),
data: new Dict(),
},
]);
let res = new Dict();
let pdfFunctionFactory = new PDFFunctionFactory({
@ -261,18 +275,21 @@ describe('colorspace', function () {
]);
colorSpace.fillRgb(testDest, 2, 2, 4, 4, 4, 8, testSrc, 0);
expect(colorSpace.getRgb(new Float32Array([0.1, 0.2, 0.3, 1]),
0)).toEqual(new Uint8ClampedArray([32, 28, 21]));
expect(
colorSpace.getRgb(new Float32Array([0.1, 0.2, 0.3, 1]), 0)
).toEqual(new Uint8ClampedArray([32, 28, 21]));
expect(colorSpace.getOutputLength(4, 0)).toEqual(3);
expect(colorSpace.isPassthrough(8)).toBeFalsy();
expect(testDest).toEqual(expectedDest);
});
it('should handle the case when cs is an indirect object', function () {
it("should handle the case when cs is an indirect object", function() {
let cs = Ref.get(10, 0);
let xref = new XRefMock([{
ref: cs,
data: Name.get('DeviceCMYK'),
}]);
let xref = new XRefMock([
{
ref: cs,
data: Name.get("DeviceCMYK"),
},
]);
let res = new Dict();
let pdfFunctionFactory = new PDFFunctionFactory({
@ -302,29 +319,29 @@ describe('colorspace', function () {
]);
colorSpace.fillRgb(testDest, 2, 2, 3, 3, 3, 8, testSrc, 0);
expect(colorSpace.getRgb(new Float32Array([0.1, 0.2, 0.3, 1]), 0))
.toEqual(new Uint8ClampedArray([32, 28, 21]));
expect(
colorSpace.getRgb(new Float32Array([0.1, 0.2, 0.3, 1]), 0)
).toEqual(new Uint8ClampedArray([32, 28, 21]));
expect(colorSpace.getOutputLength(4, 1)).toEqual(4);
expect(colorSpace.isPassthrough(8)).toBeFalsy();
expect(testDest).toEqual(expectedDest);
});
});
describe('CalGrayCS', function () {
it('should handle the case when cs is an array', function () {
describe("CalGrayCS", function() {
it("should handle the case when cs is an array", function() {
let params = new Dict();
params.set('WhitePoint', [1, 1, 1]);
params.set('BlackPoint', [0, 0, 0]);
params.set('Gamma', 2.0);
params.set("WhitePoint", [1, 1, 1]);
params.set("BlackPoint", [0, 0, 0]);
params.set("Gamma", 2.0);
let cs = [
Name.get('CalGray'),
params,
];
let xref = new XRefMock([{
ref: Ref.get(10, 0),
data: new Dict(),
}]);
let cs = [Name.get("CalGray"), params];
let xref = new XRefMock([
{
ref: Ref.get(10, 0),
data: new Dict(),
},
]);
let res = new Dict();
let pdfFunctionFactory = new PDFFunctionFactory({
@ -355,30 +372,30 @@ describe('colorspace', function () {
]);
colorSpace.fillRgb(testDest, 2, 2, 4, 4, 4, 8, testSrc, 0);
expect(colorSpace.getRgb(new Float32Array([1.0]), 0))
.toEqual(new Uint8ClampedArray([255, 255, 255]));
expect(colorSpace.getRgb(new Float32Array([1.0]), 0)).toEqual(
new Uint8ClampedArray([255, 255, 255])
);
expect(colorSpace.getOutputLength(4, 0)).toEqual(12);
expect(colorSpace.isPassthrough(8)).toBeFalsy();
expect(testDest).toEqual(expectedDest);
});
});
describe('CalRGBCS', function () {
it('should handle the case when cs is an array', function () {
describe("CalRGBCS", function() {
it("should handle the case when cs is an array", function() {
let params = new Dict();
params.set('WhitePoint', [1, 1, 1]);
params.set('BlackPoint', [0, 0, 0]);
params.set('Gamma', [1, 1, 1]);
params.set('Matrix', [1, 0, 0, 0, 1, 0, 0, 0, 1]);
params.set("WhitePoint", [1, 1, 1]);
params.set("BlackPoint", [0, 0, 0]);
params.set("Gamma", [1, 1, 1]);
params.set("Matrix", [1, 0, 0, 0, 1, 0, 0, 0, 1]);
let cs = [
Name.get('CalRGB'),
params,
];
let xref = new XRefMock([{
ref: Ref.get(10, 0),
data: new Dict(),
}]);
let cs = [Name.get("CalRGB"), params];
let xref = new XRefMock([
{
ref: Ref.get(10, 0),
data: new Dict(),
},
]);
let res = new Dict();
let pdfFunctionFactory = new PDFFunctionFactory({
@ -408,29 +425,29 @@ describe('colorspace', function () {
]);
colorSpace.fillRgb(testDest, 2, 2, 3, 3, 3, 8, testSrc, 0);
expect(colorSpace.getRgb(new Float32Array([0.1, 0.2, 0.3]), 0))
.toEqual(new Uint8ClampedArray([0, 147, 151]));
expect(colorSpace.getRgb(new Float32Array([0.1, 0.2, 0.3]), 0)).toEqual(
new Uint8ClampedArray([0, 147, 151])
);
expect(colorSpace.getOutputLength(4, 0)).toEqual(4);
expect(colorSpace.isPassthrough(8)).toBeFalsy();
expect(testDest).toEqual(expectedDest);
});
});
describe('LabCS', function () {
it('should handle the case when cs is an array', function () {
describe("LabCS", function() {
it("should handle the case when cs is an array", function() {
let params = new Dict();
params.set('WhitePoint', [1, 1, 1]);
params.set('BlackPoint', [0, 0, 0]);
params.set('Range', [-100, 100, -100, 100]);
params.set("WhitePoint", [1, 1, 1]);
params.set("BlackPoint", [0, 0, 0]);
params.set("Range", [-100, 100, -100, 100]);
let cs = [
Name.get('Lab'),
params,
];
let xref = new XRefMock([{
ref: Ref.get(10, 0),
data: new Dict(),
}]);
let cs = [Name.get("Lab"), params];
let xref = new XRefMock([
{
ref: Ref.get(10, 0),
data: new Dict(),
},
]);
let res = new Dict();
let pdfFunctionFactory = new PDFFunctionFactory({
@ -460,8 +477,9 @@ describe('colorspace', function () {
]);
colorSpace.fillRgb(testDest, 2, 2, 3, 3, 3, 8, testSrc, 0);
expect(colorSpace.getRgb([55, 25, 35], 0))
.toEqual(new Uint8ClampedArray([188, 100, 61]));
expect(colorSpace.getRgb([55, 25, 35], 0)).toEqual(
new Uint8ClampedArray([188, 100, 61])
);
expect(colorSpace.getOutputLength(4, 0)).toEqual(4);
expect(colorSpace.isPassthrough(8)).toBeFalsy();
expect(colorSpace.isDefaultDecode([0, 1])).toBeTruthy();
@ -469,24 +487,21 @@ describe('colorspace', function () {
});
});
describe('IndexedCS', function () {
it('should handle the case when cs is an array', function () {
describe("IndexedCS", function() {
it("should handle the case when cs is an array", function() {
// prettier-ignore
let lookup = new Uint8Array([
23, 155, 35,
147, 69, 93,
255, 109, 70
]);
let cs = [
Name.get('Indexed'),
Name.get('DeviceRGB'),
2,
lookup,
];
let xref = new XRefMock([{
ref: Ref.get(10, 0),
data: new Dict(),
}]);
let cs = [Name.get("Indexed"), Name.get("DeviceRGB"), 2, lookup];
let xref = new XRefMock([
{
ref: Ref.get(10, 0),
data: new Dict(),
},
]);
let res = new Dict();
let pdfFunctionFactory = new PDFFunctionFactory({
@ -511,39 +526,44 @@ describe('colorspace', function () {
colorSpace.fillRgb(testDest, 2, 2, 3, 3, 3, 8, testSrc, 0);
expect(colorSpace.getRgb([2], 0)).toEqual(
new Uint8ClampedArray([255, 109, 70]));
new Uint8ClampedArray([255, 109, 70])
);
expect(colorSpace.isPassthrough(8)).toBeFalsy();
expect(colorSpace.isDefaultDecode([0, 1], 1)).toBeTruthy();
expect(testDest).toEqual(expectedDest);
});
});
describe('AlternateCS', function () {
it('should handle the case when cs is an array', function () {
describe("AlternateCS", function() {
it("should handle the case when cs is an array", function() {
let fnDict = new Dict();
fnDict.set('FunctionType', 4);
fnDict.set('Domain', [0.0, 1.0]);
fnDict.set('Range', [0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0]);
fnDict.set('Length', 58);
fnDict.set("FunctionType", 4);
fnDict.set("Domain", [0.0, 1.0]);
fnDict.set("Range", [0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0]);
fnDict.set("Length", 58);
let fn = new StringStream('{ dup 0.84 mul ' +
'exch 0.00 exch ' +
'dup 0.44 mul ' +
'exch 0.21 mul }');
let fn = new StringStream(
"{ dup 0.84 mul " +
"exch 0.00 exch " +
"dup 0.44 mul " +
"exch 0.21 mul }"
);
fn = new Stream(fn.bytes, 0, 58, fnDict);
let fnRef = Ref.get(10, 0);
let cs = [
Name.get('Separation'),
Name.get('LogoGreen'),
Name.get('DeviceCMYK'),
Name.get("Separation"),
Name.get("LogoGreen"),
Name.get("DeviceCMYK"),
fnRef,
];
let xref = new XRefMock([{
ref: fnRef,
data: fn,
}]);
let xref = new XRefMock([
{
ref: fnRef,
data: fn,
},
]);
let res = new Dict();
let pdfFunctionFactory = new PDFFunctionFactory({
@ -567,8 +587,9 @@ describe('colorspace', function () {
]);
colorSpace.fillRgb(testDest, 2, 2, 3, 3, 3, 8, testSrc, 0);
expect(colorSpace.getRgb([0.1], 0))
.toEqual(new Uint8ClampedArray([228, 243, 242]));
expect(colorSpace.getRgb([0.1], 0)).toEqual(
new Uint8ClampedArray([228, 243, 242])
);
expect(colorSpace.isPassthrough(8)).toBeFalsy();
expect(colorSpace.isDefaultDecode([0, 1])).toBeTruthy();
expect(testDest).toEqual(expectedDest);

View file

@ -13,146 +13,171 @@
* limitations under the License.
*/
import { Dict, Ref } from '../../src/core/primitives';
import { Dict, Ref } from "../../src/core/primitives";
import {
getInheritableProperty, toRomanNumerals
} from '../../src/core/core_utils';
import { XRefMock } from './test_utils';
getInheritableProperty,
toRomanNumerals,
} from "../../src/core/core_utils";
import { XRefMock } from "./test_utils";
describe('core_utils', function() {
describe('getInheritableProperty', function() {
it('handles non-dictionary arguments', function() {
expect(getInheritableProperty({ dict: null, key: 'foo', }))
.toEqual(undefined);
expect(getInheritableProperty({ dict: undefined, key: 'foo', }))
.toEqual(undefined);
describe("core_utils", function() {
describe("getInheritableProperty", function() {
it("handles non-dictionary arguments", function() {
expect(getInheritableProperty({ dict: null, key: "foo" })).toEqual(
undefined
);
expect(getInheritableProperty({ dict: undefined, key: "foo" })).toEqual(
undefined
);
});
it('handles dictionaries that do not contain the property', function() {
it("handles dictionaries that do not contain the property", function() {
// Empty dictionary.
const emptyDict = new Dict();
expect(getInheritableProperty({ dict: emptyDict, key: 'foo', }))
.toEqual(undefined);
expect(getInheritableProperty({ dict: emptyDict, key: "foo" })).toEqual(
undefined
);
// Filled dictionary with a different property.
const filledDict = new Dict();
filledDict.set('bar', 'baz');
expect(getInheritableProperty({ dict: filledDict, key: 'foo', }))
.toEqual(undefined);
filledDict.set("bar", "baz");
expect(getInheritableProperty({ dict: filledDict, key: "foo" })).toEqual(
undefined
);
});
it('fetches the property if it is not inherited', function() {
it("fetches the property if it is not inherited", function() {
const ref = Ref.get(10, 0);
const xref = new XRefMock([{ ref, data: 'quux', }]);
const xref = new XRefMock([{ ref, data: "quux" }]);
const dict = new Dict(xref);
// Regular values should be fetched.
dict.set('foo', 'bar');
expect(getInheritableProperty({ dict, key: 'foo', })).toEqual('bar');
dict.set("foo", "bar");
expect(getInheritableProperty({ dict, key: "foo" })).toEqual("bar");
// Array value should be fetched (with references resolved).
dict.set('baz', ['qux', ref]);
expect(getInheritableProperty({ dict, key: 'baz', getArray: true, }))
.toEqual(['qux', 'quux']);
dict.set("baz", ["qux", ref]);
expect(
getInheritableProperty({ dict, key: "baz", getArray: true })
).toEqual(["qux", "quux"]);
});
it('fetches the property if it is inherited and present on one level',
function() {
it("fetches the property if it is inherited and present on one level", function() {
const ref = Ref.get(10, 0);
const xref = new XRefMock([{ ref, data: 'quux', }]);
const xref = new XRefMock([{ ref, data: "quux" }]);
const firstDict = new Dict(xref);
const secondDict = new Dict(xref);
firstDict.set('Parent', secondDict);
firstDict.set("Parent", secondDict);
// Regular values should be fetched.
secondDict.set('foo', 'bar');
expect(getInheritableProperty({ dict: firstDict, key: 'foo', }))
.toEqual('bar');
secondDict.set("foo", "bar");
expect(getInheritableProperty({ dict: firstDict, key: "foo" })).toEqual(
"bar"
);
// Array value should be fetched (with references resolved).
secondDict.set('baz', ['qux', ref]);
expect(getInheritableProperty({ dict: firstDict, key: 'baz',
getArray: true, }))
.toEqual(['qux', 'quux']);
secondDict.set("baz", ["qux", ref]);
expect(
getInheritableProperty({ dict: firstDict, key: "baz", getArray: true })
).toEqual(["qux", "quux"]);
});
it('fetches the property if it is inherited and present on multiple levels',
function() {
it("fetches the property if it is inherited and present on multiple levels", function() {
const ref = Ref.get(10, 0);
const xref = new XRefMock([{ ref, data: 'quux', }]);
const xref = new XRefMock([{ ref, data: "quux" }]);
const firstDict = new Dict(xref);
const secondDict = new Dict(xref);
firstDict.set('Parent', secondDict);
firstDict.set("Parent", secondDict);
// Regular values should be fetched.
firstDict.set('foo', 'bar1');
secondDict.set('foo', 'bar2');
expect(getInheritableProperty({ dict: firstDict, key: 'foo', }))
.toEqual('bar1');
expect(getInheritableProperty({ dict: firstDict, key: 'foo',
getArray: false, stopWhenFound: false, }))
.toEqual(['bar1', 'bar2']);
firstDict.set("foo", "bar1");
secondDict.set("foo", "bar2");
expect(getInheritableProperty({ dict: firstDict, key: "foo" })).toEqual(
"bar1"
);
expect(
getInheritableProperty({
dict: firstDict,
key: "foo",
getArray: false,
stopWhenFound: false,
})
).toEqual(["bar1", "bar2"]);
// Array value should be fetched (with references resolved).
firstDict.set('baz', ['qux1', ref]);
secondDict.set('baz', ['qux2', ref]);
expect(getInheritableProperty({ dict: firstDict, key: 'baz',
getArray: true, stopWhenFound: false, }))
.toEqual([['qux1', 'quux'], ['qux2', 'quux']]);
firstDict.set("baz", ["qux1", ref]);
secondDict.set("baz", ["qux2", ref]);
expect(
getInheritableProperty({
dict: firstDict,
key: "baz",
getArray: true,
stopWhenFound: false,
})
).toEqual([
["qux1", "quux"],
["qux2", "quux"],
]);
});
it('stops searching when the loop limit is reached', function() {
it("stops searching when the loop limit is reached", function() {
const dict = new Dict();
let currentDict = dict;
let parentDict = null;
for (let i = 0; i < 150; i++) { // Exceeds the loop limit of 100.
for (let i = 0; i < 150; i++) {
// Exceeds the loop limit of 100.
parentDict = new Dict();
currentDict.set('Parent', parentDict);
currentDict.set("Parent", parentDict);
currentDict = parentDict;
}
parentDict.set('foo', 'bar'); // Never found because of loop limit.
expect(getInheritableProperty({ dict, key: 'foo', })).toEqual(undefined);
parentDict.set("foo", "bar"); // Never found because of loop limit.
expect(getInheritableProperty({ dict, key: "foo" })).toEqual(undefined);
dict.set('foo', 'baz');
expect(getInheritableProperty({ dict, key: 'foo', getArray: false,
stopWhenFound: false, }))
.toEqual(['baz']);
dict.set("foo", "baz");
expect(
getInheritableProperty({
dict,
key: "foo",
getArray: false,
stopWhenFound: false,
})
).toEqual(["baz"]);
});
});
describe('toRomanNumerals', function() {
it('handles invalid arguments', function() {
for (const input of ['foo', -1, 0]) {
describe("toRomanNumerals", function() {
it("handles invalid arguments", function() {
for (const input of ["foo", -1, 0]) {
expect(function() {
toRomanNumerals(input);
}).toThrow(new Error('The number should be a positive integer.'));
}).toThrow(new Error("The number should be a positive integer."));
}
});
it('converts numbers to uppercase Roman numerals', function() {
expect(toRomanNumerals(1)).toEqual('I');
expect(toRomanNumerals(6)).toEqual('VI');
expect(toRomanNumerals(7)).toEqual('VII');
expect(toRomanNumerals(8)).toEqual('VIII');
expect(toRomanNumerals(10)).toEqual('X');
expect(toRomanNumerals(40)).toEqual('XL');
expect(toRomanNumerals(100)).toEqual('C');
expect(toRomanNumerals(500)).toEqual('D');
expect(toRomanNumerals(1000)).toEqual('M');
expect(toRomanNumerals(2019)).toEqual('MMXIX');
it("converts numbers to uppercase Roman numerals", function() {
expect(toRomanNumerals(1)).toEqual("I");
expect(toRomanNumerals(6)).toEqual("VI");
expect(toRomanNumerals(7)).toEqual("VII");
expect(toRomanNumerals(8)).toEqual("VIII");
expect(toRomanNumerals(10)).toEqual("X");
expect(toRomanNumerals(40)).toEqual("XL");
expect(toRomanNumerals(100)).toEqual("C");
expect(toRomanNumerals(500)).toEqual("D");
expect(toRomanNumerals(1000)).toEqual("M");
expect(toRomanNumerals(2019)).toEqual("MMXIX");
});
it('converts numbers to lowercase Roman numerals', function() {
expect(toRomanNumerals(1, /* lowercase = */ true)).toEqual('i');
expect(toRomanNumerals(6, /* lowercase = */ true)).toEqual('vi');
expect(toRomanNumerals(7, /* lowercase = */ true)).toEqual('vii');
expect(toRomanNumerals(8, /* lowercase = */ true)).toEqual('viii');
expect(toRomanNumerals(10, /* lowercase = */ true)).toEqual('x');
expect(toRomanNumerals(40, /* lowercase = */ true)).toEqual('xl');
expect(toRomanNumerals(100, /* lowercase = */ true)).toEqual('c');
expect(toRomanNumerals(500, /* lowercase = */ true)).toEqual('d');
expect(toRomanNumerals(1000, /* lowercase = */ true)).toEqual('m');
expect(toRomanNumerals(2019, /* lowercase = */ true)).toEqual('mmxix');
it("converts numbers to lowercase Roman numerals", function() {
expect(toRomanNumerals(1, /* lowercase = */ true)).toEqual("i");
expect(toRomanNumerals(6, /* lowercase = */ true)).toEqual("vi");
expect(toRomanNumerals(7, /* lowercase = */ true)).toEqual("vii");
expect(toRomanNumerals(8, /* lowercase = */ true)).toEqual("viii");
expect(toRomanNumerals(10, /* lowercase = */ true)).toEqual("x");
expect(toRomanNumerals(40, /* lowercase = */ true)).toEqual("xl");
expect(toRomanNumerals(100, /* lowercase = */ true)).toEqual("c");
expect(toRomanNumerals(500, /* lowercase = */ true)).toEqual("d");
expect(toRomanNumerals(1000, /* lowercase = */ true)).toEqual("m");
expect(toRomanNumerals(2019, /* lowercase = */ true)).toEqual("mmxix");
});
});
});

View file

@ -14,322 +14,372 @@
*/
import {
AES128Cipher, AES256Cipher, ARCFourCipher, calculateMD5, calculateSHA256,
calculateSHA384, calculateSHA512, CipherTransformFactory, PDF17, PDF20
} from '../../src/core/crypto';
import { Dict, Name } from '../../src/core/primitives';
AES128Cipher,
AES256Cipher,
ARCFourCipher,
calculateMD5,
calculateSHA256,
calculateSHA384,
calculateSHA512,
CipherTransformFactory,
PDF17,
PDF20,
} from "../../src/core/crypto";
import { Dict, Name } from "../../src/core/primitives";
import {
PasswordException, PasswordResponses, stringToBytes
} from '../../src/shared/util';
PasswordException,
PasswordResponses,
stringToBytes,
} from "../../src/shared/util";
describe('crypto', function() {
describe("crypto", function() {
function hex2binary(s) {
var digits = '0123456789ABCDEF';
var digits = "0123456789ABCDEF";
s = s.toUpperCase();
var n = s.length >> 1, i, j;
var n = s.length >> 1,
i,
j;
var result = new Uint8Array(n);
for (i = 0, j = 0; i < n; ++i) {
var d1 = s.charAt(j++);
var d2 = s.charAt(j++);
var value = (digits.indexOf(d1) << 4) | (digits.indexOf(d2));
var value = (digits.indexOf(d1) << 4) | digits.indexOf(d2);
result[i] = value;
}
return result;
}
// RFC 1321, A.5 Test suite
describe('calculateMD5', function() {
it('should pass RFC 1321 test #1', function() {
describe("calculateMD5", function() {
it("should pass RFC 1321 test #1", function() {
var input, result, expected;
input = stringToBytes('');
input = stringToBytes("");
result = calculateMD5(input, 0, input.length);
expected = hex2binary('d41d8cd98f00b204e9800998ecf8427e');
expected = hex2binary("d41d8cd98f00b204e9800998ecf8427e");
expect(result).toEqual(expected);
});
it('should pass RFC 1321 test #2', function() {
it("should pass RFC 1321 test #2", function() {
var input, result, expected;
input = stringToBytes('a');
input = stringToBytes("a");
result = calculateMD5(input, 0, input.length);
expected = hex2binary('0cc175b9c0f1b6a831c399e269772661');
expected = hex2binary("0cc175b9c0f1b6a831c399e269772661");
expect(result).toEqual(expected);
});
it('should pass RFC 1321 test #3', function() {
it("should pass RFC 1321 test #3", function() {
var input, result, expected;
input = stringToBytes('abc');
input = stringToBytes("abc");
result = calculateMD5(input, 0, input.length);
expected = hex2binary('900150983cd24fb0d6963f7d28e17f72');
expected = hex2binary("900150983cd24fb0d6963f7d28e17f72");
expect(result).toEqual(expected);
});
it('should pass RFC 1321 test #4', function() {
it("should pass RFC 1321 test #4", function() {
var input, result, expected;
input = stringToBytes('message digest');
input = stringToBytes("message digest");
result = calculateMD5(input, 0, input.length);
expected = hex2binary('f96b697d7cb7938d525a2f31aaf161d0');
expected = hex2binary("f96b697d7cb7938d525a2f31aaf161d0");
expect(result).toEqual(expected);
});
it('should pass RFC 1321 test #5', function() {
it("should pass RFC 1321 test #5", function() {
var input, result, expected;
input = stringToBytes('abcdefghijklmnopqrstuvwxyz');
input = stringToBytes("abcdefghijklmnopqrstuvwxyz");
result = calculateMD5(input, 0, input.length);
expected = hex2binary('c3fcd3d76192e4007dfb496cca67e13b');
expected = hex2binary("c3fcd3d76192e4007dfb496cca67e13b");
expect(result).toEqual(expected);
});
it('should pass RFC 1321 test #6', function() {
it("should pass RFC 1321 test #6", function() {
var input, result, expected;
input = stringToBytes('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv' +
'wxyz0123456789');
input = stringToBytes(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv" + "wxyz0123456789"
);
result = calculateMD5(input, 0, input.length);
expected = hex2binary('d174ab98d277d9f5a5611c2c9f419d9f');
expected = hex2binary("d174ab98d277d9f5a5611c2c9f419d9f");
expect(result).toEqual(expected);
});
it('should pass RFC 1321 test #7', function() {
it("should pass RFC 1321 test #7", function() {
var input, result, expected;
input = stringToBytes('123456789012345678901234567890123456789012345678' +
'90123456789012345678901234567890');
input = stringToBytes(
"123456789012345678901234567890123456789012345678" +
"90123456789012345678901234567890"
);
result = calculateMD5(input, 0, input.length);
expected = hex2binary('57edf4a22be3c955ac49da2e2107b67a');
expected = hex2binary("57edf4a22be3c955ac49da2e2107b67a");
expect(result).toEqual(expected);
});
});
// http://www.freemedialibrary.com/index.php/RC4_test_vectors are used
describe('ARCFourCipher', function() {
it('should pass test #1', function() {
describe("ARCFourCipher", function() {
it("should pass test #1", function() {
var key, input, result, expected, cipher;
key = hex2binary('0123456789abcdef');
input = hex2binary('0123456789abcdef');
key = hex2binary("0123456789abcdef");
input = hex2binary("0123456789abcdef");
cipher = new ARCFourCipher(key);
result = cipher.encryptBlock(input);
expected = hex2binary('75b7878099e0c596');
expected = hex2binary("75b7878099e0c596");
expect(result).toEqual(expected);
});
it('should pass test #2', function() {
it("should pass test #2", function() {
var key, input, result, expected, cipher;
key = hex2binary('0123456789abcdef');
input = hex2binary('0000000000000000');
key = hex2binary("0123456789abcdef");
input = hex2binary("0000000000000000");
cipher = new ARCFourCipher(key);
result = cipher.encryptBlock(input);
expected = hex2binary('7494c2e7104b0879');
expected = hex2binary("7494c2e7104b0879");
expect(result).toEqual(expected);
});
it('should pass test #3', function() {
it("should pass test #3", function() {
var key, input, result, expected, cipher;
key = hex2binary('0000000000000000');
input = hex2binary('0000000000000000');
key = hex2binary("0000000000000000");
input = hex2binary("0000000000000000");
cipher = new ARCFourCipher(key);
result = cipher.encryptBlock(input);
expected = hex2binary('de188941a3375d3a');
expected = hex2binary("de188941a3375d3a");
expect(result).toEqual(expected);
});
it('should pass test #4', function() {
it("should pass test #4", function() {
var key, input, result, expected, cipher;
key = hex2binary('ef012345');
input = hex2binary('00000000000000000000');
key = hex2binary("ef012345");
input = hex2binary("00000000000000000000");
cipher = new ARCFourCipher(key);
result = cipher.encryptBlock(input);
expected = hex2binary('d6a141a7ec3c38dfbd61');
expected = hex2binary("d6a141a7ec3c38dfbd61");
expect(result).toEqual(expected);
});
it('should pass test #5', function() {
it("should pass test #5", function() {
var key, input, result, expected, cipher;
key = hex2binary('0123456789abcdef');
input = hex2binary('010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'10101010101010101010101010101010101010101010101010101010101010101010' +
'101010101010101010101');
key = hex2binary("0123456789abcdef");
input = hex2binary(
"010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"10101010101010101010101010101010101010101010101010101010101010101010" +
"101010101010101010101"
);
cipher = new ARCFourCipher(key);
result = cipher.encryptBlock(input);
expected = hex2binary('7595c3e6114a09780c4ad452338e1ffd9a1be9498f813d76' +
'533449b6778dcad8c78a8d2ba9ac66085d0e53d59c26c2d1c490c1ebbe0ce66d1b6b' +
'1b13b6b919b847c25a91447a95e75e4ef16779cde8bf0a95850e32af9689444fd377' +
'108f98fdcbd4e726567500990bcc7e0ca3c4aaa304a387d20f3b8fbbcd42a1bd311d' +
'7a4303dda5ab078896ae80c18b0af66dff319616eb784e495ad2ce90d7f772a81747' +
'b65f62093b1e0db9e5ba532fafec47508323e671327df9444432cb7367cec82f5d44' +
'c0d00b67d650a075cd4b70dedd77eb9b10231b6b5b741347396d62897421d43df9b4' +
'2e446e358e9c11a9b2184ecbef0cd8e7a877ef968f1390ec9b3d35a5585cb009290e' +
'2fcde7b5ec66d9084be44055a619d9dd7fc3166f9487f7cb272912426445998514c1' +
'5d53a18c864ce3a2b7555793988126520eacf2e3066e230c91bee4dd5304f5fd0405' +
'b35bd99c73135d3d9bc335ee049ef69b3867bf2d7bd1eaa595d8bfc0066ff8d31509' +
'eb0c6caa006c807a623ef84c3d33c195d23ee320c40de0558157c822d4b8c569d849' +
'aed59d4e0fd7f379586b4b7ff684ed6a189f7486d49b9c4bad9ba24b96abf924372c' +
'8a8fffb10d55354900a77a3db5f205e1b99fcd8660863a159ad4abe40fa48934163d' +
'dde542a6585540fd683cbfd8c00f12129a284deacc4cdefe58be7137541c047126c8' +
'd49e2755ab181ab7e940b0c0');
expected = hex2binary(
"7595c3e6114a09780c4ad452338e1ffd9a1be9498f813d76" +
"533449b6778dcad8c78a8d2ba9ac66085d0e53d59c26c2d1c490c1ebbe0ce66d1b6b" +
"1b13b6b919b847c25a91447a95e75e4ef16779cde8bf0a95850e32af9689444fd377" +
"108f98fdcbd4e726567500990bcc7e0ca3c4aaa304a387d20f3b8fbbcd42a1bd311d" +
"7a4303dda5ab078896ae80c18b0af66dff319616eb784e495ad2ce90d7f772a81747" +
"b65f62093b1e0db9e5ba532fafec47508323e671327df9444432cb7367cec82f5d44" +
"c0d00b67d650a075cd4b70dedd77eb9b10231b6b5b741347396d62897421d43df9b4" +
"2e446e358e9c11a9b2184ecbef0cd8e7a877ef968f1390ec9b3d35a5585cb009290e" +
"2fcde7b5ec66d9084be44055a619d9dd7fc3166f9487f7cb272912426445998514c1" +
"5d53a18c864ce3a2b7555793988126520eacf2e3066e230c91bee4dd5304f5fd0405" +
"b35bd99c73135d3d9bc335ee049ef69b3867bf2d7bd1eaa595d8bfc0066ff8d31509" +
"eb0c6caa006c807a623ef84c3d33c195d23ee320c40de0558157c822d4b8c569d849" +
"aed59d4e0fd7f379586b4b7ff684ed6a189f7486d49b9c4bad9ba24b96abf924372c" +
"8a8fffb10d55354900a77a3db5f205e1b99fcd8660863a159ad4abe40fa48934163d" +
"dde542a6585540fd683cbfd8c00f12129a284deacc4cdefe58be7137541c047126c8" +
"d49e2755ab181ab7e940b0c0"
);
expect(result).toEqual(expected);
});
it('should pass test #6', function() {
it("should pass test #6", function() {
var key, input, result, expected, cipher;
key = hex2binary('fb029e3031323334');
input = hex2binary('aaaa0300000008004500004e661a00008011be640a0001220af' +
'fffff00890089003a000080a601100001000000000000204543454a4548454346434' +
'550464545494546464343414341434143414341414100002000011bd0b604');
key = hex2binary("fb029e3031323334");
input = hex2binary(
"aaaa0300000008004500004e661a00008011be640a0001220af" +
"fffff00890089003a000080a601100001000000000000204543454a4548454346434" +
"550464545494546464343414341434143414341414100002000011bd0b604"
);
cipher = new ARCFourCipher(key);
result = cipher.encryptBlock(input);
expected = hex2binary('f69c5806bd6ce84626bcbefb9474650aad1f7909b0f64d5f' +
'58a503a258b7ed22eb0ea64930d3a056a55742fcce141d485f8aa836dea18df42c53' +
'80805ad0c61a5d6f58f41040b24b7d1a693856ed0d4398e7aee3bf0e2a2ca8f7');
expected = hex2binary(
"f69c5806bd6ce84626bcbefb9474650aad1f7909b0f64d5f" +
"58a503a258b7ed22eb0ea64930d3a056a55742fcce141d485f8aa836dea18df42c53" +
"80805ad0c61a5d6f58f41040b24b7d1a693856ed0d4398e7aee3bf0e2a2ca8f7"
);
expect(result).toEqual(expected);
});
it('should pass test #7', function() {
it("should pass test #7", function() {
var key, input, result, expected, cipher;
key = hex2binary('0123456789abcdef');
input = hex2binary('123456789abcdef0123456789abcdef0123456789abcdef0123' +
'45678');
key = hex2binary("0123456789abcdef");
input = hex2binary(
"123456789abcdef0123456789abcdef0123456789abcdef0123" + "45678"
);
cipher = new ARCFourCipher(key);
result = cipher.encryptBlock(input);
expected = hex2binary('66a0949f8af7d6891f7f832ba833c00c892ebe30143ce287' +
'40011ecf');
expected = hex2binary(
"66a0949f8af7d6891f7f832ba833c00c892ebe30143ce287" + "40011ecf"
);
expect(result).toEqual(expected);
});
});
describe('calculateSHA256', function() {
it('should properly hash abc', function() {
describe("calculateSHA256", function() {
it("should properly hash abc", function() {
var input, result, expected;
input = stringToBytes('abc');
input = stringToBytes("abc");
result = calculateSHA256(input, 0, input.length);
expected = hex2binary('BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9C' +
'B410FF61F20015AD');
expected = hex2binary(
"BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9C" + "B410FF61F20015AD"
);
expect(result).toEqual(expected);
});
it('should properly hash a multiblock input', function() {
it("should properly hash a multiblock input", function() {
var input, result, expected;
input = stringToBytes('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmno' +
'mnopnopq');
input = stringToBytes(
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmno" + "mnopnopq"
);
result = calculateSHA256(input, 0, input.length);
expected = hex2binary('248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167' +
'F6ECEDD419DB06C1');
expected = hex2binary(
"248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167" + "F6ECEDD419DB06C1"
);
expect(result).toEqual(expected);
});
});
describe('calculateSHA384', function() {
it('should properly hash abc', function() {
describe("calculateSHA384", function() {
it("should properly hash abc", function() {
var input, result, expected;
input = stringToBytes('abc');
input = stringToBytes("abc");
result = calculateSHA384(input, 0, input.length);
expected = hex2binary('CB00753F45A35E8BB5A03D699AC65007272C32AB0EDED163' +
'1A8B605A43FF5BED8086072BA1E7CC2358BAECA134C825A7');
expected = hex2binary(
"CB00753F45A35E8BB5A03D699AC65007272C32AB0EDED163" +
"1A8B605A43FF5BED8086072BA1E7CC2358BAECA134C825A7"
);
expect(result).toEqual(expected);
});
it('should properly hash a multiblock input', function() {
it("should properly hash a multiblock input", function() {
var input, result, expected;
input = stringToBytes('abcdefghbcdefghicdefghijdefghijkefghijklfghijklm' +
'ghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrs' +
'mnopqrstnopqrstu');
input = stringToBytes(
"abcdefghbcdefghicdefghijdefghijkefghijklfghijklm" +
"ghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrs" +
"mnopqrstnopqrstu"
);
result = calculateSHA384(input, 0, input.length);
expected = hex2binary('09330C33F71147E83D192FC782CD1B4753111B173B3B05D2' +
'2FA08086E3B0F712FCC7C71A557E2DB966C3E9FA91746039');
expected = hex2binary(
"09330C33F71147E83D192FC782CD1B4753111B173B3B05D2" +
"2FA08086E3B0F712FCC7C71A557E2DB966C3E9FA91746039"
);
expect(result).toEqual(expected);
});
});
describe('calculateSHA512', function() {
it('should properly hash abc', function() {
describe("calculateSHA512", function() {
it("should properly hash abc", function() {
var input, result, expected;
input = stringToBytes('abc');
input = stringToBytes("abc");
result = calculateSHA512(input, 0, input.length);
expected = hex2binary('DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA2' +
'0A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD' +
'454D4423643CE80E2A9AC94FA54CA49F');
expected = hex2binary(
"DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA2" +
"0A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD" +
"454D4423643CE80E2A9AC94FA54CA49F"
);
expect(result).toEqual(expected);
});
it('should properly hash a multiblock input', function() {
it("should properly hash a multiblock input", function() {
var input, result, expected;
input = stringToBytes('abcdefghbcdefghicdefghijdefghijkefghijklfghijklm' +
'ghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrs' +
'mnopqrstnopqrstu');
input = stringToBytes(
"abcdefghbcdefghicdefghijdefghijkefghijklfghijklm" +
"ghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrs" +
"mnopqrstnopqrstu"
);
result = calculateSHA512(input, 0, input.length);
expected = hex2binary('8E959B75DAE313DA8CF4F72814FC143F8F7779C6EB9F7FA1' +
'7299AEADB6889018501D289E4900F7E4331B99DEC4B5433A' +
'C7D329EEB6DD26545E96E55B874BE909');
expected = hex2binary(
"8E959B75DAE313DA8CF4F72814FC143F8F7779C6EB9F7FA1" +
"7299AEADB6889018501D289E4900F7E4331B99DEC4B5433A" +
"C7D329EEB6DD26545E96E55B874BE909"
);
expect(result).toEqual(expected);
});
});
describe('AES128', function() {
describe('Encryption', function() {
it('should be able to encrypt a block', function() {
describe("AES128", function() {
describe("Encryption", function() {
it("should be able to encrypt a block", function() {
var input, key, result, expected, iv, cipher;
input = hex2binary('00112233445566778899aabbccddeeff');
key = hex2binary('000102030405060708090a0b0c0d0e0f');
iv = hex2binary('00000000000000000000000000000000');
input = hex2binary("00112233445566778899aabbccddeeff");
key = hex2binary("000102030405060708090a0b0c0d0e0f");
iv = hex2binary("00000000000000000000000000000000");
cipher = new AES128Cipher(key);
result = cipher.encrypt(input, iv);
expected = hex2binary('69c4e0d86a7b0430d8cdb78070b4c55a');
expected = hex2binary("69c4e0d86a7b0430d8cdb78070b4c55a");
expect(result).toEqual(expected);
});
});
describe('Decryption', function() {
it('should be able to decrypt a block with IV in stream', function() {
describe("Decryption", function() {
it("should be able to decrypt a block with IV in stream", function() {
var input, key, result, expected, cipher;
input = hex2binary('0000000000000000000000000000000069c4e0d86a7b0430d' +
'8cdb78070b4c55a');
key = hex2binary('000102030405060708090a0b0c0d0e0f');
input = hex2binary(
"0000000000000000000000000000000069c4e0d86a7b0430d" +
"8cdb78070b4c55a"
);
key = hex2binary("000102030405060708090a0b0c0d0e0f");
cipher = new AES128Cipher(key);
result = cipher.decryptBlock(input);
expected = hex2binary('00112233445566778899aabbccddeeff');
expected = hex2binary("00112233445566778899aabbccddeeff");
expect(result).toEqual(expected);
});
});
});
describe('AES256', function() {
describe('Encryption', function() {
it('should be able to encrypt a block', function() {
describe("AES256", function() {
describe("Encryption", function() {
it("should be able to encrypt a block", function() {
var input, key, result, expected, iv, cipher;
input = hex2binary('00112233445566778899aabbccddeeff');
key = hex2binary('000102030405060708090a0b0c0d0e0f101112131415161718' +
'191a1b1c1d1e1f');
iv = hex2binary('00000000000000000000000000000000');
input = hex2binary("00112233445566778899aabbccddeeff");
key = hex2binary(
"000102030405060708090a0b0c0d0e0f101112131415161718" +
"191a1b1c1d1e1f"
);
iv = hex2binary("00000000000000000000000000000000");
cipher = new AES256Cipher(key);
result = cipher.encrypt(input, iv);
expected = hex2binary('8ea2b7ca516745bfeafc49904b496089');
expected = hex2binary("8ea2b7ca516745bfeafc49904b496089");
expect(result).toEqual(expected);
});
});
describe('Decryption', function() {
it('should be able to decrypt a block with specified iv', function() {
describe("Decryption", function() {
it("should be able to decrypt a block with specified iv", function() {
var input, key, result, expected, cipher, iv;
input = hex2binary('8ea2b7ca516745bfeafc49904b496089');
key = hex2binary('000102030405060708090a0b0c0d0e0f101112131415161718' +
'191a1b1c1d1e1f');
iv = hex2binary('00000000000000000000000000000000');
input = hex2binary("8ea2b7ca516745bfeafc49904b496089");
key = hex2binary(
"000102030405060708090a0b0c0d0e0f101112131415161718" +
"191a1b1c1d1e1f"
);
iv = hex2binary("00000000000000000000000000000000");
cipher = new AES256Cipher(key);
result = cipher.decryptBlock(input, false, iv);
expected = hex2binary('00112233445566778899aabbccddeeff');
expected = hex2binary("00112233445566778899aabbccddeeff");
expect(result).toEqual(expected);
});
it('should be able to decrypt a block with IV in stream', function() {
it("should be able to decrypt a block with IV in stream", function() {
var input, key, result, expected, cipher;
input = hex2binary('000000000000000000000000000000008ea2b7ca516745bf' +
'eafc49904b496089');
key = hex2binary('000102030405060708090a0b0c0d0e0f101112131415161718' +
'191a1b1c1d1e1f');
input = hex2binary(
"000000000000000000000000000000008ea2b7ca516745bf" +
"eafc49904b496089"
);
key = hex2binary(
"000102030405060708090a0b0c0d0e0f101112131415161718" +
"191a1b1c1d1e1f"
);
cipher = new AES256Cipher(key);
result = cipher.decryptBlock(input, false);
expected = hex2binary('00112233445566778899aabbccddeeff');
expected = hex2binary("00112233445566778899aabbccddeeff");
expect(result).toEqual(expected);
});
});
});
describe('PDF17Algorithm', function() {
it('should correctly check a user key', function() {
describe("PDF17Algorithm", function() {
it("should correctly check a user key", function() {
var password, userValidation, userPassword, alg, result;
alg = new PDF17();
password = new Uint8Array([117, 115, 101, 114]);
@ -343,7 +393,7 @@ describe('crypto', function() {
expect(result).toEqual(true);
});
it('should correctly check an owner key', function () {
it("should correctly check an owner key", function() {
var password, ownerValidation, ownerPassword, alg, result, uBytes;
alg = new PDF17();
password = new Uint8Array([111, 119, 110, 101, 114]);
@ -359,12 +409,16 @@ describe('crypto', function() {
249, 105, 145, 151, 15, 194, 65, 3, 1, 126, 187,
221, 117, 169, 4, 32, 159, 101, 22, 220, 168,
94, 215, 192, 100, 38, 188, 40]);
result = alg.checkOwnerPassword(password, ownerValidation, uBytes,
ownerPassword);
result = alg.checkOwnerPassword(
password,
ownerValidation,
uBytes,
ownerPassword
);
expect(result).toEqual(true);
});
it('should generate a file encryption key from the user key', function () {
it("should generate a file encryption key from the user key", function() {
var password, userKeySalt, expected, alg, result, userEncryption;
alg = new PDF17();
password = new Uint8Array([117, 115, 101, 114]);
@ -383,7 +437,7 @@ describe('crypto', function() {
expect(result).toEqual(expected);
});
it('should generate a file encryption key from the owner key', function () {
it("should generate a file encryption key from the owner key", function() {
var password, ownerKeySalt, expected, alg, result, ownerEncryption;
var uBytes;
alg = new PDF17();
@ -410,8 +464,8 @@ describe('crypto', function() {
});
});
describe('PDF20Algorithm', function() {
it('should correctly check a user key', function () {
describe("PDF20Algorithm", function() {
it("should correctly check a user key", function() {
var password, userValidation, userPassword, alg, result;
alg = new PDF20();
password = new Uint8Array([117, 115, 101, 114]);
@ -425,7 +479,7 @@ describe('crypto', function() {
expect(result).toEqual(true);
});
it('should correctly check an owner key', function () {
it("should correctly check an owner key", function() {
var password, ownerValidation, ownerPassword, alg, result, uBytes;
alg = new PDF20();
password = new Uint8Array([111, 119, 110, 101, 114]);
@ -441,12 +495,16 @@ describe('crypto', function() {
156, 187, 233, 166, 223, 163, 253, 147, 235, 95,
184, 83, 245, 146, 101, 198, 247, 34, 198, 191,
11, 16, 94, 237, 216, 20, 175]);
result = alg.checkOwnerPassword(password, ownerValidation, uBytes,
ownerPassword);
result = alg.checkOwnerPassword(
password,
ownerValidation,
uBytes,
ownerPassword
);
expect(result).toEqual(true);
});
it('should generate a file encryption key from the user key', function () {
it("should generate a file encryption key from the user key", function() {
var password, userKeySalt, expected, alg, result, userEncryption;
alg = new PDF20();
password = new Uint8Array([117, 115, 101, 114]);
@ -465,7 +523,7 @@ describe('crypto', function() {
expect(result).toEqual(expected);
});
it('should generate a file encryption key from the owner key', function () {
it("should generate a file encryption key from the owner key", function() {
var password, ownerKeySalt, expected, alg, result, ownerEncryption;
var uBytes;
alg = new PDF20();
@ -493,7 +551,7 @@ describe('crypto', function() {
});
});
describe('CipherTransformFactory', function() {
describe("CipherTransformFactory", function() {
function buildDict(map) {
var dict = new Dict();
for (var key in map) {
@ -505,9 +563,9 @@ describe('CipherTransformFactory', function() {
function ensurePasswordCorrect(done, dict, fileId, password) {
try {
var factory = new CipherTransformFactory(dict, fileId, password);
expect('createCipherTransform' in factory).toEqual(true);
expect("createCipherTransform" in factory).toEqual(true);
} catch (ex) {
done.fail('Password should be accepted: ' + ex);
done.fail("Password should be accepted: " + ex);
return;
}
done();
@ -524,7 +582,7 @@ describe('CipherTransformFactory', function() {
done();
return;
}
done.fail('Password should be rejected.');
done.fail("Password should be rejected.");
}
function ensurePasswordIncorrect(done, dict, fileId, password) {
@ -538,104 +596,144 @@ describe('CipherTransformFactory', function() {
done();
return;
}
done.fail('Password should be rejected.');
done.fail("Password should be rejected.");
}
var fileId1, fileId2, dict1, dict2;
var aes256Dict, aes256IsoDict, aes256BlankDict, aes256IsoBlankDict;
beforeAll(function (done) {
fileId1 = unescape('%F6%C6%AF%17%F3rR%8DRM%9A%80%D1%EF%DF%18');
fileId2 = unescape('%3CL_%3AD%96%AF@%9A%9D%B3%3Cx%1Cv%AC');
beforeAll(function(done) {
fileId1 = unescape("%F6%C6%AF%17%F3rR%8DRM%9A%80%D1%EF%DF%18");
fileId2 = unescape("%3CL_%3AD%96%AF@%9A%9D%B3%3Cx%1Cv%AC");
dict1 = buildDict({
Filter: Name.get('Standard'),
Filter: Name.get("Standard"),
V: 2,
Length: 128,
O: unescape('%80%C3%04%96%91o%20sl%3A%E6%1B%13T%91%F2%0DV%12%E3%FF%5E%B' +
'B%E9VO%D8k%9A%CA%7C%5D'),
U: unescape('j%0C%8D%3EY%19%00%BCjd%7D%91%BD%AA%00%18%00%00%00%00%00%00' +
'%00%00%00%00%00%00%00%00%00%00'),
O: unescape(
"%80%C3%04%96%91o%20sl%3A%E6%1B%13T%91%F2%0DV%12%E3%FF%5E%B" +
"B%E9VO%D8k%9A%CA%7C%5D"
),
U: unescape(
"j%0C%8D%3EY%19%00%BCjd%7D%91%BD%AA%00%18%00%00%00%00%00%00" +
"%00%00%00%00%00%00%00%00%00%00"
),
P: -1028,
R: 3,
});
dict2 = buildDict({
Filter: Name.get('Standard'),
Filter: Name.get("Standard"),
V: 4,
Length: 128,
O: unescape('sF%14v.y5%27%DB%97%0A5%22%B3%E1%D4%AD%BD%9B%3C%B4%A5%89u%1' +
'5%B2Y%F1h%D9%E9%F4'),
U: unescape('%93%04%89%A9%BF%8AE%A6%88%A2%DB%C2%A0%A8gn%00%00%00%00%00%' +
'00%00%00%00%00%00%00%00%00%00%00'),
O: unescape(
"sF%14v.y5%27%DB%97%0A5%22%B3%E1%D4%AD%BD%9B%3C%B4%A5%89u%1" +
"5%B2Y%F1h%D9%E9%F4"
),
U: unescape(
"%93%04%89%A9%BF%8AE%A6%88%A2%DB%C2%A0%A8gn%00%00%00%00%00%" +
"00%00%00%00%00%00%00%00%00%00%00"
),
P: -1084,
R: 4,
});
aes256Dict = buildDict({
Filter: Name.get('Standard'),
Filter: Name.get("Standard"),
V: 5,
Length: 256,
O: unescape('%3Cb%89%233e%C8%98%D2%B2%E2%E4%86%CD%A3%18%CC%7E%B1%24j2%2' +
'4%7D%D2%AC%ABx%DEl%8Bs%F3vG%99%80%11e%3E%C8%F5%F2%0C%DA%7B' +
'%18x'),
U: unescape('%83%F2%8F%A0W%02%8A%86O%FD%BD%AD%E0I%90%F1%BEQ%C5%0F%F9i%9' +
'1%97%0F%C2A%03%01%7E%BB%DDu%A9%04%20%9Fe%16%DC%A8%5E%D7%C0' +
'd%26%BC%28'),
OE: unescape('%D5%CA%0E%BDnLF%BF%06%C3%0A%BE%9Dd%90U%08%3E%7B%B2%9C%E52' +
'%28%E5%D86%DE%22%26j%DF'),
UE: unescape('%23%96%C3%A9%F533%FF%9E%9E%21%F2%E7K%7D%BE%19%7E%ACr%C3%F' +
'4%89%F5%EA%A5*J%3C%26%11%11'),
Perms: unescape('%D8%FC%844%E5e%0DB%5D%7Ff%FD%3COMM'),
O: unescape(
"%3Cb%89%233e%C8%98%D2%B2%E2%E4%86%CD%A3%18%CC%7E%B1%24j2%2" +
"4%7D%D2%AC%ABx%DEl%8Bs%F3vG%99%80%11e%3E%C8%F5%F2%0C%DA%7B" +
"%18x"
),
U: unescape(
"%83%F2%8F%A0W%02%8A%86O%FD%BD%AD%E0I%90%F1%BEQ%C5%0F%F9i%9" +
"1%97%0F%C2A%03%01%7E%BB%DDu%A9%04%20%9Fe%16%DC%A8%5E%D7%C0" +
"d%26%BC%28"
),
OE: unescape(
"%D5%CA%0E%BDnLF%BF%06%C3%0A%BE%9Dd%90U%08%3E%7B%B2%9C%E52" +
"%28%E5%D86%DE%22%26j%DF"
),
UE: unescape(
"%23%96%C3%A9%F533%FF%9E%9E%21%F2%E7K%7D%BE%19%7E%ACr%C3%F" +
"4%89%F5%EA%A5*J%3C%26%11%11"
),
Perms: unescape("%D8%FC%844%E5e%0DB%5D%7Ff%FD%3COMM"),
P: -1084,
R: 5,
});
aes256IsoDict = buildDict({
Filter: Name.get('Standard'),
Filter: Name.get("Standard"),
V: 5,
Length: 256,
O: unescape('X%E8%3E6%F5%1A%F5%D1%89%7B%DDH%C71%25%D9%1FJs%A7%7F%9E%B0M' +
'-%A3W/%27Z%D9%8D%8E%E8%A9%D0%CA%D6%05%B9%1D%D0%B9.%0BL%87%' +
'95'),
U: unescape('%5E%E6%CDK%A6c%FAL%DB%80%11U9%11%21%A4%96.g%B0%A0%9C%BB%E9' +
'%A6%DF%A3%FD%93%EB_%B8S%F5%92e%C6%F7%22%C6%BF%0B%10%5E%ED%' +
'D8%14%AF'),
OE: unescape('%D1I%E0Mg%9B%C9%B5%BED%DF%14%3EZ8%D2%05%F0%B2%80%EE%7CD%F' +
'E%FD%F4%3El%D0%87%0A%FB'),
UE: unescape('y%D0%02%B5%E6Y%9C%3C%FD%8F%D4%1CT%B4%C4%B1%AD%80%DDk.%14%' +
'5E%BA%873_%18%14%DF%FE%24'),
Perms: unescape('l%AD%0F%A0%EBM%86WM%3E%CB%B5%E0X%C97'),
O: unescape(
"X%E8%3E6%F5%1A%F5%D1%89%7B%DDH%C71%25%D9%1FJs%A7%7F%9E%B0M" +
"-%A3W/%27Z%D9%8D%8E%E8%A9%D0%CA%D6%05%B9%1D%D0%B9.%0BL%87%" +
"95"
),
U: unescape(
"%5E%E6%CDK%A6c%FAL%DB%80%11U9%11%21%A4%96.g%B0%A0%9C%BB%E9" +
"%A6%DF%A3%FD%93%EB_%B8S%F5%92e%C6%F7%22%C6%BF%0B%10%5E%ED%" +
"D8%14%AF"
),
OE: unescape(
"%D1I%E0Mg%9B%C9%B5%BED%DF%14%3EZ8%D2%05%F0%B2%80%EE%7CD%F" +
"E%FD%F4%3El%D0%87%0A%FB"
),
UE: unescape(
"y%D0%02%B5%E6Y%9C%3C%FD%8F%D4%1CT%B4%C4%B1%AD%80%DDk.%14%" +
"5E%BA%873_%18%14%DF%FE%24"
),
Perms: unescape("l%AD%0F%A0%EBM%86WM%3E%CB%B5%E0X%C97"),
P: -1084,
R: 6,
});
aes256BlankDict = buildDict({
Filter: Name.get('Standard'),
Filter: Name.get("Standard"),
V: 5,
Length: 256,
O: unescape('%B8p%04%C3g%26%FCW%CCN%D4%16%A1%E8%950YZ%C9%9E%B1-%97%F3%F' +
'E%03%13%19ffZn%8F%F5%EB%EC%CC5sV%10e%CEl%B5%E9G%C1'),
U: unescape('%83%D4zi%F1O0%961%12%CC%82%CB%CA%BF5y%FD%21%EB%E4%D1%B5%1D' +
'%D6%FA%14%F3%BE%8Fqs%EF%88%DE%E2%E8%DC%F55%E4%B8%16%C8%14%' +
'8De%1E'),
OE: unescape('%8F%19%E8%D4%27%D5%07%CA%C6%A1%11%A6a%5Bt%F4%DF%0F%84%29%' +
'0F%E4%EFF7%5B%5B%11%A0%8F%17e'),
UE: unescape('%81%F5%5D%B0%28%81%E4%7F_%7C%8F%85b%A0%7E%10%D0%88lx%7B%7' +
'EJ%5E%912%B6d%12%27%05%F6'),
Perms: unescape('%86%1562%0D%AE%A2%FB%5D%3B%22%3Dq%12%B2H'),
O: unescape(
"%B8p%04%C3g%26%FCW%CCN%D4%16%A1%E8%950YZ%C9%9E%B1-%97%F3%F" +
"E%03%13%19ffZn%8F%F5%EB%EC%CC5sV%10e%CEl%B5%E9G%C1"
),
U: unescape(
"%83%D4zi%F1O0%961%12%CC%82%CB%CA%BF5y%FD%21%EB%E4%D1%B5%1D" +
"%D6%FA%14%F3%BE%8Fqs%EF%88%DE%E2%E8%DC%F55%E4%B8%16%C8%14%" +
"8De%1E"
),
OE: unescape(
"%8F%19%E8%D4%27%D5%07%CA%C6%A1%11%A6a%5Bt%F4%DF%0F%84%29%" +
"0F%E4%EFF7%5B%5B%11%A0%8F%17e"
),
UE: unescape(
"%81%F5%5D%B0%28%81%E4%7F_%7C%8F%85b%A0%7E%10%D0%88lx%7B%7" +
"EJ%5E%912%B6d%12%27%05%F6"
),
Perms: unescape("%86%1562%0D%AE%A2%FB%5D%3B%22%3Dq%12%B2H"),
P: -1084,
R: 5,
});
aes256IsoBlankDict = buildDict({
Filter: Name.get('Standard'),
Filter: Name.get("Standard"),
V: 5,
Length: 256,
O: unescape('%F7%DB%99U%A6M%ACk%AF%CF%D7AFw%E9%C1%91%CBDgI%23R%CF%0C%15' +
'r%D74%0D%CE%E9%91@%E4%98QF%BF%88%7Ej%DE%AD%8F%F4@%C1'),
U: unescape('%1A%A9%DC%918%83%93k%29%5B%117%B16%DB%E8%8E%FE%28%E5%89%D4' +
'%0E%AD%12%3B%7DN_6fez%8BG%18%05YOh%7DZH%A3Z%87%17*'),
OE: unescape('%A4a%88%20h%1B%7F%CD%D5%CAc%D8R%83%E5%D6%1C%D2%98%07%984%' +
'BA%AF%1B%B4%7FQ%F8%1EU%7D'),
UE: unescape('%A0%0AZU%27%1D%27%2C%0B%FE%0E%A2L%F9b%5E%A1%B9%D6v7b%B26%' +
'A9N%99%F1%A4Deq'),
Perms: unescape('%03%F2i%07%0D%C3%F9%F2%28%80%B7%F5%DD%D1c%EB'),
O: unescape(
"%F7%DB%99U%A6M%ACk%AF%CF%D7AFw%E9%C1%91%CBDgI%23R%CF%0C%15" +
"r%D74%0D%CE%E9%91@%E4%98QF%BF%88%7Ej%DE%AD%8F%F4@%C1"
),
U: unescape(
"%1A%A9%DC%918%83%93k%29%5B%117%B16%DB%E8%8E%FE%28%E5%89%D4" +
"%0E%AD%12%3B%7DN_6fez%8BG%18%05YOh%7DZH%A3Z%87%17*"
),
OE: unescape(
"%A4a%88%20h%1B%7F%CD%D5%CAc%D8R%83%E5%D6%1C%D2%98%07%984%" +
"BA%AF%1B%B4%7FQ%F8%1EU%7D"
),
UE: unescape(
"%A0%0AZU%27%1D%27%2C%0B%FE%0E%A2L%F9b%5E%A1%B9%D6v7b%B26%" +
"A9N%99%F1%A4Deq"
),
Perms: unescape("%03%F2i%07%0D%C3%F9%F2%28%80%B7%F5%DD%D1c%EB"),
P: -1084,
R: 6,
});
@ -643,61 +741,61 @@ describe('CipherTransformFactory', function() {
done();
});
afterAll(function () {
afterAll(function() {
fileId1 = fileId2 = dict1 = dict2 = null;
aes256Dict = aes256IsoDict = aes256BlankDict = aes256IsoBlankDict = null;
});
describe('#ctor', function() {
describe('AES256 Revision 5', function () {
it('should accept user password', function (done) {
ensurePasswordCorrect(done, aes256Dict, fileId1, 'user');
describe("#ctor", function() {
describe("AES256 Revision 5", function() {
it("should accept user password", function(done) {
ensurePasswordCorrect(done, aes256Dict, fileId1, "user");
});
it('should accept owner password', function (done) {
ensurePasswordCorrect(done, aes256Dict, fileId1, 'owner');
it("should accept owner password", function(done) {
ensurePasswordCorrect(done, aes256Dict, fileId1, "owner");
});
it('should not accept blank password', function (done) {
it("should not accept blank password", function(done) {
ensurePasswordNeeded(done, aes256Dict, fileId1);
});
it('should not accept wrong password', function (done) {
ensurePasswordIncorrect(done, aes256Dict, fileId1, 'wrong');
it("should not accept wrong password", function(done) {
ensurePasswordIncorrect(done, aes256Dict, fileId1, "wrong");
});
it('should accept blank password', function (done) {
it("should accept blank password", function(done) {
ensurePasswordCorrect(done, aes256BlankDict, fileId1);
});
});
describe('AES256 Revision 6', function () {
it('should accept user password', function (done) {
ensurePasswordCorrect(done, aes256IsoDict, fileId1, 'user');
describe("AES256 Revision 6", function() {
it("should accept user password", function(done) {
ensurePasswordCorrect(done, aes256IsoDict, fileId1, "user");
});
it('should accept owner password', function (done) {
ensurePasswordCorrect(done, aes256IsoDict, fileId1, 'owner');
it("should accept owner password", function(done) {
ensurePasswordCorrect(done, aes256IsoDict, fileId1, "owner");
});
it('should not accept blank password', function (done) {
it("should not accept blank password", function(done) {
ensurePasswordNeeded(done, aes256IsoDict, fileId1);
});
it('should not accept wrong password', function (done) {
ensurePasswordIncorrect(done, aes256IsoDict, fileId1, 'wrong');
it("should not accept wrong password", function(done) {
ensurePasswordIncorrect(done, aes256IsoDict, fileId1, "wrong");
});
it('should accept blank password', function (done) {
it("should accept blank password", function(done) {
ensurePasswordCorrect(done, aes256IsoBlankDict, fileId1);
});
});
it('should accept user password', function (done) {
ensurePasswordCorrect(done, dict1, fileId1, '123456');
it("should accept user password", function(done) {
ensurePasswordCorrect(done, dict1, fileId1, "123456");
});
it('should accept owner password', function (done) {
ensurePasswordCorrect(done, dict1, fileId1, '654321');
it("should accept owner password", function(done) {
ensurePasswordCorrect(done, dict1, fileId1, "654321");
});
it('should not accept blank password', function (done) {
it("should not accept blank password", function(done) {
ensurePasswordNeeded(done, dict1, fileId1);
});
it('should not accept wrong password', function (done) {
ensurePasswordIncorrect(done, dict1, fileId1, 'wrong');
it("should not accept wrong password", function(done) {
ensurePasswordIncorrect(done, dict1, fileId1, "wrong");
});
it('should accept blank password', function (done) {
it("should accept blank password", function(done) {
ensurePasswordCorrect(done, dict2, fileId2);
});
});

View file

@ -13,10 +13,10 @@
* limitations under the License.
*/
import { buildGetDocumentParams, NodeCanvasFactory } from './test_utils';
import { DOMCanvasFactory } from '../../src/display/display_utils';
import { getDocument } from '../../src/display/api';
import { isNodeJS } from '../../src/shared/is_node';
import { buildGetDocumentParams, NodeCanvasFactory } from "./test_utils";
import { DOMCanvasFactory } from "../../src/display/display_utils";
import { getDocument } from "../../src/display/api";
import { isNodeJS } from "../../src/shared/is_node";
function getTopLeftPixel(canvasContext) {
let imgData = canvasContext.getImageData(0, 0, 1, 1);
@ -28,8 +28,8 @@ function getTopLeftPixel(canvasContext) {
};
}
describe('custom canvas rendering', function() {
let transparentGetDocumentParams = buildGetDocumentParams('transparent.pdf');
describe("custom canvas rendering", function() {
let transparentGetDocumentParams = buildGetDocumentParams("transparent.pdf");
let CanvasFactory;
let loadingTask;
@ -42,12 +42,15 @@ describe('custom canvas rendering', function() {
CanvasFactory = new DOMCanvasFactory();
}
loadingTask = getDocument(transparentGetDocumentParams);
loadingTask.promise.then(function(doc) {
return doc.getPage(1);
}).then(function(data) {
page = data;
done();
}).catch(done.fail);
loadingTask.promise
.then(function(doc) {
return doc.getPage(1);
})
.then(function(data) {
page = data;
done();
})
.catch(done.fail);
});
afterAll(function(done) {
@ -56,36 +59,48 @@ describe('custom canvas rendering', function() {
loadingTask.destroy().then(done);
});
it('renders to canvas with a default white background', function(done) {
var viewport = page.getViewport({ scale: 1, });
it("renders to canvas with a default white background", function(done) {
var viewport = page.getViewport({ scale: 1 });
var canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height);
const renderTask = page.render({
canvasContext: canvasAndCtx.context,
viewport,
});
renderTask.promise.then(function() {
expect(getTopLeftPixel(canvasAndCtx.context)).toEqual(
{ r: 255, g: 255, b: 255, a: 255, });
CanvasFactory.destroy(canvasAndCtx);
done();
}).catch(done.fail);
renderTask.promise
.then(function() {
expect(getTopLeftPixel(canvasAndCtx.context)).toEqual({
r: 255,
g: 255,
b: 255,
a: 255,
});
CanvasFactory.destroy(canvasAndCtx);
done();
})
.catch(done.fail);
});
it('renders to canvas with a custom background', function(done) {
var viewport = page.getViewport({ scale: 1, });
it("renders to canvas with a custom background", function(done) {
var viewport = page.getViewport({ scale: 1 });
var canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height);
const renderTask = page.render({
canvasContext: canvasAndCtx.context,
viewport,
background: 'rgba(255,0,0,1.0)',
background: "rgba(255,0,0,1.0)",
});
renderTask.promise.then(function() {
expect(getTopLeftPixel(canvasAndCtx.context)).toEqual(
{ r: 255, g: 0, b: 0, a: 255, });
CanvasFactory.destroy(canvasAndCtx);
done();
}).catch(done.fail);
renderTask.promise
.then(function() {
expect(getTopLeftPixel(canvasAndCtx.context)).toEqual({
r: 255,
g: 0,
b: 0,
a: 255,
});
CanvasFactory.destroy(canvasAndCtx);
done();
})
.catch(done.fail);
});
});

View file

@ -14,14 +14,14 @@
*/
/* globals __non_webpack_require__ */
import { setStubs, unsetStubs } from '../../examples/node/domstubs';
import { buildGetDocumentParams } from './test_utils';
import { getDocument } from '../../src/display/api';
import { isNodeJS } from '../../src/shared/is_node';
import { NativeImageDecoding } from '../../src/shared/util';
import { SVGGraphics } from '../../src/display/svg';
import { setStubs, unsetStubs } from "../../examples/node/domstubs";
import { buildGetDocumentParams } from "./test_utils";
import { getDocument } from "../../src/display/api";
import { isNodeJS } from "../../src/shared/is_node";
import { NativeImageDecoding } from "../../src/shared/util";
import { SVGGraphics } from "../../src/display/svg";
const XLINK_NS = 'http://www.w3.org/1999/xlink';
const XLINK_NS = "http://www.w3.org/1999/xlink";
// withZlib(true, callback); = run test with require('zlib') if possible.
// withZlib(false, callback); = run test without require('zlib').deflateSync.
@ -31,7 +31,7 @@ function withZlib(isZlibRequired, callback) {
// We could try to polyfill zlib in the browser, e.g. using pako.
// For now, only support zlib functionality on Node.js
if (!isNodeJS) {
throw new Error('zlib test can only be run in Node.js');
throw new Error("zlib test can only be run in Node.js");
}
return callback();
@ -42,11 +42,11 @@ function withZlib(isZlibRequired, callback) {
return callback();
}
var zlib = __non_webpack_require__('zlib');
var zlib = __non_webpack_require__("zlib");
var deflateSync = zlib.deflateSync;
zlib.deflateSync = disabledDeflateSync;
function disabledDeflateSync() {
throw new Error('zlib.deflateSync is explicitly disabled for testing.');
throw new Error("zlib.deflateSync is explicitly disabled for testing.");
}
function restoreDeflateSync() {
if (zlib.deflateSync === disabledDeflateSync) {
@ -58,13 +58,15 @@ function withZlib(isZlibRequired, callback) {
return promise;
}
describe('SVGGraphics', function () {
describe("SVGGraphics", function() {
var loadingTask;
var page;
beforeAll(function(done) {
loadingTask = getDocument(buildGetDocumentParams('xobject-image.pdf', {
nativeImageDecoderSupport: NativeImageDecoding.DISPLAY,
}));
loadingTask = getDocument(
buildGetDocumentParams("xobject-image.pdf", {
nativeImageDecoderSupport: NativeImageDecoding.DISPLAY,
})
);
loadingTask.promise.then(function(doc) {
doc.getPage(1).then(function(firstPage) {
page = firstPage;
@ -76,42 +78,45 @@ describe('SVGGraphics', function () {
loadingTask.destroy().then(done);
});
describe('paintImageXObject', function() {
describe("paintImageXObject", function() {
function getSVGImage() {
var svgGfx;
return page.getOperatorList().then(function(opList) {
var forceDataSchema = true;
svgGfx = new SVGGraphics(page.commonObjs, page.objs, forceDataSchema);
return svgGfx.loadDependencies(opList);
}).then(function() {
var svgImg;
// A mock to steal the svg:image element from paintInlineImageXObject.
var elementContainer = {
appendChild(element) {
svgImg = element;
},
};
return page
.getOperatorList()
.then(function(opList) {
var forceDataSchema = true;
svgGfx = new SVGGraphics(page.commonObjs, page.objs, forceDataSchema);
return svgGfx.loadDependencies(opList);
})
.then(function() {
var svgImg;
// A mock to steal the svg:image element from paintInlineImageXObject.
var elementContainer = {
appendChild(element) {
svgImg = element;
},
};
// This points to the XObject image in xobject-image.pdf.
var xobjectObjId = 'img_p0_1';
if (isNodeJS) {
setStubs(global);
}
try {
var imgData = svgGfx.objs.get(xobjectObjId);
svgGfx.paintInlineImageXObject(imgData, elementContainer);
} finally {
// This points to the XObject image in xobject-image.pdf.
var xobjectObjId = "img_p0_1";
if (isNodeJS) {
unsetStubs(global);
setStubs(global);
}
}
return svgImg;
});
try {
var imgData = svgGfx.objs.get(xobjectObjId);
svgGfx.paintInlineImageXObject(imgData, elementContainer);
} finally {
if (isNodeJS) {
unsetStubs(global);
}
}
return svgImg;
});
}
it('should fail require("zlib") unless in Node.js', function() {
function testFunc() {
__non_webpack_require__('zlib');
__non_webpack_require__("zlib");
}
// Verifies that the script loader replaces __non_webpack_require__ with
// require.
@ -124,35 +129,39 @@ describe('SVGGraphics', function () {
}
});
it('should produce a reasonably small svg:image', function(done) {
it("should produce a reasonably small svg:image", function(done) {
if (!isNodeJS) {
pending('zlib.deflateSync is not supported in non-Node environments.');
pending("zlib.deflateSync is not supported in non-Node environments.");
}
withZlib(true, getSVGImage).then(function(svgImg) {
expect(svgImg.nodeName).toBe('svg:image');
expect(svgImg.getAttributeNS(null, 'width')).toBe('200px');
expect(svgImg.getAttributeNS(null, 'height')).toBe('100px');
var imgUrl = svgImg.getAttributeNS(XLINK_NS, 'href');
// forceDataSchema = true, so the generated URL should be a data:-URL.
expect(imgUrl).toMatch(/^data:image\/png;base64,/);
// Test whether the generated image has a reasonable file size.
// I obtained a data URL of size 366 with Node 8.1.3 and zlib 1.2.11.
// Without zlib (uncompressed), the size of the data URL was excessive
// (80246).
expect(imgUrl.length).toBeLessThan(367);
}).then(done, done.fail);
withZlib(true, getSVGImage)
.then(function(svgImg) {
expect(svgImg.nodeName).toBe("svg:image");
expect(svgImg.getAttributeNS(null, "width")).toBe("200px");
expect(svgImg.getAttributeNS(null, "height")).toBe("100px");
var imgUrl = svgImg.getAttributeNS(XLINK_NS, "href");
// forceDataSchema = true, so the generated URL should be a data:-URL.
expect(imgUrl).toMatch(/^data:image\/png;base64,/);
// Test whether the generated image has a reasonable file size.
// I obtained a data URL of size 366 with Node 8.1.3 and zlib 1.2.11.
// Without zlib (uncompressed), the size of the data URL was excessive
// (80246).
expect(imgUrl.length).toBeLessThan(367);
})
.then(done, done.fail);
});
it('should be able to produce a svg:image without zlib', function(done) {
withZlib(false, getSVGImage).then(function(svgImg) {
expect(svgImg.nodeName).toBe('svg:image');
expect(svgImg.getAttributeNS(null, 'width')).toBe('200px');
expect(svgImg.getAttributeNS(null, 'height')).toBe('100px');
var imgUrl = svgImg.getAttributeNS(XLINK_NS, 'href');
expect(imgUrl).toMatch(/^data:image\/png;base64,/);
// The size of our naively generated PNG file is excessive :(
expect(imgUrl.length).toBe(80246);
}).then(done, done.fail);
it("should be able to produce a svg:image without zlib", function(done) {
withZlib(false, getSVGImage)
.then(function(svgImg) {
expect(svgImg.nodeName).toBe("svg:image");
expect(svgImg.getAttributeNS(null, "width")).toBe("200px");
expect(svgImg.getAttributeNS(null, "height")).toBe("100px");
var imgUrl = svgImg.getAttributeNS(XLINK_NS, "href");
expect(imgUrl).toMatch(/^data:image\/png;base64,/);
// The size of our naively generated PNG file is excessive :(
expect(imgUrl.length).toBe(80246);
})
.then(done, done.fail);
});
});
});

View file

@ -15,13 +15,16 @@
/* eslint no-var: error */
import {
DOMCanvasFactory, DOMSVGFactory, getFilenameFromUrl, isValidFetchUrl,
PDFDateString
} from '../../src/display/display_utils';
import { isNodeJS } from '../../src/shared/is_node';
DOMCanvasFactory,
DOMSVGFactory,
getFilenameFromUrl,
isValidFetchUrl,
PDFDateString,
} from "../../src/display/display_utils";
import { isNodeJS } from "../../src/shared/is_node";
describe('display_utils', function() {
describe('DOMCanvasFactory', function() {
describe("display_utils", function() {
describe("DOMCanvasFactory", function() {
let canvasFactory;
beforeAll(function(done) {
@ -33,92 +36,88 @@ describe('display_utils', function() {
canvasFactory = null;
});
it('`create` should throw an error if the dimensions are invalid',
function() {
it("`create` should throw an error if the dimensions are invalid", function() {
// Invalid width.
expect(function() {
return canvasFactory.create(-1, 1);
}).toThrow(new Error('Invalid canvas size'));
}).toThrow(new Error("Invalid canvas size"));
// Invalid height.
expect(function() {
return canvasFactory.create(1, -1);
}).toThrow(new Error('Invalid canvas size'));
}).toThrow(new Error("Invalid canvas size"));
});
it('`create` should return a canvas if the dimensions are valid',
function() {
it("`create` should return a canvas if the dimensions are valid", function() {
if (isNodeJS) {
pending('Document is not supported in Node.js.');
pending("Document is not supported in Node.js.");
}
const { canvas, context, } = canvasFactory.create(20, 40);
const { canvas, context } = canvasFactory.create(20, 40);
expect(canvas instanceof HTMLCanvasElement).toBe(true);
expect(context instanceof CanvasRenderingContext2D).toBe(true);
expect(canvas.width).toBe(20);
expect(canvas.height).toBe(40);
});
it('`reset` should throw an error if no canvas is provided', function() {
const canvasAndContext = { canvas: null, context: null, };
it("`reset` should throw an error if no canvas is provided", function() {
const canvasAndContext = { canvas: null, context: null };
expect(function() {
return canvasFactory.reset(canvasAndContext, 20, 40);
}).toThrow(new Error('Canvas is not specified'));
}).toThrow(new Error("Canvas is not specified"));
});
it('`reset` should throw an error if the dimensions are invalid',
function() {
const canvasAndContext = { canvas: 'foo', context: 'bar', };
it("`reset` should throw an error if the dimensions are invalid", function() {
const canvasAndContext = { canvas: "foo", context: "bar" };
// Invalid width.
expect(function() {
return canvasFactory.reset(canvasAndContext, -1, 1);
}).toThrow(new Error('Invalid canvas size'));
}).toThrow(new Error("Invalid canvas size"));
// Invalid height.
expect(function() {
return canvasFactory.reset(canvasAndContext, 1, -1);
}).toThrow(new Error('Invalid canvas size'));
}).toThrow(new Error("Invalid canvas size"));
});
it('`reset` should alter the canvas/context if the dimensions are valid',
function() {
it("`reset` should alter the canvas/context if the dimensions are valid", function() {
if (isNodeJS) {
pending('Document is not supported in Node.js.');
pending("Document is not supported in Node.js.");
}
const canvasAndContext = canvasFactory.create(20, 40);
canvasFactory.reset(canvasAndContext, 60, 80);
const { canvas, context, } = canvasAndContext;
const { canvas, context } = canvasAndContext;
expect(canvas instanceof HTMLCanvasElement).toBe(true);
expect(context instanceof CanvasRenderingContext2D).toBe(true);
expect(canvas.width).toBe(60);
expect(canvas.height).toBe(80);
});
it('`destroy` should throw an error if no canvas is provided', function() {
it("`destroy` should throw an error if no canvas is provided", function() {
expect(function() {
return canvasFactory.destroy({});
}).toThrow(new Error('Canvas is not specified'));
}).toThrow(new Error("Canvas is not specified"));
});
it('`destroy` should clear the canvas/context', function() {
it("`destroy` should clear the canvas/context", function() {
if (isNodeJS) {
pending('Document is not supported in Node.js.');
pending("Document is not supported in Node.js.");
}
const canvasAndContext = canvasFactory.create(20, 40);
canvasFactory.destroy(canvasAndContext);
const { canvas, context, } = canvasAndContext;
const { canvas, context } = canvasAndContext;
expect(canvas).toBe(null);
expect(context).toBe(null);
});
});
describe('DOMSVGFactory', function() {
describe("DOMSVGFactory", function() {
let svgFactory;
beforeAll(function(done) {
@ -130,150 +129,146 @@ describe('display_utils', function() {
svgFactory = null;
});
it('`create` should throw an error if the dimensions are invalid',
function() {
it("`create` should throw an error if the dimensions are invalid", function() {
// Invalid width.
expect(function() {
return svgFactory.create(-1, 0);
}).toThrow(new Error('Invalid SVG dimensions'));
}).toThrow(new Error("Invalid SVG dimensions"));
// Invalid height.
expect(function() {
return svgFactory.create(0, -1);
}).toThrow(new Error('Invalid SVG dimensions'));
}).toThrow(new Error("Invalid SVG dimensions"));
});
it('`create` should return an SVG element if the dimensions are valid',
function() {
it("`create` should return an SVG element if the dimensions are valid", function() {
if (isNodeJS) {
pending('Document is not supported in Node.js.');
pending("Document is not supported in Node.js.");
}
const svg = svgFactory.create(20, 40);
expect(svg instanceof SVGSVGElement).toBe(true);
expect(svg.getAttribute('version')).toBe('1.1');
expect(svg.getAttribute('width')).toBe('20px');
expect(svg.getAttribute('height')).toBe('40px');
expect(svg.getAttribute('preserveAspectRatio')).toBe('none');
expect(svg.getAttribute('viewBox')).toBe('0 0 20 40');
expect(svg.getAttribute("version")).toBe("1.1");
expect(svg.getAttribute("width")).toBe("20px");
expect(svg.getAttribute("height")).toBe("40px");
expect(svg.getAttribute("preserveAspectRatio")).toBe("none");
expect(svg.getAttribute("viewBox")).toBe("0 0 20 40");
});
it('`createElement` should throw an error if the type is not a string',
function() {
it("`createElement` should throw an error if the type is not a string", function() {
expect(function() {
return svgFactory.createElement(true);
}).toThrow(new Error('Invalid SVG element type'));
}).toThrow(new Error("Invalid SVG element type"));
});
it('`createElement` should return an SVG element if the type is valid',
function() {
it("`createElement` should return an SVG element if the type is valid", function() {
if (isNodeJS) {
pending('Document is not supported in Node.js.');
pending("Document is not supported in Node.js.");
}
const svg = svgFactory.createElement('svg:rect');
const svg = svgFactory.createElement("svg:rect");
expect(svg instanceof SVGRectElement).toBe(true);
});
});
describe('getFilenameFromUrl', function() {
it('should get the filename from an absolute URL', function() {
const url = 'https://server.org/filename.pdf';
expect(getFilenameFromUrl(url)).toEqual('filename.pdf');
describe("getFilenameFromUrl", function() {
it("should get the filename from an absolute URL", function() {
const url = "https://server.org/filename.pdf";
expect(getFilenameFromUrl(url)).toEqual("filename.pdf");
});
it('should get the filename from a relative URL', function() {
const url = '../../filename.pdf';
expect(getFilenameFromUrl(url)).toEqual('filename.pdf');
it("should get the filename from a relative URL", function() {
const url = "../../filename.pdf";
expect(getFilenameFromUrl(url)).toEqual("filename.pdf");
});
it('should get the filename from a URL with an anchor', function() {
const url = 'https://server.org/filename.pdf#foo';
expect(getFilenameFromUrl(url)).toEqual('filename.pdf');
it("should get the filename from a URL with an anchor", function() {
const url = "https://server.org/filename.pdf#foo";
expect(getFilenameFromUrl(url)).toEqual("filename.pdf");
});
it('should get the filename from a URL with query parameters', function() {
const url = 'https://server.org/filename.pdf?foo=bar';
expect(getFilenameFromUrl(url)).toEqual('filename.pdf');
it("should get the filename from a URL with query parameters", function() {
const url = "https://server.org/filename.pdf?foo=bar";
expect(getFilenameFromUrl(url)).toEqual("filename.pdf");
});
});
describe('isValidFetchUrl', function() {
it('handles invalid Fetch URLs', function() {
describe("isValidFetchUrl", function() {
it("handles invalid Fetch URLs", function() {
expect(isValidFetchUrl(null)).toEqual(false);
expect(isValidFetchUrl(100)).toEqual(false);
expect(isValidFetchUrl('foo')).toEqual(false);
expect(isValidFetchUrl('/foo', 100)).toEqual(false);
expect(isValidFetchUrl("foo")).toEqual(false);
expect(isValidFetchUrl("/foo", 100)).toEqual(false);
});
it('handles relative Fetch URLs', function() {
expect(isValidFetchUrl('/foo', 'file://www.example.com')).toEqual(false);
expect(isValidFetchUrl('/foo', 'http://www.example.com')).toEqual(true);
it("handles relative Fetch URLs", function() {
expect(isValidFetchUrl("/foo", "file://www.example.com")).toEqual(false);
expect(isValidFetchUrl("/foo", "http://www.example.com")).toEqual(true);
});
it('handles unsupported Fetch protocols', function() {
expect(isValidFetchUrl('file://www.example.com')).toEqual(false);
expect(isValidFetchUrl('ftp://www.example.com')).toEqual(false);
it("handles unsupported Fetch protocols", function() {
expect(isValidFetchUrl("file://www.example.com")).toEqual(false);
expect(isValidFetchUrl("ftp://www.example.com")).toEqual(false);
});
it('handles supported Fetch protocols', function() {
expect(isValidFetchUrl('http://www.example.com')).toEqual(true);
expect(isValidFetchUrl('https://www.example.com')).toEqual(true);
it("handles supported Fetch protocols", function() {
expect(isValidFetchUrl("http://www.example.com")).toEqual(true);
expect(isValidFetchUrl("https://www.example.com")).toEqual(true);
});
});
describe('PDFDateString', function() {
describe('toDateObject', function() {
it('converts PDF date strings to JavaScript `Date` objects', function() {
describe("PDFDateString", function() {
describe("toDateObject", function() {
it("converts PDF date strings to JavaScript `Date` objects", function() {
const expectations = {
undefined: null,
null: null,
42: null,
'2019': null,
'D2019': null,
'D:': null,
'D:201': null,
'D:2019': new Date(Date.UTC(2019, 0, 1, 0, 0, 0)),
'D:20190': new Date(Date.UTC(2019, 0, 1, 0, 0, 0)),
'D:201900': new Date(Date.UTC(2019, 0, 1, 0, 0, 0)),
'D:201913': new Date(Date.UTC(2019, 0, 1, 0, 0, 0)),
'D:201902': new Date(Date.UTC(2019, 1, 1, 0, 0, 0)),
'D:2019020': new Date(Date.UTC(2019, 1, 1, 0, 0, 0)),
'D:20190200': new Date(Date.UTC(2019, 1, 1, 0, 0, 0)),
'D:20190232': new Date(Date.UTC(2019, 1, 1, 0, 0, 0)),
'D:20190203': new Date(Date.UTC(2019, 1, 3, 0, 0, 0)),
"2019": null,
D2019: null,
"D:": null,
"D:201": null,
"D:2019": new Date(Date.UTC(2019, 0, 1, 0, 0, 0)),
"D:20190": new Date(Date.UTC(2019, 0, 1, 0, 0, 0)),
"D:201900": new Date(Date.UTC(2019, 0, 1, 0, 0, 0)),
"D:201913": new Date(Date.UTC(2019, 0, 1, 0, 0, 0)),
"D:201902": new Date(Date.UTC(2019, 1, 1, 0, 0, 0)),
"D:2019020": new Date(Date.UTC(2019, 1, 1, 0, 0, 0)),
"D:20190200": new Date(Date.UTC(2019, 1, 1, 0, 0, 0)),
"D:20190232": new Date(Date.UTC(2019, 1, 1, 0, 0, 0)),
"D:20190203": new Date(Date.UTC(2019, 1, 3, 0, 0, 0)),
// Invalid dates like the 31th of April are handled by JavaScript:
'D:20190431': new Date(Date.UTC(2019, 4, 1, 0, 0, 0)),
'D:201902030': new Date(Date.UTC(2019, 1, 3, 0, 0, 0)),
'D:2019020300': new Date(Date.UTC(2019, 1, 3, 0, 0, 0)),
'D:2019020324': new Date(Date.UTC(2019, 1, 3, 0, 0, 0)),
'D:2019020304': new Date(Date.UTC(2019, 1, 3, 4, 0, 0)),
'D:20190203040': new Date(Date.UTC(2019, 1, 3, 4, 0, 0)),
'D:201902030400': new Date(Date.UTC(2019, 1, 3, 4, 0, 0)),
'D:201902030460': new Date(Date.UTC(2019, 1, 3, 4, 0, 0)),
'D:201902030405': new Date(Date.UTC(2019, 1, 3, 4, 5, 0)),
'D:2019020304050': new Date(Date.UTC(2019, 1, 3, 4, 5, 0)),
'D:20190203040500': new Date(Date.UTC(2019, 1, 3, 4, 5, 0)),
'D:20190203040560': new Date(Date.UTC(2019, 1, 3, 4, 5, 0)),
'D:20190203040506': new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
'D:20190203040506F': new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
'D:20190203040506Z': new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
'D:20190203040506-': new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
'D:20190203040506+': new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
'D:20190203040506+\'': new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
'D:20190203040506+0': new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
'D:20190203040506+01': new Date(Date.UTC(2019, 1, 3, 3, 5, 6)),
'D:20190203040506+00\'': new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
'D:20190203040506+24\'': new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
'D:20190203040506+01\'': new Date(Date.UTC(2019, 1, 3, 3, 5, 6)),
'D:20190203040506+01\'0': new Date(Date.UTC(2019, 1, 3, 3, 5, 6)),
'D:20190203040506+01\'00': new Date(Date.UTC(2019, 1, 3, 3, 5, 6)),
'D:20190203040506+01\'60': new Date(Date.UTC(2019, 1, 3, 3, 5, 6)),
'D:20190203040506+0102': new Date(Date.UTC(2019, 1, 3, 3, 3, 6)),
'D:20190203040506+01\'02': new Date(Date.UTC(2019, 1, 3, 3, 3, 6)),
'D:20190203040506+01\'02\'': new Date(Date.UTC(2019, 1, 3, 3, 3, 6)),
"D:20190431": new Date(Date.UTC(2019, 4, 1, 0, 0, 0)),
"D:201902030": new Date(Date.UTC(2019, 1, 3, 0, 0, 0)),
"D:2019020300": new Date(Date.UTC(2019, 1, 3, 0, 0, 0)),
"D:2019020324": new Date(Date.UTC(2019, 1, 3, 0, 0, 0)),
"D:2019020304": new Date(Date.UTC(2019, 1, 3, 4, 0, 0)),
"D:20190203040": new Date(Date.UTC(2019, 1, 3, 4, 0, 0)),
"D:201902030400": new Date(Date.UTC(2019, 1, 3, 4, 0, 0)),
"D:201902030460": new Date(Date.UTC(2019, 1, 3, 4, 0, 0)),
"D:201902030405": new Date(Date.UTC(2019, 1, 3, 4, 5, 0)),
"D:2019020304050": new Date(Date.UTC(2019, 1, 3, 4, 5, 0)),
"D:20190203040500": new Date(Date.UTC(2019, 1, 3, 4, 5, 0)),
"D:20190203040560": new Date(Date.UTC(2019, 1, 3, 4, 5, 0)),
"D:20190203040506": new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
"D:20190203040506F": new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
"D:20190203040506Z": new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
"D:20190203040506-": new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
"D:20190203040506+": new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
"D:20190203040506+'": new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
"D:20190203040506+0": new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
"D:20190203040506+01": new Date(Date.UTC(2019, 1, 3, 3, 5, 6)),
"D:20190203040506+00'": new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
"D:20190203040506+24'": new Date(Date.UTC(2019, 1, 3, 4, 5, 6)),
"D:20190203040506+01'": new Date(Date.UTC(2019, 1, 3, 3, 5, 6)),
"D:20190203040506+01'0": new Date(Date.UTC(2019, 1, 3, 3, 5, 6)),
"D:20190203040506+01'00": new Date(Date.UTC(2019, 1, 3, 3, 5, 6)),
"D:20190203040506+01'60": new Date(Date.UTC(2019, 1, 3, 3, 5, 6)),
"D:20190203040506+0102": new Date(Date.UTC(2019, 1, 3, 3, 3, 6)),
"D:20190203040506+01'02": new Date(Date.UTC(2019, 1, 3, 3, 3, 6)),
"D:20190203040506+01'02'": new Date(Date.UTC(2019, 1, 3, 3, 3, 6)),
// Offset hour and minute that result in a day change:
'D:20190203040506+05\'07': new Date(Date.UTC(2019, 1, 2, 22, 58, 6)),
"D:20190203040506+05'07": new Date(Date.UTC(2019, 1, 2, 22, 58, 6)),
};
for (const [input, expectation] of Object.entries(expectations)) {

View file

@ -13,25 +13,25 @@
* limitations under the License.
*/
import { createIdFactory } from './test_utils';
import { createIdFactory } from "./test_utils";
describe('document', function () {
describe('Page', function () {
it('should create correct objId using the idFactory', function () {
describe("document", function() {
describe("Page", function() {
it("should create correct objId using the idFactory", function() {
const idFactory1 = createIdFactory(/* pageIndex = */ 0);
const idFactory2 = createIdFactory(/* pageIndex = */ 1);
expect(idFactory1.createObjId()).toEqual('p0_1');
expect(idFactory1.createObjId()).toEqual('p0_2');
expect(idFactory1.getDocId()).toEqual('g_d0');
expect(idFactory1.createObjId()).toEqual("p0_1");
expect(idFactory1.createObjId()).toEqual("p0_2");
expect(idFactory1.getDocId()).toEqual("g_d0");
expect(idFactory2.createObjId()).toEqual('p1_1');
expect(idFactory2.createObjId()).toEqual('p1_2');
expect(idFactory2.getDocId()).toEqual('g_d0');
expect(idFactory2.createObjId()).toEqual("p1_1");
expect(idFactory2.createObjId()).toEqual("p1_2");
expect(idFactory2.getDocId()).toEqual("g_d0");
expect(idFactory1.createObjId()).toEqual('p0_3');
expect(idFactory1.createObjId()).toEqual('p0_4');
expect(idFactory1.getDocId()).toEqual('g_d0');
expect(idFactory1.createObjId()).toEqual("p0_3");
expect(idFactory1.createObjId()).toEqual("p0_4");
expect(idFactory1.getDocId()).toEqual("g_d0");
});
});
});

View file

@ -13,15 +13,19 @@
* limitations under the License.
*/
import { getEncoding } from '../../src/core/encodings';
import { getEncoding } from "../../src/core/encodings";
describe('encodings', function() {
describe('getEncoding', function() {
it('fetches a valid array for known encoding names', function() {
describe("encodings", function() {
describe("getEncoding", function() {
it("fetches a valid array for known encoding names", function() {
const knownEncodingNames = [
'ExpertEncoding', 'MacExpertEncoding', 'MacRomanEncoding',
'StandardEncoding', 'SymbolSetEncoding', 'WinAnsiEncoding',
'ZapfDingbatsEncoding',
"ExpertEncoding",
"MacExpertEncoding",
"MacRomanEncoding",
"StandardEncoding",
"SymbolSetEncoding",
"WinAnsiEncoding",
"ZapfDingbatsEncoding",
];
for (const knownEncodingName of knownEncodingNames) {
@ -30,13 +34,13 @@ describe('encodings', function() {
expect(encoding.length).toEqual(256);
for (const item of encoding) {
expect(typeof item).toEqual('string');
expect(typeof item).toEqual("string");
}
}
});
it('fetches `null` for unknown encoding names', function() {
expect(getEncoding('FooBarEncoding')).toEqual(null);
it("fetches `null` for unknown encoding names", function() {
expect(getEncoding("FooBarEncoding")).toEqual(null);
});
});
});

View file

@ -13,24 +13,24 @@
* limitations under the License.
*/
import { createIdFactory, XRefMock } from './test_utils';
import { Dict, Name } from '../../src/core/primitives';
import { FormatError, OPS } from '../../src/shared/util';
import { Stream, StringStream } from '../../src/core/stream';
import { OperatorList } from '../../src/core/operator_list';
import { PartialEvaluator } from '../../src/core/evaluator';
import { WorkerTask } from '../../src/core/worker';
import { createIdFactory, XRefMock } from "./test_utils";
import { Dict, Name } from "../../src/core/primitives";
import { FormatError, OPS } from "../../src/shared/util";
import { Stream, StringStream } from "../../src/core/stream";
import { OperatorList } from "../../src/core/operator_list";
import { PartialEvaluator } from "../../src/core/evaluator";
import { WorkerTask } from "../../src/core/worker";
describe('evaluator', function() {
describe("evaluator", function() {
function HandlerMock() {
this.inputs = [];
}
HandlerMock.prototype = {
send(name, data) {
this.inputs.push({ name, data, });
this.inputs.push({ name, data });
},
};
function ResourcesMock() { }
function ResourcesMock() {}
ResourcesMock.prototype = {
get(name) {
return this[name];
@ -39,17 +39,22 @@ describe('evaluator', function() {
function runOperatorListCheck(evaluator, stream, resources, callback) {
var result = new OperatorList();
var task = new WorkerTask('OperatorListCheck');
evaluator.getOperatorList({
stream,
task,
resources,
operatorList: result,
}).then(function() {
callback(result);
}, function(reason) {
callback(reason);
});
var task = new WorkerTask("OperatorListCheck");
evaluator
.getOperatorList({
stream,
task,
resources,
operatorList: result,
})
.then(
function() {
callback(result);
},
function(reason) {
callback(reason);
}
);
}
var partialEvaluator;
@ -68,36 +73,45 @@ describe('evaluator', function() {
partialEvaluator = null;
});
describe('splitCombinedOperations', function() {
it('should reject unknown operations', function(done) {
var stream = new StringStream('fTT');
runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(),
function(result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(1);
expect(result.fnArray[0]).toEqual(OPS.fill);
expect(result.argsArray[0]).toEqual(null);
done();
});
describe("splitCombinedOperations", function() {
it("should reject unknown operations", function(done) {
var stream = new StringStream("fTT");
runOperatorListCheck(
partialEvaluator,
stream,
new ResourcesMock(),
function(result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(1);
expect(result.fnArray[0]).toEqual(OPS.fill);
expect(result.argsArray[0]).toEqual(null);
done();
}
);
});
it('should handle one operation', function(done) {
var stream = new StringStream('Q');
runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(),
function(result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(1);
expect(result.fnArray[0]).toEqual(OPS.restore);
done();
});
it("should handle one operation", function(done) {
var stream = new StringStream("Q");
runOperatorListCheck(
partialEvaluator,
stream,
new ResourcesMock(),
function(result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(1);
expect(result.fnArray[0]).toEqual(OPS.restore);
done();
}
);
});
it('should handle two glued operations', function(done) {
it("should handle two glued operations", function(done) {
var resources = new ResourcesMock();
resources.Res1 = {};
var stream = new StringStream('/Res1 DoQ');
runOperatorListCheck(partialEvaluator, stream, resources,
function(result) {
var stream = new StringStream("/Res1 DoQ");
runOperatorListCheck(partialEvaluator, stream, resources, function(
result
) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(2);
expect(result.fnArray[0]).toEqual(OPS.paintXObject);
@ -106,25 +120,30 @@ describe('evaluator', function() {
});
});
it('should handle three glued operations', function(done) {
var stream = new StringStream('fff');
runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(),
function (result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(3);
expect(result.fnArray[0]).toEqual(OPS.fill);
expect(result.fnArray[1]).toEqual(OPS.fill);
expect(result.fnArray[2]).toEqual(OPS.fill);
done();
});
it("should handle three glued operations", function(done) {
var stream = new StringStream("fff");
runOperatorListCheck(
partialEvaluator,
stream,
new ResourcesMock(),
function(result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(3);
expect(result.fnArray[0]).toEqual(OPS.fill);
expect(result.fnArray[1]).toEqual(OPS.fill);
expect(result.fnArray[2]).toEqual(OPS.fill);
done();
}
);
});
it('should handle three glued operations #2', function(done) {
it("should handle three glued operations #2", function(done) {
var resources = new ResourcesMock();
resources.Res1 = {};
var stream = new StringStream('B*Bf*');
runOperatorListCheck(partialEvaluator, stream, resources,
function(result) {
var stream = new StringStream("B*Bf*");
runOperatorListCheck(partialEvaluator, stream, resources, function(
result
) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(3);
expect(result.fnArray[0]).toEqual(OPS.eoFillStroke);
@ -134,150 +153,197 @@ describe('evaluator', function() {
});
});
it('should handle glued operations and operands', function(done) {
var stream = new StringStream('f5 Ts');
runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(),
function (result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(2);
expect(result.fnArray[0]).toEqual(OPS.fill);
expect(result.fnArray[1]).toEqual(OPS.setTextRise);
expect(result.argsArray.length).toEqual(2);
expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[1][0]).toEqual(5);
done();
});
it("should handle glued operations and operands", function(done) {
var stream = new StringStream("f5 Ts");
runOperatorListCheck(
partialEvaluator,
stream,
new ResourcesMock(),
function(result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(2);
expect(result.fnArray[0]).toEqual(OPS.fill);
expect(result.fnArray[1]).toEqual(OPS.setTextRise);
expect(result.argsArray.length).toEqual(2);
expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[1][0]).toEqual(5);
done();
}
);
});
it('should handle glued operations and literals', function(done) {
var stream = new StringStream('trueifalserinulln');
runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(),
function (result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(3);
expect(result.fnArray[0]).toEqual(OPS.setFlatness);
expect(result.fnArray[1]).toEqual(OPS.setRenderingIntent);
expect(result.fnArray[2]).toEqual(OPS.endPath);
expect(result.argsArray.length).toEqual(3);
expect(result.argsArray[0].length).toEqual(1);
expect(result.argsArray[0][0]).toEqual(true);
expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[1][0]).toEqual(false);
expect(result.argsArray[2]).toEqual(null);
done();
});
it("should handle glued operations and literals", function(done) {
var stream = new StringStream("trueifalserinulln");
runOperatorListCheck(
partialEvaluator,
stream,
new ResourcesMock(),
function(result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(3);
expect(result.fnArray[0]).toEqual(OPS.setFlatness);
expect(result.fnArray[1]).toEqual(OPS.setRenderingIntent);
expect(result.fnArray[2]).toEqual(OPS.endPath);
expect(result.argsArray.length).toEqual(3);
expect(result.argsArray[0].length).toEqual(1);
expect(result.argsArray[0][0]).toEqual(true);
expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[1][0]).toEqual(false);
expect(result.argsArray[2]).toEqual(null);
done();
}
);
});
});
describe('validateNumberOfArgs', function() {
it('should execute if correct number of arguments', function(done) {
var stream = new StringStream('5 1 d0');
runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(),
function (result) {
expect(result.argsArray[0][0]).toEqual(5);
expect(result.argsArray[0][1]).toEqual(1);
expect(result.fnArray[0]).toEqual(OPS.setCharWidth);
done();
});
describe("validateNumberOfArgs", function() {
it("should execute if correct number of arguments", function(done) {
var stream = new StringStream("5 1 d0");
runOperatorListCheck(
partialEvaluator,
stream,
new ResourcesMock(),
function(result) {
expect(result.argsArray[0][0]).toEqual(5);
expect(result.argsArray[0][1]).toEqual(1);
expect(result.fnArray[0]).toEqual(OPS.setCharWidth);
done();
}
);
});
it('should execute if too many arguments', function(done) {
var stream = new StringStream('5 1 4 d0');
runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(),
function (result) {
expect(result.argsArray[0][0]).toEqual(1);
expect(result.argsArray[0][1]).toEqual(4);
expect(result.fnArray[0]).toEqual(OPS.setCharWidth);
done();
});
it("should execute if too many arguments", function(done) {
var stream = new StringStream("5 1 4 d0");
runOperatorListCheck(
partialEvaluator,
stream,
new ResourcesMock(),
function(result) {
expect(result.argsArray[0][0]).toEqual(1);
expect(result.argsArray[0][1]).toEqual(4);
expect(result.fnArray[0]).toEqual(OPS.setCharWidth);
done();
}
);
});
it('should execute if nested commands', function(done) {
var stream = new StringStream('/F2 /GS2 gs 5.711 Tf');
runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(),
function (result) {
expect(result.fnArray.length).toEqual(3);
expect(result.fnArray[0]).toEqual(OPS.setGState);
expect(result.fnArray[1]).toEqual(OPS.dependency);
expect(result.fnArray[2]).toEqual(OPS.setFont);
expect(result.argsArray.length).toEqual(3);
expect(result.argsArray[0].length).toEqual(1);
expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[2].length).toEqual(2);
done();
});
it("should execute if nested commands", function(done) {
var stream = new StringStream("/F2 /GS2 gs 5.711 Tf");
runOperatorListCheck(
partialEvaluator,
stream,
new ResourcesMock(),
function(result) {
expect(result.fnArray.length).toEqual(3);
expect(result.fnArray[0]).toEqual(OPS.setGState);
expect(result.fnArray[1]).toEqual(OPS.dependency);
expect(result.fnArray[2]).toEqual(OPS.setFont);
expect(result.argsArray.length).toEqual(3);
expect(result.argsArray[0].length).toEqual(1);
expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[2].length).toEqual(2);
done();
}
);
});
it('should skip if too few arguments', function(done) {
var stream = new StringStream('5 d0');
runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(),
function (result) {
expect(result.argsArray).toEqual([]);
expect(result.fnArray).toEqual([]);
done();
});
it("should skip if too few arguments", function(done) {
var stream = new StringStream("5 d0");
runOperatorListCheck(
partialEvaluator,
stream,
new ResourcesMock(),
function(result) {
expect(result.argsArray).toEqual([]);
expect(result.fnArray).toEqual([]);
done();
}
);
});
it('should error if (many) path operators have too few arguments ' +
'(bug 1443140)', function(done) {
const NUM_INVALID_OPS = 25;
const tempArr = new Array(NUM_INVALID_OPS + 1);
it(
"should error if (many) path operators have too few arguments " +
"(bug 1443140)",
function(done) {
const NUM_INVALID_OPS = 25;
const tempArr = new Array(NUM_INVALID_OPS + 1);
// Non-path operators, should be ignored.
const invalidMoveText = tempArr.join('10 Td\n');
const moveTextStream = new StringStream(invalidMoveText);
runOperatorListCheck(partialEvaluator, moveTextStream,
new ResourcesMock(), function(result) {
expect(result.argsArray).toEqual([]);
expect(result.fnArray).toEqual([]);
done();
});
// Path operators, should throw error.
const invalidLineTo = tempArr.join('20 l\n');
const lineToStream = new StringStream(invalidLineTo);
runOperatorListCheck(partialEvaluator, lineToStream, new ResourcesMock(),
function(error) {
expect(error instanceof FormatError).toEqual(true);
expect(error.message).toEqual(
'Invalid command l: expected 2 args, but received 1 args.');
done();
});
});
it('should close opened saves', function(done) {
var stream = new StringStream('qq');
runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(),
function (result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(4);
expect(result.fnArray[0]).toEqual(OPS.save);
expect(result.fnArray[1]).toEqual(OPS.save);
expect(result.fnArray[2]).toEqual(OPS.restore);
expect(result.fnArray[3]).toEqual(OPS.restore);
done();
});
});
it('should error on paintXObject if name is missing', function(done) {
var stream = new StringStream('/ Do');
runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(),
// Non-path operators, should be ignored.
const invalidMoveText = tempArr.join("10 Td\n");
const moveTextStream = new StringStream(invalidMoveText);
runOperatorListCheck(
partialEvaluator,
moveTextStream,
new ResourcesMock(),
function(result) {
expect(result instanceof FormatError).toEqual(true);
expect(result.message).toEqual('XObject must be referred to by name.');
done();
});
expect(result.argsArray).toEqual([]);
expect(result.fnArray).toEqual([]);
done();
}
);
// Path operators, should throw error.
const invalidLineTo = tempArr.join("20 l\n");
const lineToStream = new StringStream(invalidLineTo);
runOperatorListCheck(
partialEvaluator,
lineToStream,
new ResourcesMock(),
function(error) {
expect(error instanceof FormatError).toEqual(true);
expect(error.message).toEqual(
"Invalid command l: expected 2 args, but received 1 args."
);
done();
}
);
}
);
it("should close opened saves", function(done) {
var stream = new StringStream("qq");
runOperatorListCheck(
partialEvaluator,
stream,
new ResourcesMock(),
function(result) {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(4);
expect(result.fnArray[0]).toEqual(OPS.save);
expect(result.fnArray[1]).toEqual(OPS.save);
expect(result.fnArray[2]).toEqual(OPS.restore);
expect(result.fnArray[3]).toEqual(OPS.restore);
done();
}
);
});
it('should skip paintXObject if subtype is PS', function(done) {
it("should error on paintXObject if name is missing", function(done) {
var stream = new StringStream("/ Do");
runOperatorListCheck(
partialEvaluator,
stream,
new ResourcesMock(),
function(result) {
expect(result instanceof FormatError).toEqual(true);
expect(result.message).toEqual(
"XObject must be referred to by name."
);
done();
}
);
});
it("should skip paintXObject if subtype is PS", function(done) {
var xobjStreamDict = new Dict();
xobjStreamDict.set('Subtype', Name.get('PS'));
xobjStreamDict.set("Subtype", Name.get("PS"));
var xobjStream = new Stream([], 0, 0, xobjStreamDict);
var xobjs = new Dict();
xobjs.set('Res1', xobjStream);
xobjs.set("Res1", xobjStream);
var resources = new Dict();
resources.set('XObject', xobjs);
resources.set("XObject", xobjs);
var stream = new StringStream('/Res1 Do');
runOperatorListCheck(partialEvaluator, stream, resources,
function(result) {
var stream = new StringStream("/Res1 Do");
runOperatorListCheck(partialEvaluator, stream, resources, function(
result
) {
expect(result.argsArray).toEqual([]);
expect(result.fnArray).toEqual([]);
done();
@ -285,46 +351,50 @@ describe('evaluator', function() {
});
});
describe('thread control', function() {
it('should abort operator list parsing', function (done) {
var stream = new StringStream('qqQQ');
describe("thread control", function() {
it("should abort operator list parsing", function(done) {
var stream = new StringStream("qqQQ");
var resources = new ResourcesMock();
var result = new OperatorList();
var task = new WorkerTask('OperatorListAbort');
var task = new WorkerTask("OperatorListAbort");
task.terminate();
partialEvaluator.getOperatorList({
stream,
task,
resources,
operatorList: result,
}).catch(function() {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(0);
done();
});
partialEvaluator
.getOperatorList({
stream,
task,
resources,
operatorList: result,
})
.catch(function() {
expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(0);
done();
});
});
it('should abort text parsing parsing', function (done) {
it("should abort text parsing parsing", function(done) {
var resources = new ResourcesMock();
var stream = new StringStream('qqQQ');
var task = new WorkerTask('TextContentAbort');
var stream = new StringStream("qqQQ");
var task = new WorkerTask("TextContentAbort");
task.terminate();
partialEvaluator.getTextContent({
stream,
task,
resources,
}).catch(function() {
expect(true).toEqual(true);
done();
});
partialEvaluator
.getTextContent({
stream,
task,
resources,
})
.catch(function() {
expect(true).toEqual(true);
done();
});
});
});
describe('operator list', function () {
describe("operator list", function() {
class StreamSinkMock {
enqueue() { }
enqueue() {}
}
it('should get correct total length after flushing', function () {
it("should get correct total length after flushing", function() {
var operatorList = new OperatorList(null, new StreamSinkMock());
operatorList.addOp(OPS.save, null);
operatorList.addOp(OPS.restore, null);

View file

@ -14,14 +14,14 @@
*/
/* eslint no-var: error */
import { AbortException } from '../../src/shared/util';
import { PDFFetchStream } from '../../src/display/fetch_stream';
import { AbortException } from "../../src/shared/util";
import { PDFFetchStream } from "../../src/display/fetch_stream";
describe('fetch_stream', function() {
const pdfUrl = new URL('../pdfs/tracemonkey.pdf', window.location).href;
describe("fetch_stream", function() {
const pdfUrl = new URL("../pdfs/tracemonkey.pdf", window.location).href;
const pdfLength = 1016315;
it('read with streaming', function(done) {
it("read with streaming", function(done) {
const stream = new PDFFetchStream({
url: pdfUrl,
disableStream: false,
@ -49,15 +49,17 @@ describe('fetch_stream', function() {
};
const readPromise = Promise.all([read(), promise]);
readPromise.then(function() {
expect(len).toEqual(pdfLength);
expect(isStreamingSupported).toEqual(true);
expect(isRangeSupported).toEqual(false);
done();
}).catch(done.fail);
readPromise
.then(function() {
expect(len).toEqual(pdfLength);
expect(isStreamingSupported).toEqual(true);
expect(isRangeSupported).toEqual(false);
done();
})
.catch(done.fail);
});
it('read ranges with streaming', function(done) {
it("read ranges with streaming", function(done) {
const rangeSize = 32768;
const stream = new PDFFetchStream({
url: pdfUrl,
@ -73,16 +75,19 @@ describe('fetch_stream', function() {
isStreamingSupported = fullReader.isStreamingSupported;
isRangeSupported = fullReader.isRangeSupported;
// We shall be able to close full reader without any issue.
fullReader.cancel(new AbortException('Don\'t need fullReader.'));
fullReader.cancel(new AbortException("Don't need fullReader."));
fullReaderCancelled = true;
});
const tailSize = (pdfLength % rangeSize) || rangeSize;
const rangeReader1 = stream.getRangeReader(pdfLength - tailSize - rangeSize,
pdfLength - tailSize);
const tailSize = pdfLength % rangeSize || rangeSize;
const rangeReader1 = stream.getRangeReader(
pdfLength - tailSize - rangeSize,
pdfLength - tailSize
);
const rangeReader2 = stream.getRangeReader(pdfLength - tailSize, pdfLength);
let result1 = { value: 0, }, result2 = { value: 0, };
let result1 = { value: 0 },
result2 = { value: 0 };
const read = function(reader, lenResult) {
return reader.read().then(function(result) {
if (result.done) {
@ -94,16 +99,20 @@ describe('fetch_stream', function() {
});
};
const readPromise = Promise.all([read(rangeReader1, result1),
read(rangeReader2, result2),
promise]);
readPromise.then(function() {
expect(isStreamingSupported).toEqual(true);
expect(isRangeSupported).toEqual(true);
expect(fullReaderCancelled).toEqual(true);
expect(result1.value).toEqual(rangeSize);
expect(result2.value).toEqual(tailSize);
done();
}).catch(done.fail);
const readPromise = Promise.all([
read(rangeReader1, result1),
read(rangeReader2, result2),
promise,
]);
readPromise
.then(function() {
expect(isStreamingSupported).toEqual(true);
expect(isRangeSupported).toEqual(true);
expect(fullReaderCancelled).toEqual(true);
expect(result1.value).toEqual(rangeSize);
expect(result2.value).toEqual(tailSize);
done();
})
.catch(done.fail);
});
});

View file

@ -14,12 +14,13 @@
*/
import {
PostScriptCompiler, PostScriptEvaluator
} from '../../src/core/function';
import { PostScriptLexer, PostScriptParser } from '../../src/core/ps_parser';
import { StringStream } from '../../src/core/stream';
PostScriptCompiler,
PostScriptEvaluator,
} from "../../src/core/function";
import { PostScriptLexer, PostScriptParser } from "../../src/core/ps_parser";
import { StringStream } from "../../src/core/stream";
describe('function', function() {
describe("function", function() {
beforeEach(function() {
jasmine.addMatchers({
toMatchArray(util, customEqualityTesters) {
@ -28,20 +29,25 @@ describe('function', function() {
var result = {};
if (actual.length !== expected.length) {
result.pass = false;
result.message = 'Array length: ' + actual.length +
', expected: ' + expected.length;
result.message =
"Array length: " +
actual.length +
", expected: " +
expected.length;
return result;
}
result.pass = true;
for (var i = 0; i < expected.length; i++) {
var a = actual[i], b = expected[i];
var a = actual[i],
b = expected[i];
if (Array.isArray(b)) {
if (a.length !== b.length) {
result.pass = false;
break;
}
for (var j = 0; j < a.length; j++) {
var suba = a[j], subb = b[j];
var suba = a[j],
subb = b[j];
if (suba !== subb) {
result.pass = false;
break;
@ -61,63 +67,63 @@ describe('function', function() {
});
});
describe('PostScriptParser', function() {
describe("PostScriptParser", function() {
function parse(program) {
var stream = new StringStream(program);
var parser = new PostScriptParser(new PostScriptLexer(stream));
return parser.parse();
}
it('parses empty programs', function() {
var output = parse('{}');
it("parses empty programs", function() {
var output = parse("{}");
expect(output.length).toEqual(0);
});
it('parses positive numbers', function() {
it("parses positive numbers", function() {
var number = 999;
var program = parse('{ ' + number + ' }');
var program = parse("{ " + number + " }");
var expectedProgram = [number];
expect(program).toMatchArray(expectedProgram);
});
it('parses negative numbers', function() {
it("parses negative numbers", function() {
var number = -999;
var program = parse('{ ' + number + ' }');
var program = parse("{ " + number + " }");
var expectedProgram = [number];
expect(program).toMatchArray(expectedProgram);
});
it('parses negative floats', function() {
it("parses negative floats", function() {
var number = 3.3;
var program = parse('{ ' + number + ' }');
var program = parse("{ " + number + " }");
var expectedProgram = [number];
expect(program).toMatchArray(expectedProgram);
});
it('parses operators', function() {
var program = parse('{ sub }');
var expectedProgram = ['sub'];
it("parses operators", function() {
var program = parse("{ sub }");
var expectedProgram = ["sub"];
expect(program).toMatchArray(expectedProgram);
});
it('parses if statements', function() {
var program = parse('{ { 99 } if }');
var expectedProgram = [3, 'jz', 99];
it("parses if statements", function() {
var program = parse("{ { 99 } if }");
var expectedProgram = [3, "jz", 99];
expect(program).toMatchArray(expectedProgram);
});
it('parses ifelse statements', function() {
var program = parse('{ { 99 } { 44 } ifelse }');
var expectedProgram = [5, 'jz', 99, 6, 'j', 44];
it("parses ifelse statements", function() {
var program = parse("{ { 99 } { 44 } ifelse }");
var expectedProgram = [5, "jz", 99, 6, "j", 44];
expect(program).toMatchArray(expectedProgram);
});
it('handles missing brackets', function() {
it("handles missing brackets", function() {
expect(function() {
parse('{');
}).toThrow(new Error('Unexpected symbol: found undefined expected 1.'));
parse("{");
}).toThrow(new Error("Unexpected symbol: found undefined expected 1."));
});
it('handles junk after the end', function() {
it("handles junk after the end", function() {
var number = 3.3;
var program = parse('{ ' + number + ' }#');
var program = parse("{ " + number + " }#");
var expectedProgram = [number];
expect(program).toMatchArray(expectedProgram);
});
});
describe('PostScriptEvaluator', function() {
describe("PostScriptEvaluator", function() {
function evaluate(program) {
var stream = new StringStream(program);
var parser = new PostScriptParser(new PostScriptLexer(stream));
@ -127,320 +133,320 @@ describe('function', function() {
return output;
}
it('pushes stack', function() {
var stack = evaluate('{ 99 }');
it("pushes stack", function() {
var stack = evaluate("{ 99 }");
var expectedStack = [99];
expect(stack).toMatchArray(expectedStack);
});
it('handles if with true', function() {
var stack = evaluate('{ 1 {99} if }');
it("handles if with true", function() {
var stack = evaluate("{ 1 {99} if }");
var expectedStack = [99];
expect(stack).toMatchArray(expectedStack);
});
it('handles if with false', function() {
var stack = evaluate('{ 0 {99} if }');
it("handles if with false", function() {
var stack = evaluate("{ 0 {99} if }");
var expectedStack = [];
expect(stack).toMatchArray(expectedStack);
});
it('handles ifelse with true', function() {
var stack = evaluate('{ 1 {99} {77} ifelse }');
it("handles ifelse with true", function() {
var stack = evaluate("{ 1 {99} {77} ifelse }");
var expectedStack = [99];
expect(stack).toMatchArray(expectedStack);
});
it('handles ifelse with false', function() {
var stack = evaluate('{ 0 {99} {77} ifelse }');
it("handles ifelse with false", function() {
var stack = evaluate("{ 0 {99} {77} ifelse }");
var expectedStack = [77];
expect(stack).toMatchArray(expectedStack);
});
it('handles nested if', function() {
var stack = evaluate('{ 1 {1 {77} if} if }');
it("handles nested if", function() {
var stack = evaluate("{ 1 {1 {77} if} if }");
var expectedStack = [77];
expect(stack).toMatchArray(expectedStack);
});
it('abs', function() {
var stack = evaluate('{ -2 abs }');
it("abs", function() {
var stack = evaluate("{ -2 abs }");
var expectedStack = [2];
expect(stack).toMatchArray(expectedStack);
});
it('adds', function() {
var stack = evaluate('{ 1 2 add }');
it("adds", function() {
var stack = evaluate("{ 1 2 add }");
var expectedStack = [3];
expect(stack).toMatchArray(expectedStack);
});
it('boolean and', function() {
var stack = evaluate('{ true false and }');
it("boolean and", function() {
var stack = evaluate("{ true false and }");
var expectedStack = [false];
expect(stack).toMatchArray(expectedStack);
});
it('bitwise and', function() {
var stack = evaluate('{ 254 1 and }');
it("bitwise and", function() {
var stack = evaluate("{ 254 1 and }");
var expectedStack = [254 & 1];
expect(stack).toMatchArray(expectedStack);
});
it('calculates the inverse tangent of a number', function() {
var stack = evaluate('{ 90 atan }');
it("calculates the inverse tangent of a number", function() {
var stack = evaluate("{ 90 atan }");
var expectedStack = [Math.atan(90)];
expect(stack).toMatchArray(expectedStack);
});
it('handles bitshifting ', function() {
var stack = evaluate('{ 50 2 bitshift }');
it("handles bitshifting ", function() {
var stack = evaluate("{ 50 2 bitshift }");
var expectedStack = [200];
expect(stack).toMatchArray(expectedStack);
});
it('calculates the ceiling value', function() {
var stack = evaluate('{ 9.9 ceiling }');
it("calculates the ceiling value", function() {
var stack = evaluate("{ 9.9 ceiling }");
var expectedStack = [10];
expect(stack).toMatchArray(expectedStack);
});
it('copies', function() {
var stack = evaluate('{ 99 98 2 copy }');
it("copies", function() {
var stack = evaluate("{ 99 98 2 copy }");
var expectedStack = [99, 98, 99, 98];
expect(stack).toMatchArray(expectedStack);
});
it('calculates the cosine of a number', function() {
var stack = evaluate('{ 90 cos }');
it("calculates the cosine of a number", function() {
var stack = evaluate("{ 90 cos }");
var expectedStack = [Math.cos(90)];
expect(stack).toMatchArray(expectedStack);
});
it('converts to int', function() {
var stack = evaluate('{ 9.9 cvi }');
it("converts to int", function() {
var stack = evaluate("{ 9.9 cvi }");
var expectedStack = [9];
expect(stack).toMatchArray(expectedStack);
});
it('converts negatives to int', function() {
var stack = evaluate('{ -9.9 cvi }');
it("converts negatives to int", function() {
var stack = evaluate("{ -9.9 cvi }");
var expectedStack = [-9];
expect(stack).toMatchArray(expectedStack);
});
it('converts to real', function() {
var stack = evaluate('{ 55.34 cvr }');
it("converts to real", function() {
var stack = evaluate("{ 55.34 cvr }");
var expectedStack = [55.34];
expect(stack).toMatchArray(expectedStack);
});
it('divides', function() {
var stack = evaluate('{ 6 5 div }');
it("divides", function() {
var stack = evaluate("{ 6 5 div }");
var expectedStack = [1.2];
expect(stack).toMatchArray(expectedStack);
});
it('maps division by zero to infinity', function() {
var stack = evaluate('{ 6 0 div }');
it("maps division by zero to infinity", function() {
var stack = evaluate("{ 6 0 div }");
var expectedStack = [Infinity];
expect(stack).toMatchArray(expectedStack);
});
it('duplicates', function() {
var stack = evaluate('{ 99 dup }');
it("duplicates", function() {
var stack = evaluate("{ 99 dup }");
var expectedStack = [99, 99];
expect(stack).toMatchArray(expectedStack);
});
it('accepts an equality', function() {
var stack = evaluate('{ 9 9 eq }');
it("accepts an equality", function() {
var stack = evaluate("{ 9 9 eq }");
var expectedStack = [true];
expect(stack).toMatchArray(expectedStack);
});
it('rejects an inequality', function() {
var stack = evaluate('{ 9 8 eq }');
it("rejects an inequality", function() {
var stack = evaluate("{ 9 8 eq }");
var expectedStack = [false];
expect(stack).toMatchArray(expectedStack);
});
it('exchanges', function() {
var stack = evaluate('{ 44 99 exch }');
it("exchanges", function() {
var stack = evaluate("{ 44 99 exch }");
var expectedStack = [99, 44];
expect(stack).toMatchArray(expectedStack);
});
it('handles exponentiation', function() {
var stack = evaluate('{ 10 2 exp }');
it("handles exponentiation", function() {
var stack = evaluate("{ 10 2 exp }");
var expectedStack = [100];
expect(stack).toMatchArray(expectedStack);
});
it('pushes false onto the stack', function() {
var stack = evaluate('{ false }');
it("pushes false onto the stack", function() {
var stack = evaluate("{ false }");
var expectedStack = [false];
expect(stack).toMatchArray(expectedStack);
});
it('calculates the floor value', function() {
var stack = evaluate('{ 9.9 floor }');
it("calculates the floor value", function() {
var stack = evaluate("{ 9.9 floor }");
var expectedStack = [9];
expect(stack).toMatchArray(expectedStack);
});
it('handles greater than or equal to', function() {
var stack = evaluate('{ 10 9 ge }');
it("handles greater than or equal to", function() {
var stack = evaluate("{ 10 9 ge }");
var expectedStack = [true];
expect(stack).toMatchArray(expectedStack);
});
it('rejects less than for greater than or equal to', function() {
var stack = evaluate('{ 8 9 ge }');
it("rejects less than for greater than or equal to", function() {
var stack = evaluate("{ 8 9 ge }");
var expectedStack = [false];
expect(stack).toMatchArray(expectedStack);
});
it('handles greater than', function() {
var stack = evaluate('{ 10 9 gt }');
it("handles greater than", function() {
var stack = evaluate("{ 10 9 gt }");
var expectedStack = [true];
expect(stack).toMatchArray(expectedStack);
});
it('rejects less than or equal for greater than', function() {
var stack = evaluate('{ 9 9 gt }');
it("rejects less than or equal for greater than", function() {
var stack = evaluate("{ 9 9 gt }");
var expectedStack = [false];
expect(stack).toMatchArray(expectedStack);
});
it('divides to integer', function() {
var stack = evaluate('{ 2 3 idiv }');
it("divides to integer", function() {
var stack = evaluate("{ 2 3 idiv }");
var expectedStack = [0];
expect(stack).toMatchArray(expectedStack);
});
it('divides to negative integer', function() {
var stack = evaluate('{ -2 3 idiv }');
it("divides to negative integer", function() {
var stack = evaluate("{ -2 3 idiv }");
var expectedStack = [0];
expect(stack).toMatchArray(expectedStack);
});
it('duplicates index', function() {
var stack = evaluate('{ 4 3 2 1 2 index }');
it("duplicates index", function() {
var stack = evaluate("{ 4 3 2 1 2 index }");
var expectedStack = [4, 3, 2, 1, 3];
expect(stack).toMatchArray(expectedStack);
});
it('handles less than or equal to', function() {
var stack = evaluate('{ 9 10 le }');
it("handles less than or equal to", function() {
var stack = evaluate("{ 9 10 le }");
var expectedStack = [true];
expect(stack).toMatchArray(expectedStack);
});
it('rejects greater than for less than or equal to', function() {
var stack = evaluate('{ 10 9 le }');
it("rejects greater than for less than or equal to", function() {
var stack = evaluate("{ 10 9 le }");
var expectedStack = [false];
expect(stack).toMatchArray(expectedStack);
});
it('calculates the natural logarithm', function() {
var stack = evaluate('{ 10 ln }');
it("calculates the natural logarithm", function() {
var stack = evaluate("{ 10 ln }");
var expectedStack = [Math.log(10)];
expect(stack).toMatchArray(expectedStack);
});
it('calculates the base 10 logarithm', function() {
var stack = evaluate('{ 100 log }');
it("calculates the base 10 logarithm", function() {
var stack = evaluate("{ 100 log }");
var expectedStack = [2];
expect(stack).toMatchArray(expectedStack);
});
it('handles less than', function() {
var stack = evaluate('{ 9 10 lt }');
it("handles less than", function() {
var stack = evaluate("{ 9 10 lt }");
var expectedStack = [true];
expect(stack).toMatchArray(expectedStack);
});
it('rejects greater than or equal to for less than', function() {
var stack = evaluate('{ 10 9 lt }');
it("rejects greater than or equal to for less than", function() {
var stack = evaluate("{ 10 9 lt }");
var expectedStack = [false];
expect(stack).toMatchArray(expectedStack);
});
it('performs the modulo operation', function() {
var stack = evaluate('{ 4 3 mod }');
it("performs the modulo operation", function() {
var stack = evaluate("{ 4 3 mod }");
var expectedStack = [1];
expect(stack).toMatchArray(expectedStack);
});
it('multiplies two numbers (positive result)', function() {
var stack = evaluate('{ 9 8 mul }');
it("multiplies two numbers (positive result)", function() {
var stack = evaluate("{ 9 8 mul }");
var expectedStack = [72];
expect(stack).toMatchArray(expectedStack);
});
it('multiplies two numbers (negative result)', function() {
var stack = evaluate('{ 9 -8 mul }');
it("multiplies two numbers (negative result)", function() {
var stack = evaluate("{ 9 -8 mul }");
var expectedStack = [-72];
expect(stack).toMatchArray(expectedStack);
});
it('accepts an inequality', function() {
var stack = evaluate('{ 9 8 ne }');
it("accepts an inequality", function() {
var stack = evaluate("{ 9 8 ne }");
var expectedStack = [true];
expect(stack).toMatchArray(expectedStack);
});
it('rejects an equality', function() {
var stack = evaluate('{ 9 9 ne }');
it("rejects an equality", function() {
var stack = evaluate("{ 9 9 ne }");
var expectedStack = [false];
expect(stack).toMatchArray(expectedStack);
});
it('negates', function() {
var stack = evaluate('{ 4.5 neg }');
it("negates", function() {
var stack = evaluate("{ 4.5 neg }");
var expectedStack = [-4.5];
expect(stack).toMatchArray(expectedStack);
});
it('boolean not', function() {
var stack = evaluate('{ true not }');
it("boolean not", function() {
var stack = evaluate("{ true not }");
var expectedStack = [false];
expect(stack).toMatchArray(expectedStack);
});
it('bitwise not', function() {
var stack = evaluate('{ 12 not }');
it("bitwise not", function() {
var stack = evaluate("{ 12 not }");
var expectedStack = [-13];
expect(stack).toMatchArray(expectedStack);
});
it('boolean or', function() {
var stack = evaluate('{ true false or }');
it("boolean or", function() {
var stack = evaluate("{ true false or }");
var expectedStack = [true];
expect(stack).toMatchArray(expectedStack);
});
it('bitwise or', function() {
var stack = evaluate('{ 254 1 or }');
it("bitwise or", function() {
var stack = evaluate("{ 254 1 or }");
var expectedStack = [254 | 1];
expect(stack).toMatchArray(expectedStack);
});
it('pops stack', function() {
var stack = evaluate('{ 1 2 pop }');
it("pops stack", function() {
var stack = evaluate("{ 1 2 pop }");
var expectedStack = [1];
expect(stack).toMatchArray(expectedStack);
});
it('rolls stack right', function() {
var stack = evaluate('{ 1 3 2 2 4 1 roll }');
it("rolls stack right", function() {
var stack = evaluate("{ 1 3 2 2 4 1 roll }");
var expectedStack = [2, 1, 3, 2];
expect(stack).toMatchArray(expectedStack);
});
it('rolls stack left', function() {
var stack = evaluate('{ 1 3 2 2 4 -1 roll }');
it("rolls stack left", function() {
var stack = evaluate("{ 1 3 2 2 4 -1 roll }");
var expectedStack = [3, 2, 2, 1];
expect(stack).toMatchArray(expectedStack);
});
it('rounds a number', function() {
var stack = evaluate('{ 9.52 round }');
it("rounds a number", function() {
var stack = evaluate("{ 9.52 round }");
var expectedStack = [10];
expect(stack).toMatchArray(expectedStack);
});
it('calculates the sine of a number', function() {
var stack = evaluate('{ 90 sin }');
it("calculates the sine of a number", function() {
var stack = evaluate("{ 90 sin }");
var expectedStack = [Math.sin(90)];
expect(stack).toMatchArray(expectedStack);
});
it('calculates a square root (integer)', function() {
var stack = evaluate('{ 100 sqrt }');
it("calculates a square root (integer)", function() {
var stack = evaluate("{ 100 sqrt }");
var expectedStack = [10];
expect(stack).toMatchArray(expectedStack);
});
it('calculates a square root (float)', function() {
var stack = evaluate('{ 99 sqrt }');
it("calculates a square root (float)", function() {
var stack = evaluate("{ 99 sqrt }");
var expectedStack = [Math.sqrt(99)];
expect(stack).toMatchArray(expectedStack);
});
it('subtracts (positive result)', function() {
var stack = evaluate('{ 6 4 sub }');
it("subtracts (positive result)", function() {
var stack = evaluate("{ 6 4 sub }");
var expectedStack = [2];
expect(stack).toMatchArray(expectedStack);
});
it('subtracts (negative result)', function() {
var stack = evaluate('{ 4 6 sub }');
it("subtracts (negative result)", function() {
var stack = evaluate("{ 4 6 sub }");
var expectedStack = [-2];
expect(stack).toMatchArray(expectedStack);
});
it('pushes true onto the stack', function() {
var stack = evaluate('{ true }');
it("pushes true onto the stack", function() {
var stack = evaluate("{ true }");
var expectedStack = [true];
expect(stack).toMatchArray(expectedStack);
});
it('truncates a number', function() {
var stack = evaluate('{ 35.004 truncate }');
it("truncates a number", function() {
var stack = evaluate("{ 35.004 truncate }");
var expectedStack = [35];
expect(stack).toMatchArray(expectedStack);
});
it('calculates an exclusive or value', function() {
var stack = evaluate('{ 3 9 xor }');
it("calculates an exclusive or value", function() {
var stack = evaluate("{ 3 9 xor }");
var expectedStack = [10];
expect(stack).toMatchArray(expectedStack);
});
});
describe('PostScriptCompiler', function() {
describe("PostScriptCompiler", function() {
function check(code, domain, range, samples) {
var compiler = new PostScriptCompiler();
var compiledCode = compiler.compile(code, domain, range);
@ -449,100 +455,177 @@ describe('function', function() {
} else {
expect(compiledCode).not.toBeNull();
// eslint-disable-next-line no-new-func
var fn = new Function('src', 'srcOffset', 'dest', 'destOffset',
compiledCode);
var fn = new Function(
"src",
"srcOffset",
"dest",
"destOffset",
compiledCode
);
for (var i = 0; i < samples.length; i++) {
var out = new Float32Array(samples[i].output.length);
fn(samples[i].input, 0, out, 0);
expect(Array.prototype.slice.call(out, 0)).
toMatchArray(samples[i].output);
expect(Array.prototype.slice.call(out, 0)).toMatchArray(
samples[i].output
);
}
}
}
it('check compiled add', function() {
check([0.25, 0.5, 'add'], [], [0, 1], [{ input: [], output: [0.75], }]);
check([0, 'add'], [0, 1], [0, 1], [{ input: [0.25], output: [0.25], }]);
check([0.5, 'add'], [0, 1], [0, 1], [{ input: [0.25], output: [0.75], }]);
check([0, 'exch', 'add'], [0, 1], [0, 1],
[{ input: [0.25], output: [0.25], }]);
check([0.5, 'exch', 'add'], [0, 1], [0, 1],
[{ input: [0.25], output: [0.75], }]);
check(['add'], [0, 1, 0, 1], [0, 1],
[{ input: [0.25, 0.5], output: [0.75], }]);
check(['add'], [0, 1], [0, 1], null);
it("check compiled add", function() {
check([0.25, 0.5, "add"], [], [0, 1], [{ input: [], output: [0.75] }]);
check([0, "add"], [0, 1], [0, 1], [{ input: [0.25], output: [0.25] }]);
check([0.5, "add"], [0, 1], [0, 1], [{ input: [0.25], output: [0.75] }]);
check(
[0, "exch", "add"],
[0, 1],
[0, 1],
[{ input: [0.25], output: [0.25] }]
);
check(
[0.5, "exch", "add"],
[0, 1],
[0, 1],
[{ input: [0.25], output: [0.75] }]
);
check(
["add"],
[0, 1, 0, 1],
[0, 1],
[{ input: [0.25, 0.5], output: [0.75] }]
);
check(["add"], [0, 1], [0, 1], null);
});
it('check compiled sub', function() {
check([0.5, 0.25, 'sub'], [], [0, 1], [{ input: [], output: [0.25], }]);
check([0, 'sub'], [0, 1], [0, 1], [{ input: [0.25], output: [0.25], }]);
check([0.5, 'sub'], [0, 1], [0, 1], [{ input: [0.75], output: [0.25], }]);
check([0, 'exch', 'sub'], [0, 1], [-1, 1],
[{ input: [0.25], output: [-0.25], }]);
check([0.75, 'exch', 'sub'], [0, 1], [-1, 1],
[{ input: [0.25], output: [0.5], }]);
check(['sub'], [0, 1, 0, 1], [-1, 1],
[{ input: [0.25, 0.5], output: [-0.25], }]);
check(['sub'], [0, 1], [0, 1], null);
it("check compiled sub", function() {
check([0.5, 0.25, "sub"], [], [0, 1], [{ input: [], output: [0.25] }]);
check([0, "sub"], [0, 1], [0, 1], [{ input: [0.25], output: [0.25] }]);
check([0.5, "sub"], [0, 1], [0, 1], [{ input: [0.75], output: [0.25] }]);
check(
[0, "exch", "sub"],
[0, 1],
[-1, 1],
[{ input: [0.25], output: [-0.25] }]
);
check(
[0.75, "exch", "sub"],
[0, 1],
[-1, 1],
[{ input: [0.25], output: [0.5] }]
);
check(
["sub"],
[0, 1, 0, 1],
[-1, 1],
[{ input: [0.25, 0.5], output: [-0.25] }]
);
check(["sub"], [0, 1], [0, 1], null);
check([1, 'dup', 3, 2, 'roll', 'sub', 'sub'], [0, 1], [0, 1],
[{ input: [0.75], output: [0.75], }]);
check(
[1, "dup", 3, 2, "roll", "sub", "sub"],
[0, 1],
[0, 1],
[{ input: [0.75], output: [0.75] }]
);
});
it('check compiled mul', function() {
check([0.25, 0.5, 'mul'], [], [0, 1], [{ input: [], output: [0.125], }]);
check([0, 'mul'], [0, 1], [0, 1], [{ input: [0.25], output: [0], }]);
check([0.5, 'mul'], [0, 1], [0, 1],
[{ input: [0.25], output: [0.125], }]);
check([1, 'mul'], [0, 1], [0, 1], [{ input: [0.25], output: [0.25], }]);
check([0, 'exch', 'mul'], [0, 1], [0, 1],
[{ input: [0.25], output: [0], }]);
check([0.5, 'exch', 'mul'], [0, 1], [0, 1],
[{ input: [0.25], output: [0.125], }]);
check([1, 'exch', 'mul'], [0, 1], [0, 1],
[{ input: [0.25], output: [0.25], }]);
check(['mul'], [0, 1, 0, 1], [0, 1],
[{ input: [0.25, 0.5], output: [0.125], }]);
check(['mul'], [0, 1], [0, 1], null);
it("check compiled mul", function() {
check([0.25, 0.5, "mul"], [], [0, 1], [{ input: [], output: [0.125] }]);
check([0, "mul"], [0, 1], [0, 1], [{ input: [0.25], output: [0] }]);
check([0.5, "mul"], [0, 1], [0, 1], [{ input: [0.25], output: [0.125] }]);
check([1, "mul"], [0, 1], [0, 1], [{ input: [0.25], output: [0.25] }]);
check(
[0, "exch", "mul"],
[0, 1],
[0, 1],
[{ input: [0.25], output: [0] }]
);
check(
[0.5, "exch", "mul"],
[0, 1],
[0, 1],
[{ input: [0.25], output: [0.125] }]
);
check(
[1, "exch", "mul"],
[0, 1],
[0, 1],
[{ input: [0.25], output: [0.25] }]
);
check(
["mul"],
[0, 1, 0, 1],
[0, 1],
[{ input: [0.25, 0.5], output: [0.125] }]
);
check(["mul"], [0, 1], [0, 1], null);
});
it('check compiled max', function() {
check(['dup', 0.75, 'gt', 7, 'jz', 'pop', 0.75], [0, 1], [0, 1],
[{ input: [0.5], output: [0.5], }]);
check(['dup', 0.75, 'gt', 7, 'jz', 'pop', 0.75], [0, 1], [0, 1],
[{ input: [1], output: [0.75], }]);
check(['dup', 0.75, 'gt', 5, 'jz', 'pop', 0.75], [0, 1], [0, 1], null);
it("check compiled max", function() {
check(
["dup", 0.75, "gt", 7, "jz", "pop", 0.75],
[0, 1],
[0, 1],
[{ input: [0.5], output: [0.5] }]
);
check(
["dup", 0.75, "gt", 7, "jz", "pop", 0.75],
[0, 1],
[0, 1],
[{ input: [1], output: [0.75] }]
);
check(["dup", 0.75, "gt", 5, "jz", "pop", 0.75], [0, 1], [0, 1], null);
});
it('check pop/roll/index', function() {
check([1, 'pop'], [0, 1], [0, 1], [{ input: [0.5], output: [0.5], }]);
check([1, 3, -1, 'roll'], [0, 1, 0, 1], [0, 1, 0, 1, 0, 1],
[{ input: [0.25, 0.5], output: [0.5, 1, 0.25], }]);
check([1, 3, 1, 'roll'], [0, 1, 0, 1], [0, 1, 0, 1, 0, 1],
[{ input: [0.25, 0.5], output: [1, 0.25, 0.5], }]);
check([1, 3, 1.5, 'roll'], [0, 1, 0, 1], [0, 1, 0, 1, 0, 1], null);
check([1, 1, 'index'], [0, 1], [0, 1, 0, 1, 0, 1],
[{ input: [0.5], output: [0.5, 1, 0.5], }]);
check([1, 3, 'index', 'pop'], [0, 1], [0, 1], null);
check([1, 0.5, 'index', 'pop'], [0, 1], [0, 1], null);
it("check pop/roll/index", function() {
check([1, "pop"], [0, 1], [0, 1], [{ input: [0.5], output: [0.5] }]);
check(
[1, 3, -1, "roll"],
[0, 1, 0, 1],
[0, 1, 0, 1, 0, 1],
[{ input: [0.25, 0.5], output: [0.5, 1, 0.25] }]
);
check(
[1, 3, 1, "roll"],
[0, 1, 0, 1],
[0, 1, 0, 1, 0, 1],
[{ input: [0.25, 0.5], output: [1, 0.25, 0.5] }]
);
check([1, 3, 1.5, "roll"], [0, 1, 0, 1], [0, 1, 0, 1, 0, 1], null);
check(
[1, 1, "index"],
[0, 1],
[0, 1, 0, 1, 0, 1],
[{ input: [0.5], output: [0.5, 1, 0.5] }]
);
check([1, 3, "index", "pop"], [0, 1], [0, 1], null);
check([1, 0.5, "index", "pop"], [0, 1], [0, 1], null);
});
it('check input boundaries', function () {
check([], [0, 0.5], [0, 1], [{ input: [1], output: [0.5], }]);
check([], [0.5, 1], [0, 1], [{ input: [0], output: [0.5], }]);
check(['dup'], [0.5, 0.75], [0, 1, 0, 1],
[{ input: [0], output: [0.5, 0.5], }]);
check([], [100, 1001], [0, 10000], [{ input: [1000], output: [1000], }]);
it("check input boundaries", function() {
check([], [0, 0.5], [0, 1], [{ input: [1], output: [0.5] }]);
check([], [0.5, 1], [0, 1], [{ input: [0], output: [0.5] }]);
check(
["dup"],
[0.5, 0.75],
[0, 1, 0, 1],
[{ input: [0], output: [0.5, 0.5] }]
);
check([], [100, 1001], [0, 10000], [{ input: [1000], output: [1000] }]);
});
it('check output boundaries', function () {
check([], [0, 1], [0, 0.5], [{ input: [1], output: [0.5], }]);
check([], [0, 1], [0.5, 1], [{ input: [0], output: [0.5], }]);
check(['dup'], [0, 1], [0.5, 1, 0.75, 1],
[{ input: [0], output: [0.5, 0.75], }]);
check([], [0, 10000], [100, 1001], [{ input: [1000], output: [1000], }]);
it("check output boundaries", function() {
check([], [0, 1], [0, 0.5], [{ input: [1], output: [0.5] }]);
check([], [0, 1], [0.5, 1], [{ input: [0], output: [0.5] }]);
check(
["dup"],
[0, 1],
[0.5, 1, 0.75, 1],
[{ input: [0], output: [0.5, 0.75] }]
);
check([], [0, 10000], [100, 1001], [{ input: [1000], output: [1000] }]);
});
it('compile optimized', function () {
it("compile optimized", function() {
var compiler = new PostScriptCompiler();
var code = [0, 'add', 1, 1, 3, -1, 'roll', 'sub', 'sub', 1, 'mul'];
var code = [0, "add", 1, 1, 3, -1, "roll", "sub", "sub", 1, "mul"];
var compiledCode = compiler.compile(code, [0, 1], [0, 1]);
expect(compiledCode).toEqual(
'dest[destOffset + 0] = Math.max(0, Math.min(1, src[srcOffset + 0]));');
"dest[destOffset + 0] = Math.max(0, Math.min(1, src[srcOffset + 0]));"
);
});
});
});

View file

@ -38,62 +38,69 @@
// Modified jasmine's boot.js file to load PDF.js libraries async.
'use strict';
"use strict";
function initializePDFJS(callback) {
Promise.all([
'pdfjs/display/api',
'pdfjs/display/worker_options',
'pdfjs/display/network',
'pdfjs/display/fetch_stream',
'pdfjs/shared/is_node',
'pdfjs-test/unit/annotation_spec',
'pdfjs-test/unit/api_spec',
'pdfjs-test/unit/bidi_spec',
'pdfjs-test/unit/cff_parser_spec',
'pdfjs-test/unit/cmap_spec',
'pdfjs-test/unit/colorspace_spec',
'pdfjs-test/unit/core_utils_spec',
'pdfjs-test/unit/crypto_spec',
'pdfjs-test/unit/custom_spec',
'pdfjs-test/unit/display_svg_spec',
'pdfjs-test/unit/display_utils_spec',
'pdfjs-test/unit/document_spec',
'pdfjs-test/unit/encodings_spec',
'pdfjs-test/unit/evaluator_spec',
'pdfjs-test/unit/function_spec',
'pdfjs-test/unit/fetch_stream_spec',
'pdfjs-test/unit/message_handler_spec',
'pdfjs-test/unit/metadata_spec',
'pdfjs-test/unit/murmurhash3_spec',
'pdfjs-test/unit/network_spec',
'pdfjs-test/unit/network_utils_spec',
'pdfjs-test/unit/parser_spec',
'pdfjs-test/unit/pdf_find_controller_spec',
'pdfjs-test/unit/pdf_find_utils_spec',
'pdfjs-test/unit/pdf_history_spec',
'pdfjs-test/unit/primitives_spec',
'pdfjs-test/unit/stream_spec',
'pdfjs-test/unit/type1_parser_spec',
'pdfjs-test/unit/ui_utils_spec',
'pdfjs-test/unit/unicode_spec',
'pdfjs-test/unit/util_spec',
].map(function (moduleName) {
return SystemJS.import(moduleName);
})).then(function(modules) {
Promise.all(
[
"pdfjs/display/api",
"pdfjs/display/worker_options",
"pdfjs/display/network",
"pdfjs/display/fetch_stream",
"pdfjs/shared/is_node",
"pdfjs-test/unit/annotation_spec",
"pdfjs-test/unit/api_spec",
"pdfjs-test/unit/bidi_spec",
"pdfjs-test/unit/cff_parser_spec",
"pdfjs-test/unit/cmap_spec",
"pdfjs-test/unit/colorspace_spec",
"pdfjs-test/unit/core_utils_spec",
"pdfjs-test/unit/crypto_spec",
"pdfjs-test/unit/custom_spec",
"pdfjs-test/unit/display_svg_spec",
"pdfjs-test/unit/display_utils_spec",
"pdfjs-test/unit/document_spec",
"pdfjs-test/unit/encodings_spec",
"pdfjs-test/unit/evaluator_spec",
"pdfjs-test/unit/function_spec",
"pdfjs-test/unit/fetch_stream_spec",
"pdfjs-test/unit/message_handler_spec",
"pdfjs-test/unit/metadata_spec",
"pdfjs-test/unit/murmurhash3_spec",
"pdfjs-test/unit/network_spec",
"pdfjs-test/unit/network_utils_spec",
"pdfjs-test/unit/parser_spec",
"pdfjs-test/unit/pdf_find_controller_spec",
"pdfjs-test/unit/pdf_find_utils_spec",
"pdfjs-test/unit/pdf_history_spec",
"pdfjs-test/unit/primitives_spec",
"pdfjs-test/unit/stream_spec",
"pdfjs-test/unit/type1_parser_spec",
"pdfjs-test/unit/ui_utils_spec",
"pdfjs-test/unit/unicode_spec",
"pdfjs-test/unit/util_spec",
].map(function(moduleName) {
return SystemJS.import(moduleName);
})
).then(function(modules) {
const displayApi = modules[0];
const { GlobalWorkerOptions, } = modules[1];
const { PDFNetworkStream, } = modules[2];
const { PDFFetchStream, } = modules[3];
const { isNodeJS, } = modules[4];
const { GlobalWorkerOptions } = modules[1];
const { PDFNetworkStream } = modules[2];
const { PDFFetchStream } = modules[3];
const { isNodeJS } = modules[4];
if (isNodeJS) {
throw new Error('The `gulp unittest` command cannot be used in ' +
'Node.js environments.');
throw new Error(
"The `gulp unittest` command cannot be used in " +
"Node.js environments."
);
}
// Set the network stream factory for unit-tests.
if (typeof Response !== 'undefined' && 'body' in Response.prototype &&
typeof ReadableStream !== 'undefined') {
if (
typeof Response !== "undefined" &&
"body" in Response.prototype &&
typeof ReadableStream !== "undefined"
) {
displayApi.setPDFNetworkStreamFactory(function(params) {
return new PDFFetchStream(params);
});
@ -104,7 +111,7 @@ function initializePDFJS(callback) {
}
// Configure the worker.
GlobalWorkerOptions.workerSrc = '../../build/generic/build/pdf.worker.js';
GlobalWorkerOptions.workerSrc = "../../build/generic/build/pdf.worker.js";
callback();
});
@ -128,17 +135,17 @@ function initializePDFJS(callback) {
});
var config = {
failFast: queryString.getParam('failFast'),
oneFailurePerSpec: queryString.getParam('oneFailurePerSpec'),
hideDisabled: queryString.getParam('hideDisabled'),
failFast: queryString.getParam("failFast"),
oneFailurePerSpec: queryString.getParam("oneFailurePerSpec"),
hideDisabled: queryString.getParam("hideDisabled"),
};
var random = queryString.getParam('random');
if (random !== undefined && random !== '') {
var random = queryString.getParam("random");
if (random !== undefined && random !== "") {
config.random = random;
}
var seed = queryString.getParam('seed');
var seed = queryString.getParam("seed");
if (seed) {
config.seed = seed;
}
@ -166,9 +173,11 @@ function initializePDFJS(callback) {
env.addReporter(htmlReporter);
if (queryString.getParam('browser')) {
var testReporter = new TestReporter(queryString.getParam('browser'),
queryString.getParam('path'));
if (queryString.getParam("browser")) {
var testReporter = new TestReporter(
queryString.getParam("browser"),
queryString.getParam("path")
);
env.addReporter(testReporter);
}
@ -176,7 +185,7 @@ function initializePDFJS(callback) {
// against the `spec` query param.
var specFilter = new jasmine.HtmlSpecFilter({
filterString() {
return queryString.getParam('spec');
return queryString.getParam("spec");
},
});
@ -199,7 +208,7 @@ function initializePDFJS(callback) {
currentWindowOnload();
}
initializePDFJS(function () {
initializePDFJS(function() {
htmlReporter.initialize();
env.execute();
});
@ -211,4 +220,4 @@ function initializePDFJS(callback) {
}
return destination;
}
}());
})();

View file

@ -14,307 +14,371 @@
*/
import {
AbortException, createPromiseCapability, UnknownErrorException
} from '../../src/shared/util';
import { LoopbackPort } from '../../src/display/api';
import { MessageHandler } from '../../src/shared/message_handler';
AbortException,
createPromiseCapability,
UnknownErrorException,
} from "../../src/shared/util";
import { LoopbackPort } from "../../src/display/api";
import { MessageHandler } from "../../src/shared/message_handler";
describe('message_handler', function () {
describe("message_handler", function() {
// Sleep function to wait for sometime, similar to setTimeout but faster.
function sleep(ticks) {
return Promise.resolve().then(() => {
return (ticks && sleep(ticks - 1));
return ticks && sleep(ticks - 1);
});
}
describe('sendWithStream', function () {
it('should return a ReadableStream', function () {
describe("sendWithStream", function() {
it("should return a ReadableStream", function() {
let port = new LoopbackPort();
let messageHandler1 = new MessageHandler('main', 'worker', port);
let readable = messageHandler1.sendWithStream('fakeHandler');
let messageHandler1 = new MessageHandler("main", "worker", port);
let readable = messageHandler1.sendWithStream("fakeHandler");
// Check if readable is an instance of ReadableStream.
expect(typeof readable).toEqual('object');
expect(typeof readable.getReader).toEqual('function');
expect(typeof readable).toEqual("object");
expect(typeof readable.getReader).toEqual("function");
});
it('should read using a reader', function (done) {
let log = '';
it("should read using a reader", function(done) {
let log = "";
let port = new LoopbackPort();
let messageHandler1 = new MessageHandler('main', 'worker', port);
let messageHandler2 = new MessageHandler('worker', 'main', port);
messageHandler2.on('fakeHandler', (data, sink) => {
sink.onPull = function () {
log += 'p';
let messageHandler1 = new MessageHandler("main", "worker", port);
let messageHandler2 = new MessageHandler("worker", "main", port);
messageHandler2.on("fakeHandler", (data, sink) => {
sink.onPull = function() {
log += "p";
};
sink.onCancel = function (reason) {
log += 'c';
sink.onCancel = function(reason) {
log += "c";
};
sink.ready.then(() => {
sink.enqueue('hi');
return sink.ready;
}).then(() => {
sink.close();
});
sink.ready
.then(() => {
sink.enqueue("hi");
return sink.ready;
})
.then(() => {
sink.close();
});
return sleep(5);
});
let readable = messageHandler1.sendWithStream('fakeHandler', {}, {
highWaterMark: 1,
size() {
return 1;
},
});
let readable = messageHandler1.sendWithStream(
"fakeHandler",
{},
{
highWaterMark: 1,
size() {
return 1;
},
}
);
let reader = readable.getReader();
sleep(10).then(() => {
expect(log).toEqual('');
return reader.read();
}).then((result) => {
expect(log).toEqual('p');
expect(result.value).toEqual('hi');
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
return reader.read();
}).then((result) => {
expect(result.value).toEqual(undefined);
expect(result.done).toEqual(true);
done();
});
sleep(10)
.then(() => {
expect(log).toEqual("");
return reader.read();
})
.then(result => {
expect(log).toEqual("p");
expect(result.value).toEqual("hi");
expect(result.done).toEqual(false);
return sleep(10);
})
.then(() => {
return reader.read();
})
.then(result => {
expect(result.value).toEqual(undefined);
expect(result.done).toEqual(true);
done();
});
});
it('should not read any data when cancelled', function (done) {
let log = '';
it("should not read any data when cancelled", function(done) {
let log = "";
let port = new LoopbackPort();
let messageHandler2 = new MessageHandler('worker', 'main', port);
messageHandler2.on('fakeHandler', (data, sink) => {
sink.onPull = function () {
log += 'p';
let messageHandler2 = new MessageHandler("worker", "main", port);
messageHandler2.on("fakeHandler", (data, sink) => {
sink.onPull = function() {
log += "p";
};
sink.onCancel = function (reason) {
log += 'c';
sink.onCancel = function(reason) {
log += "c";
};
log += '0';
sink.ready.then(() => {
log += '1';
sink.enqueue([1, 2, 3, 4], 4);
return sink.ready;
}).then(() => {
log += '2';
sink.enqueue([5, 6, 7, 8], 4);
return sink.ready;
}).then(() => {
log += '3';
sink.close();
}, () => {
log += '4';
});
});
let messageHandler1 = new MessageHandler('main', 'worker', port);
let readable = messageHandler1.sendWithStream('fakeHandler', {}, {
highWaterMark: 4,
size(arr) {
return arr.length;
},
log += "0";
sink.ready
.then(() => {
log += "1";
sink.enqueue([1, 2, 3, 4], 4);
return sink.ready;
})
.then(() => {
log += "2";
sink.enqueue([5, 6, 7, 8], 4);
return sink.ready;
})
.then(
() => {
log += "3";
sink.close();
},
() => {
log += "4";
}
);
});
let messageHandler1 = new MessageHandler("main", "worker", port);
let readable = messageHandler1.sendWithStream(
"fakeHandler",
{},
{
highWaterMark: 4,
size(arr) {
return arr.length;
},
}
);
let reader = readable.getReader();
sleep(10).then(() => {
expect(log).toEqual('01');
return reader.read();
}).then((result) => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
expect(log).toEqual('01p2');
return reader.cancel(new AbortException('reader cancelled.'));
}).then(() => {
expect(log).toEqual('01p2c4');
done();
});
sleep(10)
.then(() => {
expect(log).toEqual("01");
return reader.read();
})
.then(result => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return sleep(10);
})
.then(() => {
expect(log).toEqual("01p2");
return reader.cancel(new AbortException("reader cancelled."));
})
.then(() => {
expect(log).toEqual("01p2c4");
done();
});
});
it('should not read when errored', function(done) {
let log = '';
it("should not read when errored", function(done) {
let log = "";
let port = new LoopbackPort();
let messageHandler2 = new MessageHandler('worker', 'main', port);
messageHandler2.on('fakeHandler', (data, sink) => {
sink.onPull = function () {
log += 'p';
let messageHandler2 = new MessageHandler("worker", "main", port);
messageHandler2.on("fakeHandler", (data, sink) => {
sink.onPull = function() {
log += "p";
};
sink.onCancel = function (reason) {
log += 'c';
sink.onCancel = function(reason) {
log += "c";
};
log += '0';
sink.ready.then(() => {
log += '1';
sink.enqueue([1, 2, 3, 4], 4);
return sink.ready;
}).then(() => {
log += 'e';
sink.error(new Error('should not read when errored'));
});
});
let messageHandler1 = new MessageHandler('main', 'worker', port);
let readable = messageHandler1.sendWithStream('fakeHandler', {}, {
highWaterMark: 4,
size(arr) {
return arr.length;
},
log += "0";
sink.ready
.then(() => {
log += "1";
sink.enqueue([1, 2, 3, 4], 4);
return sink.ready;
})
.then(() => {
log += "e";
sink.error(new Error("should not read when errored"));
});
});
let messageHandler1 = new MessageHandler("main", "worker", port);
let readable = messageHandler1.sendWithStream(
"fakeHandler",
{},
{
highWaterMark: 4,
size(arr) {
return arr.length;
},
}
);
let reader = readable.getReader();
sleep(10).then(() => {
expect(log).toEqual('01');
return reader.read();
}).then((result) => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return reader.read();
}).catch((reason) => {
expect(log).toEqual('01pe');
expect(reason instanceof UnknownErrorException).toEqual(true);
expect(reason.message).toEqual('should not read when errored');
done();
});
sleep(10)
.then(() => {
expect(log).toEqual("01");
return reader.read();
})
.then(result => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return reader.read();
})
.catch(reason => {
expect(log).toEqual("01pe");
expect(reason instanceof UnknownErrorException).toEqual(true);
expect(reason.message).toEqual("should not read when errored");
done();
});
});
it('should read data with blocking promise', function (done) {
let log = '';
it("should read data with blocking promise", function(done) {
let log = "";
let port = new LoopbackPort();
let messageHandler2 = new MessageHandler('worker', 'main', port);
messageHandler2.on('fakeHandler', (data, sink) => {
sink.onPull = function () {
log += 'p';
let messageHandler2 = new MessageHandler("worker", "main", port);
messageHandler2.on("fakeHandler", (data, sink) => {
sink.onPull = function() {
log += "p";
};
sink.onCancel = function (reason) {
log += 'c';
sink.onCancel = function(reason) {
log += "c";
};
log += '0';
sink.ready.then(() => {
log += '1';
sink.enqueue([1, 2, 3, 4], 4);
return sink.ready;
}).then(() => {
log += '2';
sink.enqueue([5, 6, 7, 8], 4);
return sink.ready;
}).then(() => {
sink.close();
});
log += "0";
sink.ready
.then(() => {
log += "1";
sink.enqueue([1, 2, 3, 4], 4);
return sink.ready;
})
.then(() => {
log += "2";
sink.enqueue([5, 6, 7, 8], 4);
return sink.ready;
})
.then(() => {
sink.close();
});
});
let messageHandler1 = new MessageHandler('main', 'worker', port);
let readable = messageHandler1.sendWithStream('fakeHandler', {}, {
highWaterMark: 4,
size(arr) {
return arr.length;
},
});
let messageHandler1 = new MessageHandler("main", "worker", port);
let readable = messageHandler1.sendWithStream(
"fakeHandler",
{},
{
highWaterMark: 4,
size(arr) {
return arr.length;
},
}
);
let reader = readable.getReader();
// Sleep for 10ms, so that read() is not unblocking the ready promise.
// Chain all read() to stream in sequence.
sleep(10).then(() => {
expect(log).toEqual('01');
return reader.read();
}).then((result) => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
expect(log).toEqual('01p2');
return reader.read();
}).then((result) => {
expect(result.value).toEqual([5, 6, 7, 8]);
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
expect(log).toEqual('01p2p');
return reader.read();
}).then((result) => {
expect(result.value).toEqual(undefined);
expect(result.done).toEqual(true);
done();
});
});
it('should read data with blocking promise and buffer whole data' +
' into stream', function (done) {
let log = '';
let port = new LoopbackPort();
let messageHandler2 = new MessageHandler('worker', 'main', port);
messageHandler2.on('fakeHandler', (data, sink) => {
sink.onPull = function () {
log += 'p';
};
sink.onCancel = function (reason) {
log += 'c';
};
log += '0';
sink.ready.then(() => {
log += '1';
sink.enqueue([1, 2, 3, 4], 4);
return sink.ready;
}).then(() => {
log += '2';
sink.enqueue([5, 6, 7, 8], 4);
return sink.ready;
}).then(() => {
sink.close();
sleep(10)
.then(() => {
expect(log).toEqual("01");
return reader.read();
})
.then(result => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return sleep(10);
})
.then(() => {
expect(log).toEqual("01p2");
return reader.read();
})
.then(result => {
expect(result.value).toEqual([5, 6, 7, 8]);
expect(result.done).toEqual(false);
return sleep(10);
})
.then(() => {
expect(log).toEqual("01p2p");
return reader.read();
})
.then(result => {
expect(result.value).toEqual(undefined);
expect(result.done).toEqual(true);
done();
});
return sleep(10);
});
let messageHandler1 = new MessageHandler('main', 'worker', port);
let readable = messageHandler1.sendWithStream('fakeHandler', {}, {
highWaterMark: 8,
size(arr) {
return arr.length;
},
});
let reader = readable.getReader();
sleep(10).then(() => {
expect(log).toEqual('012');
return reader.read();
}).then((result) => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
expect(log).toEqual('012p');
return reader.read();
}).then((result) => {
expect(result.value).toEqual([5, 6, 7, 8]);
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
expect(log).toEqual('012p');
return reader.read();
}).then((result) => {
expect(result.value).toEqual(undefined);
expect(result.done).toEqual(true);
done();
});
});
it('should ignore any pull after close is called', function (done) {
let log = '';
it(
"should read data with blocking promise and buffer whole data" +
" into stream",
function(done) {
let log = "";
let port = new LoopbackPort();
let messageHandler2 = new MessageHandler("worker", "main", port);
messageHandler2.on("fakeHandler", (data, sink) => {
sink.onPull = function() {
log += "p";
};
sink.onCancel = function(reason) {
log += "c";
};
log += "0";
sink.ready
.then(() => {
log += "1";
sink.enqueue([1, 2, 3, 4], 4);
return sink.ready;
})
.then(() => {
log += "2";
sink.enqueue([5, 6, 7, 8], 4);
return sink.ready;
})
.then(() => {
sink.close();
});
return sleep(10);
});
let messageHandler1 = new MessageHandler("main", "worker", port);
let readable = messageHandler1.sendWithStream(
"fakeHandler",
{},
{
highWaterMark: 8,
size(arr) {
return arr.length;
},
}
);
let reader = readable.getReader();
sleep(10)
.then(() => {
expect(log).toEqual("012");
return reader.read();
})
.then(result => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return sleep(10);
})
.then(() => {
expect(log).toEqual("012p");
return reader.read();
})
.then(result => {
expect(result.value).toEqual([5, 6, 7, 8]);
expect(result.done).toEqual(false);
return sleep(10);
})
.then(() => {
expect(log).toEqual("012p");
return reader.read();
})
.then(result => {
expect(result.value).toEqual(undefined);
expect(result.done).toEqual(true);
done();
});
}
);
it("should ignore any pull after close is called", function(done) {
let log = "";
let port = new LoopbackPort();
let capability = createPromiseCapability();
let messageHandler2 = new MessageHandler('worker', 'main', port);
messageHandler2.on('fakeHandler', (data, sink) => {
sink.onPull = function () {
log += 'p';
let messageHandler2 = new MessageHandler("worker", "main", port);
messageHandler2.on("fakeHandler", (data, sink) => {
sink.onPull = function() {
log += "p";
};
sink.onCancel = function (reason) {
log += 'c';
sink.onCancel = function(reason) {
log += "c";
};
log += '0';
log += "0";
sink.ready.then(() => {
log += '1';
log += "1";
sink.enqueue([1, 2, 3, 4], 4);
});
return capability.promise.then(() => {
@ -322,34 +386,42 @@ describe('message_handler', function () {
});
});
let messageHandler1 = new MessageHandler('main', 'worker', port);
let readable = messageHandler1.sendWithStream('fakeHandler', {}, {
highWaterMark: 10,
size(arr) {
return arr.length;
},
});
let messageHandler1 = new MessageHandler("main", "worker", port);
let readable = messageHandler1.sendWithStream(
"fakeHandler",
{},
{
highWaterMark: 10,
size(arr) {
return arr.length;
},
}
);
let reader = readable.getReader();
sleep(10).then(() => {
expect(log).toEqual('01');
capability.resolve();
return capability.promise.then(() => {
sleep(10)
.then(() => {
expect(log).toEqual("01");
capability.resolve();
return capability.promise.then(() => {
return reader.read();
});
})
.then(result => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return sleep(10);
})
.then(() => {
expect(log).toEqual("01");
return reader.read();
})
.then(result => {
expect(result.value).toEqual(undefined);
expect(result.done).toEqual(true);
done();
});
}).then((result) => {
expect(result.value).toEqual([1, 2, 3, 4]);
expect(result.done).toEqual(false);
return sleep(10);
}).then(() => {
expect(log).toEqual('01');
return reader.read();
}).then((result) => {
expect(result.value).toEqual(undefined);
expect(result.done).toEqual(true);
done();
});
});
});
});

View file

@ -13,215 +13,222 @@
* limitations under the License.
*/
import { isEmptyObj } from '../../src/shared/util';
import { Metadata } from '../../src/display/metadata';
import { isEmptyObj } from "../../src/shared/util";
import { Metadata } from "../../src/display/metadata";
describe('metadata', function() {
it('should handle valid metadata', function() {
const data = '<x:xmpmeta xmlns:x=\'adobe:ns:meta/\'>' +
'<rdf:RDF xmlns:rdf=\'http://www.w3.org/1999/02/22-rdf-syntax-ns#\'>' +
'<rdf:Description xmlns:dc=\'http://purl.org/dc/elements/1.1/\'>' +
describe("metadata", function() {
it("should handle valid metadata", function() {
const data =
"<x:xmpmeta xmlns:x='adobe:ns:meta/'>" +
"<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>" +
"<rdf:Description xmlns:dc='http://purl.org/dc/elements/1.1/'>" +
'<dc:title><rdf:Alt><rdf:li xml:lang="x-default">Foo bar baz</rdf:li>' +
'</rdf:Alt></dc:title></rdf:Description></rdf:RDF></x:xmpmeta>';
"</rdf:Alt></dc:title></rdf:Description></rdf:RDF></x:xmpmeta>";
const metadata = new Metadata(data);
expect(metadata.has('dc:title')).toBeTruthy();
expect(metadata.has('dc:qux')).toBeFalsy();
expect(metadata.has("dc:title")).toBeTruthy();
expect(metadata.has("dc:qux")).toBeFalsy();
expect(metadata.get('dc:title')).toEqual('Foo bar baz');
expect(metadata.get('dc:qux')).toEqual(null);
expect(metadata.get("dc:title")).toEqual("Foo bar baz");
expect(metadata.get("dc:qux")).toEqual(null);
expect(metadata.getAll()).toEqual({ 'dc:title': 'Foo bar baz', });
expect(metadata.getAll()).toEqual({ "dc:title": "Foo bar baz" });
});
it('should repair and handle invalid metadata', function() {
const data = '<x:xmpmeta xmlns:x=\'adobe:ns:meta/\'>' +
'<rdf:RDF xmlns:rdf=\'http://www.w3.org/1999/02/22-rdf-syntax-ns#\'>' +
'<rdf:Description xmlns:dc=\'http://purl.org/dc/elements/1.1/\'>' +
'<dc:title>\\376\\377\\000P\\000D\\000F\\000&</dc:title>' +
'</rdf:Description></rdf:RDF></x:xmpmeta>';
it("should repair and handle invalid metadata", function() {
const data =
"<x:xmpmeta xmlns:x='adobe:ns:meta/'>" +
"<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>" +
"<rdf:Description xmlns:dc='http://purl.org/dc/elements/1.1/'>" +
"<dc:title>\\376\\377\\000P\\000D\\000F\\000&</dc:title>" +
"</rdf:Description></rdf:RDF></x:xmpmeta>";
const metadata = new Metadata(data);
expect(metadata.has('dc:title')).toBeTruthy();
expect(metadata.has('dc:qux')).toBeFalsy();
expect(metadata.has("dc:title")).toBeTruthy();
expect(metadata.has("dc:qux")).toBeFalsy();
expect(metadata.get('dc:title')).toEqual('PDF&');
expect(metadata.get('dc:qux')).toEqual(null);
expect(metadata.get("dc:title")).toEqual("PDF&");
expect(metadata.get("dc:qux")).toEqual(null);
expect(metadata.getAll()).toEqual({ 'dc:title': 'PDF&', });
expect(metadata.getAll()).toEqual({ "dc:title": "PDF&" });
});
it('should repair and handle invalid metadata (bug 1424938)', function() {
const data = '<x:xmpmeta xmlns:x=\'adobe:ns:meta/\' ' +
'x:xmptk=\'XMP toolkit 2.9.1-13, framework 1.6\'>' +
'<rdf:RDF xmlns:rdf=\'http://www.w3.org/1999/02/22-rdf-syntax-ns#\' ' +
'xmlns:iX=\'http://ns.adobe.com/iX/1.0/\'>' +
'<rdf:Description rdf:about=\'61652fa7-fc1f-11dd-0000-ce81d41f9ecf\' ' +
'xmlns:pdf=\'http://ns.adobe.com/pdf/1.3/\' ' +
'pdf:Producer=\'GPL Ghostscript 8.63\'/>' +
'<rdf:Description rdf:about=\'61652fa7-fc1f-11dd-0000-ce81d41f9ecf\' ' +
'xmlns:xap=\'http://ns.adobe.com/xap/1.0/\' ' +
'xap:ModifyDate=\'2009-02-13T12:42:54+01:00\' ' +
'xap:CreateDate=\'2009-02-13T12:42:54+01:00\'>' +
'<xap:CreatorTool>\\376\\377\\000P\\000D\\000F\\000C\\000r\\000e\\000a' +
'\\000t\\000o\\000r\\000 \\000V\\000e\\000r\\000s\\000i\\000o\\000n' +
'\\000 \\0000\\000.\\0009\\000.\\0006</xap:CreatorTool>' +
'</rdf:Description><rdf:Description ' +
'rdf:about=\'61652fa7-fc1f-11dd-0000-ce81d41f9ecf\' ' +
'xmlns:xapMM=\'http://ns.adobe.com/xap/1.0/mm/\' ' +
'xapMM:DocumentID=\'61652fa7-fc1f-11dd-0000-ce81d41f9ecf\'/>' +
'<rdf:Description rdf:about=\'61652fa7-fc1f-11dd-0000-ce81d41f9ecf\' ' +
'xmlns:dc=\'http://purl.org/dc/elements/1.1/\' ' +
'dc:format=\'application/pdf\'><dc:title><rdf:Alt>' +
'<rdf:li xml:lang=\'x-default\'>\\376\\377\\000L\\000&apos;\\000O\\000d' +
'\\000i\\000s\\000s\\000e\\000e\\000 \\000t\\000h\\000\\351\\000m\\000a' +
'\\000t\\000i\\000q\\000u\\000e\\000 \\000l\\000o\\000g\\000o\\000 ' +
'\\000O\\000d\\000i\\000s\\000s\\000\\351\\000\\351\\000 \\000-\\000 ' +
'\\000d\\000\\351\\000c\\000e\\000m\\000b\\000r\\000e\\000 \\0002\\0000' +
'\\0000\\0008\\000.\\000p\\000u\\000b</rdf:li></rdf:Alt></dc:title>' +
'<dc:creator><rdf:Seq><rdf:li>\\376\\377\\000O\\000D\\000I\\000S' +
'</rdf:li></rdf:Seq></dc:creator></rdf:Description></rdf:RDF>' +
'</x:xmpmeta>';
it("should repair and handle invalid metadata (bug 1424938)", function() {
const data =
"<x:xmpmeta xmlns:x='adobe:ns:meta/' " +
"x:xmptk='XMP toolkit 2.9.1-13, framework 1.6'>" +
"<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' " +
"xmlns:iX='http://ns.adobe.com/iX/1.0/'>" +
"<rdf:Description rdf:about='61652fa7-fc1f-11dd-0000-ce81d41f9ecf' " +
"xmlns:pdf='http://ns.adobe.com/pdf/1.3/' " +
"pdf:Producer='GPL Ghostscript 8.63'/>" +
"<rdf:Description rdf:about='61652fa7-fc1f-11dd-0000-ce81d41f9ecf' " +
"xmlns:xap='http://ns.adobe.com/xap/1.0/' " +
"xap:ModifyDate='2009-02-13T12:42:54+01:00' " +
"xap:CreateDate='2009-02-13T12:42:54+01:00'>" +
"<xap:CreatorTool>\\376\\377\\000P\\000D\\000F\\000C\\000r\\000e\\000a" +
"\\000t\\000o\\000r\\000 \\000V\\000e\\000r\\000s\\000i\\000o\\000n" +
"\\000 \\0000\\000.\\0009\\000.\\0006</xap:CreatorTool>" +
"</rdf:Description><rdf:Description " +
"rdf:about='61652fa7-fc1f-11dd-0000-ce81d41f9ecf' " +
"xmlns:xapMM='http://ns.adobe.com/xap/1.0/mm/' " +
"xapMM:DocumentID='61652fa7-fc1f-11dd-0000-ce81d41f9ecf'/>" +
"<rdf:Description rdf:about='61652fa7-fc1f-11dd-0000-ce81d41f9ecf' " +
"xmlns:dc='http://purl.org/dc/elements/1.1/' " +
"dc:format='application/pdf'><dc:title><rdf:Alt>" +
"<rdf:li xml:lang='x-default'>\\376\\377\\000L\\000&apos;\\000O\\000d" +
"\\000i\\000s\\000s\\000e\\000e\\000 \\000t\\000h\\000\\351\\000m\\000a" +
"\\000t\\000i\\000q\\000u\\000e\\000 \\000l\\000o\\000g\\000o\\000 " +
"\\000O\\000d\\000i\\000s\\000s\\000\\351\\000\\351\\000 \\000-\\000 " +
"\\000d\\000\\351\\000c\\000e\\000m\\000b\\000r\\000e\\000 \\0002\\0000" +
"\\0000\\0008\\000.\\000p\\000u\\000b</rdf:li></rdf:Alt></dc:title>" +
"<dc:creator><rdf:Seq><rdf:li>\\376\\377\\000O\\000D\\000I\\000S" +
"</rdf:li></rdf:Seq></dc:creator></rdf:Description></rdf:RDF>" +
"</x:xmpmeta>";
const metadata = new Metadata(data);
expect(metadata.has('dc:title')).toBeTruthy();
expect(metadata.has('dc:qux')).toBeFalsy();
expect(metadata.has("dc:title")).toBeTruthy();
expect(metadata.has("dc:qux")).toBeFalsy();
expect(metadata.get('dc:title')).toEqual(
'L\'Odissee thématique logo Odisséé - décembre 2008.pub');
expect(metadata.get('dc:qux')).toEqual(null);
expect(metadata.get("dc:title")).toEqual(
"L'Odissee thématique logo Odisséé - décembre 2008.pub"
);
expect(metadata.get("dc:qux")).toEqual(null);
expect(metadata.getAll()).toEqual({
'dc:creator': 'ODIS',
'dc:title': 'L\'Odissee thématique logo Odisséé - décembre 2008.pub',
'xap:creatortool': 'PDFCreator Version 0.9.6',
"dc:creator": "ODIS",
"dc:title": "L'Odissee thématique logo Odisséé - décembre 2008.pub",
"xap:creatortool": "PDFCreator Version 0.9.6",
});
});
it('should gracefully handle incomplete tags (issue 8884)', function() {
const data = '<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d' +
it("should gracefully handle incomplete tags (issue 8884)", function() {
const data =
'<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d' +
'<x:xmpmeta xmlns:x="adobe:ns:meta/">' +
'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">' +
'<rdf:Description rdf:about=""' +
'xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/">' +
'</rdf:Description>' +
"</rdf:Description>" +
'<rdf:Description rdf:about=""' +
'xmlns:xap="http://ns.adobe.com/xap/1.0/">' +
'<xap:ModifyDate>2010-03-25T11:20:09-04:00</xap:ModifyDate>' +
'<xap:CreateDate>2010-03-25T11:20:09-04:00</xap:CreateDate>' +
'<xap:MetadataDate>2010-03-25T11:20:09-04:00</xap:MetadataDate>' +
'</rdf:Description>' +
"<xap:ModifyDate>2010-03-25T11:20:09-04:00</xap:ModifyDate>" +
"<xap:CreateDate>2010-03-25T11:20:09-04:00</xap:CreateDate>" +
"<xap:MetadataDate>2010-03-25T11:20:09-04:00</xap:MetadataDate>" +
"</rdf:Description>" +
'<rdf:Description rdf:about=""' +
'xmlns:dc="http://purl.org/dc/elements/1.1/">' +
'<dc:format>application/pdf</dc:format>' +
'</rdf:Description>' +
"<dc:format>application/pdf</dc:format>" +
"</rdf:Description>" +
'<rdf:Description rdf:about=""' +
'xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">' +
'<pdfaid:part>1</pdfaid:part>' +
'<pdfaid:conformance>A</pdfaid:conformance>' +
'</rdf:Description>' +
'</rdf:RDF>' +
'</x:xmpmeta>' +
"<pdfaid:part>1</pdfaid:part>" +
"<pdfaid:conformance>A</pdfaid:conformance>" +
"</rdf:Description>" +
"</rdf:RDF>" +
"</x:xmpmeta>" +
'<?xpacket end="w"?>';
const metadata = new Metadata(data);
expect(isEmptyObj(metadata.getAll())).toEqual(true);
});
it('should gracefully handle "junk" before the actual metadata (issue 10395)',
function() {
const data = '<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>' +
it('should gracefully handle "junk" before the actual metadata (issue 10395)', function() {
const data =
'<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>' +
'<x:xmpmeta x:xmptk="TallComponents PDFObjects 1.0" ' +
'xmlns:x="adobe:ns:meta/">' +
'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">' +
'<rdf:Description rdf:about="" ' +
'xmlns:pdf="http://ns.adobe.com/pdf/1.3/">' +
'<pdf:Producer>PDFKit.NET 4.0.102.0</pdf:Producer>' +
'<pdf:Keywords></pdf:Keywords>' +
'<pdf:PDFVersion>1.7</pdf:PDFVersion></rdf:Description>' +
"<pdf:Producer>PDFKit.NET 4.0.102.0</pdf:Producer>" +
"<pdf:Keywords></pdf:Keywords>" +
"<pdf:PDFVersion>1.7</pdf:PDFVersion></rdf:Description>" +
'<rdf:Description rdf:about="" ' +
'xmlns:xap="http://ns.adobe.com/xap/1.0/">' +
'<xap:CreateDate>2018-12-27T13:50:36-08:00</xap:CreateDate>' +
'<xap:ModifyDate>2018-12-27T13:50:38-08:00</xap:ModifyDate>' +
'<xap:CreatorTool></xap:CreatorTool>' +
'<xap:MetadataDate>2018-12-27T13:50:38-08:00</xap:MetadataDate>' +
"<xap:CreateDate>2018-12-27T13:50:36-08:00</xap:CreateDate>" +
"<xap:ModifyDate>2018-12-27T13:50:38-08:00</xap:ModifyDate>" +
"<xap:CreatorTool></xap:CreatorTool>" +
"<xap:MetadataDate>2018-12-27T13:50:38-08:00</xap:MetadataDate>" +
'</rdf:Description><rdf:Description rdf:about="" ' +
'xmlns:dc="http://purl.org/dc/elements/1.1/">' +
'<dc:creator><rdf:Seq><rdf:li></rdf:li></rdf:Seq></dc:creator>' +
'<dc:subject><rdf:Bag /></dc:subject>' +
"<dc:creator><rdf:Seq><rdf:li></rdf:li></rdf:Seq></dc:creator>" +
"<dc:subject><rdf:Bag /></dc:subject>" +
'<dc:description><rdf:Alt><rdf:li xml:lang="x-default">' +
'</rdf:li></rdf:Alt></dc:description>' +
"</rdf:li></rdf:Alt></dc:description>" +
'<dc:title><rdf:Alt><rdf:li xml:lang="x-default"></rdf:li>' +
'</rdf:Alt></dc:title><dc:format>application/pdf</dc:format>' +
"</rdf:Alt></dc:title><dc:format>application/pdf</dc:format>" +
'</rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end="w"?>';
const metadata = new Metadata(data);
expect(metadata.has('dc:title')).toBeTruthy();
expect(metadata.has('dc:qux')).toBeFalsy();
expect(metadata.has("dc:title")).toBeTruthy();
expect(metadata.has("dc:qux")).toBeFalsy();
expect(metadata.get('dc:title')).toEqual('');
expect(metadata.get('dc:qux')).toEqual(null);
expect(metadata.get("dc:title")).toEqual("");
expect(metadata.get("dc:qux")).toEqual(null);
expect(metadata.getAll()).toEqual({
'dc:creator': '',
'dc:description': '',
'dc:format': 'application/pdf',
'dc:subject': '',
'dc:title': '',
'pdf:keywords': '',
'pdf:pdfversion': '1.7',
'pdf:producer': 'PDFKit.NET 4.0.102.0',
'xap:createdate': '2018-12-27T13:50:36-08:00',
'xap:creatortool': '',
'xap:metadatadate': '2018-12-27T13:50:38-08:00',
'xap:modifydate': '2018-12-27T13:50:38-08:00',
"dc:creator": "",
"dc:description": "",
"dc:format": "application/pdf",
"dc:subject": "",
"dc:title": "",
"pdf:keywords": "",
"pdf:pdfversion": "1.7",
"pdf:producer": "PDFKit.NET 4.0.102.0",
"xap:createdate": "2018-12-27T13:50:36-08:00",
"xap:creatortool": "",
"xap:metadatadate": "2018-12-27T13:50:38-08:00",
"xap:modifydate": "2018-12-27T13:50:38-08:00",
});
});
it('should correctly handle metadata containing "&apos" (issue 10407)',
function() {
const data = '<x:xmpmeta xmlns:x=\'adobe:ns:meta/\'>' +
'<rdf:RDF xmlns:rdf=\'http://www.w3.org/1999/02/22-rdf-syntax-ns#\'>' +
'<rdf:Description xmlns:dc=\'http://purl.org/dc/elements/1.1/\'>' +
'<dc:title><rdf:Alt>' +
it('should correctly handle metadata containing "&apos" (issue 10407)', function() {
const data =
"<x:xmpmeta xmlns:x='adobe:ns:meta/'>" +
"<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>" +
"<rdf:Description xmlns:dc='http://purl.org/dc/elements/1.1/'>" +
"<dc:title><rdf:Alt>" +
'<rdf:li xml:lang="x-default">&apos;Foo bar baz&apos;</rdf:li>' +
'</rdf:Alt></dc:title></rdf:Description></rdf:RDF></x:xmpmeta>';
"</rdf:Alt></dc:title></rdf:Description></rdf:RDF></x:xmpmeta>";
const metadata = new Metadata(data);
expect(metadata.has('dc:title')).toBeTruthy();
expect(metadata.has('dc:qux')).toBeFalsy();
expect(metadata.has("dc:title")).toBeTruthy();
expect(metadata.has("dc:qux")).toBeFalsy();
expect(metadata.get('dc:title')).toEqual('\'Foo bar baz\'');
expect(metadata.get('dc:qux')).toEqual(null);
expect(metadata.get("dc:title")).toEqual("'Foo bar baz'");
expect(metadata.get("dc:qux")).toEqual(null);
expect(metadata.getAll()).toEqual({ 'dc:title': '\'Foo bar baz\'', });
expect(metadata.getAll()).toEqual({ "dc:title": "'Foo bar baz'" });
});
it('should gracefully handle unbalanced end tags (issue 10410)', function() {
const data = '<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>' +
it("should gracefully handle unbalanced end tags (issue 10410)", function() {
const data =
'<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>' +
'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">' +
'<rdf:Description rdf:about="" ' +
'xmlns:pdf="http://ns.adobe.com/pdf/1.3/">' +
'<pdf:Producer>Soda PDF 5</pdf:Producer></rdf:Description>' +
"<pdf:Producer>Soda PDF 5</pdf:Producer></rdf:Description>" +
'<rdf:Description rdf:about="" ' +
'xmlns:xap="http://ns.adobe.com/xap/1.0/">' +
'<xap:CreateDate>2018-10-02T08:14:49-05:00</xap:CreateDate>' +
'<xap:CreatorTool>Soda PDF 5</xap:CreatorTool>' +
'<xap:MetadataDate>2018-10-02T08:14:49-05:00</xap:MetadataDate> ' +
'<xap:ModifyDate>2018-10-02T08:14:49-05:00</xap:ModifyDate>' +
"<xap:CreateDate>2018-10-02T08:14:49-05:00</xap:CreateDate>" +
"<xap:CreatorTool>Soda PDF 5</xap:CreatorTool>" +
"<xap:MetadataDate>2018-10-02T08:14:49-05:00</xap:MetadataDate> " +
"<xap:ModifyDate>2018-10-02T08:14:49-05:00</xap:ModifyDate>" +
'</rdf:Description><rdf:Description rdf:about="" ' +
'xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">' +
'<xmpMM:DocumentID>uuid:00000000-1c84-3cf9-89ba-bef0e729c831' +
'</xmpMM:DocumentID></rdf:Description>' +
"<xmpMM:DocumentID>uuid:00000000-1c84-3cf9-89ba-bef0e729c831" +
"</xmpMM:DocumentID></rdf:Description>" +
'</rdf:RDF></x:xmpmeta><?xpacket end="w"?>';
const metadata = new Metadata(data);
expect(isEmptyObj(metadata.getAll())).toEqual(true);
});
it('should not be vulnerable to the billion laughs attack', function() {
const data = '<?xml version="1.0"?>' +
'<!DOCTYPE lolz [' +
it("should not be vulnerable to the billion laughs attack", function() {
const data =
'<?xml version="1.0"?>' +
"<!DOCTYPE lolz [" +
' <!ENTITY lol "lol">' +
' <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">' +
' <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">' +
@ -232,24 +239,24 @@ describe('metadata', function() {
' <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">' +
' <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">' +
' <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">' +
']>' +
"]>" +
'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">' +
' <rdf:Description xmlns:dc="http://purl.org/dc/elements/1.1/">' +
' <dc:title>' +
' <rdf:Alt>' +
" <dc:title>" +
" <rdf:Alt>" +
' <rdf:li xml:lang="x-default">a&lol9;b</rdf:li>' +
' </rdf:Alt>' +
' </dc:title>' +
' </rdf:Description>' +
'</rdf:RDF>';
" </rdf:Alt>" +
" </dc:title>" +
" </rdf:Description>" +
"</rdf:RDF>";
const metadata = new Metadata(data);
expect(metadata.has('dc:title')).toBeTruthy();
expect(metadata.has('dc:qux')).toBeFalsy();
expect(metadata.has("dc:title")).toBeTruthy();
expect(metadata.has("dc:qux")).toBeFalsy();
expect(metadata.get('dc:title')).toEqual('a&lol9;b');
expect(metadata.get('dc:qux')).toEqual(null);
expect(metadata.get("dc:title")).toEqual("a&lol9;b");
expect(metadata.get("dc:qux")).toEqual(null);
expect(metadata.getAll()).toEqual({ 'dc:title': 'a&lol9;b', });
expect(metadata.getAll()).toEqual({ "dc:title": "a&lol9;b" });
});
});

View file

@ -13,38 +13,38 @@
* limitations under the License.
*/
import { MurmurHash3_64 } from '../../src/core/murmurhash3';
import { MurmurHash3_64 } from "../../src/core/murmurhash3";
describe('MurmurHash3_64', function() {
it('instantiates without seed', function() {
describe("MurmurHash3_64", function() {
it("instantiates without seed", function() {
var hash = new MurmurHash3_64();
expect(hash).toEqual(jasmine.any(MurmurHash3_64));
});
it('instantiates with seed', function() {
it("instantiates with seed", function() {
var hash = new MurmurHash3_64(1);
expect(hash).toEqual(jasmine.any(MurmurHash3_64));
});
var hexDigestExpected = 'f61cfdbfdae0f65e';
var sourceText = 'test';
var hexDigestExpected = "f61cfdbfdae0f65e";
var sourceText = "test";
var sourceCharCodes = [116, 101, 115, 116]; // 't','e','s','t'
it('correctly generates a hash from a string', function() {
it("correctly generates a hash from a string", function() {
var hash = new MurmurHash3_64();
hash.update(sourceText);
expect(hash.hexdigest()).toEqual(hexDigestExpected);
});
it('correctly generates a hash from a Uint8Array', function() {
it("correctly generates a hash from a Uint8Array", function() {
var hash = new MurmurHash3_64();
hash.update(new Uint8Array(sourceCharCodes));
expect(hash.hexdigest()).toEqual(hexDigestExpected);
});
it('correctly generates a hash from a Uint32Array', function() {
it("correctly generates a hash from a Uint32Array", function() {
var hash = new MurmurHash3_64();
hash.update(new Uint32Array(new Uint8Array(sourceCharCodes).buffer));
expect(hash.hexdigest()).toEqual(hexDigestExpected);
});
it('changes the hash after update without seed', function() {
it("changes the hash after update without seed", function() {
var hash = new MurmurHash3_64();
var hexdigest1, hexdigest2;
hash.update(sourceText);
@ -53,7 +53,7 @@ describe('MurmurHash3_64', function() {
hexdigest2 = hash.hexdigest();
expect(hexdigest1).not.toEqual(hexdigest2);
});
it('changes the hash after update with seed', function() {
it("changes the hash after update with seed", function() {
var hash = new MurmurHash3_64(1);
var hexdigest1, hexdigest2;
hash.update(sourceText);

View file

@ -13,14 +13,14 @@
* limitations under the License.
*/
import { AbortException } from '../../src/shared/util';
import { PDFNetworkStream } from '../../src/display/network';
import { AbortException } from "../../src/shared/util";
import { PDFNetworkStream } from "../../src/display/network";
describe('network', function() {
var pdf1 = new URL('../pdfs/tracemonkey.pdf', window.location).href;
describe("network", function() {
var pdf1 = new URL("../pdfs/tracemonkey.pdf", window.location).href;
var pdf1Length = 1016315;
it('read without stream and range', function(done) {
it("read without stream and range", function(done) {
var stream = new PDFNetworkStream({
url: pdf1,
rangeChunkSize: 65536,
@ -31,14 +31,15 @@ describe('network', function() {
var fullReader = stream.getFullReader();
var isStreamingSupported, isRangeSupported;
var promise = fullReader.headersReady.then(function () {
var promise = fullReader.headersReady.then(function() {
isStreamingSupported = fullReader.isStreamingSupported;
isRangeSupported = fullReader.isRangeSupported;
});
var len = 0, count = 0;
var read = function () {
return fullReader.read().then(function (result) {
var len = 0,
count = 0;
var read = function() {
return fullReader.read().then(function(result) {
if (result.done) {
return undefined;
}
@ -50,18 +51,20 @@ describe('network', function() {
var readPromise = Promise.all([read(), promise]);
readPromise.then(function (page) {
expect(len).toEqual(pdf1Length);
expect(count).toEqual(1);
expect(isStreamingSupported).toEqual(false);
expect(isRangeSupported).toEqual(false);
done();
}).catch(function (reason) {
done.fail(reason);
});
readPromise
.then(function(page) {
expect(len).toEqual(pdf1Length);
expect(count).toEqual(1);
expect(isStreamingSupported).toEqual(false);
expect(isRangeSupported).toEqual(false);
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('read custom ranges', function (done) {
it("read custom ranges", function(done) {
// We don't test on browsers that don't support range request, so
// requiring this test to pass.
var rangeSize = 32768;
@ -76,24 +79,27 @@ describe('network', function() {
var fullReader = stream.getFullReader();
var isStreamingSupported, isRangeSupported, fullReaderCancelled;
var promise = fullReader.headersReady.then(function () {
var promise = fullReader.headersReady.then(function() {
isStreamingSupported = fullReader.isStreamingSupported;
isRangeSupported = fullReader.isRangeSupported;
// we shall be able to close the full reader without issues
fullReader.cancel(new AbortException('Don\'t need fullReader.'));
fullReader.cancel(new AbortException("Don't need fullReader."));
fullReaderCancelled = true;
});
// Skipping fullReader results, requesting something from the PDF end.
var tailSize = (pdf1Length % rangeSize) || rangeSize;
var tailSize = pdf1Length % rangeSize || rangeSize;
var range1Reader = stream.getRangeReader(pdf1Length - tailSize - rangeSize,
pdf1Length - tailSize);
var range1Reader = stream.getRangeReader(
pdf1Length - tailSize - rangeSize,
pdf1Length - tailSize
);
var range2Reader = stream.getRangeReader(pdf1Length - tailSize, pdf1Length);
var result1 = { value: 0, }, result2 = { value: 0, };
var read = function (reader, lenResult) {
return reader.read().then(function (result) {
var result1 = { value: 0 },
result2 = { value: 0 };
var read = function(reader, lenResult) {
return reader.read().then(function(result) {
if (result.done) {
return undefined;
}
@ -102,19 +108,23 @@ describe('network', function() {
});
};
var readPromises = Promise.all([read(range1Reader, result1),
read(range2Reader, result2),
promise]);
var readPromises = Promise.all([
read(range1Reader, result1),
read(range2Reader, result2),
promise,
]);
readPromises.then(function () {
expect(result1.value).toEqual(rangeSize);
expect(result2.value).toEqual(tailSize);
expect(isStreamingSupported).toEqual(false);
expect(isRangeSupported).toEqual(true);
expect(fullReaderCancelled).toEqual(true);
done();
}).catch(function (reason) {
done.fail(reason);
});
readPromises
.then(function() {
expect(result1.value).toEqual(rangeSize);
expect(result2.value).toEqual(tailSize);
expect(isStreamingSupported).toEqual(false);
expect(isRangeSupported).toEqual(true);
expect(fullReaderCancelled).toEqual(true);
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
});

View file

@ -14,347 +14,404 @@
*/
import {
createResponseStatusError, extractFilenameFromHeader,
validateRangeRequestCapabilities, validateResponseStatus
} from '../../src/display/network_utils';
createResponseStatusError,
extractFilenameFromHeader,
validateRangeRequestCapabilities,
validateResponseStatus,
} from "../../src/display/network_utils";
import {
MissingPDFException, UnexpectedResponseException
} from '../../src/shared/util';
MissingPDFException,
UnexpectedResponseException,
} from "../../src/shared/util";
describe('network_utils', function() {
describe('validateRangeRequestCapabilities', function() {
it('rejects range chunk sizes that are not larger than zero', function() {
describe("network_utils", function() {
describe("validateRangeRequestCapabilities", function() {
it("rejects range chunk sizes that are not larger than zero", function() {
expect(function() {
validateRangeRequestCapabilities({ rangeChunkSize: 0, });
}).toThrow(new Error('Range chunk size must be larger than zero'));
validateRangeRequestCapabilities({ rangeChunkSize: 0 });
}).toThrow(new Error("Range chunk size must be larger than zero"));
});
it('rejects disabled or non-HTTP range requests', function() {
expect(validateRangeRequestCapabilities({
disableRange: true,
isHttp: true,
getResponseHeader: (headerName) => {
if (headerName === 'Content-Length') {
return 8;
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})).toEqual({
it("rejects disabled or non-HTTP range requests", function() {
expect(
validateRangeRequestCapabilities({
disableRange: true,
isHttp: true,
getResponseHeader: headerName => {
if (headerName === "Content-Length") {
return 8;
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})
).toEqual({
allowRangeRequests: false,
suggestedLength: 8,
});
expect(validateRangeRequestCapabilities({
disableRange: false,
isHttp: false,
getResponseHeader: (headerName) => {
if (headerName === 'Content-Length') {
return 8;
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})).toEqual({
expect(
validateRangeRequestCapabilities({
disableRange: false,
isHttp: false,
getResponseHeader: headerName => {
if (headerName === "Content-Length") {
return 8;
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})
).toEqual({
allowRangeRequests: false,
suggestedLength: 8,
});
});
it('rejects invalid Accept-Ranges header values', function() {
expect(validateRangeRequestCapabilities({
disableRange: false,
isHttp: true,
getResponseHeader: (headerName) => {
if (headerName === 'Accept-Ranges') {
return 'none';
} else if (headerName === 'Content-Length') {
return 8;
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})).toEqual({
it("rejects invalid Accept-Ranges header values", function() {
expect(
validateRangeRequestCapabilities({
disableRange: false,
isHttp: true,
getResponseHeader: headerName => {
if (headerName === "Accept-Ranges") {
return "none";
} else if (headerName === "Content-Length") {
return 8;
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})
).toEqual({
allowRangeRequests: false,
suggestedLength: 8,
});
});
it('rejects invalid Content-Encoding header values', function() {
expect(validateRangeRequestCapabilities({
disableRange: false,
isHttp: true,
getResponseHeader: (headerName) => {
if (headerName === 'Accept-Ranges') {
return 'bytes';
} else if (headerName === 'Content-Encoding') {
return 'gzip';
} else if (headerName === 'Content-Length') {
return 8;
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})).toEqual({
it("rejects invalid Content-Encoding header values", function() {
expect(
validateRangeRequestCapabilities({
disableRange: false,
isHttp: true,
getResponseHeader: headerName => {
if (headerName === "Accept-Ranges") {
return "bytes";
} else if (headerName === "Content-Encoding") {
return "gzip";
} else if (headerName === "Content-Length") {
return 8;
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})
).toEqual({
allowRangeRequests: false,
suggestedLength: 8,
});
});
it('rejects invalid Content-Length header values', function() {
expect(validateRangeRequestCapabilities({
disableRange: false,
isHttp: true,
getResponseHeader: (headerName) => {
if (headerName === 'Accept-Ranges') {
return 'bytes';
} else if (headerName === 'Content-Encoding') {
return null;
} else if (headerName === 'Content-Length') {
return 'eight';
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})).toEqual({
it("rejects invalid Content-Length header values", function() {
expect(
validateRangeRequestCapabilities({
disableRange: false,
isHttp: true,
getResponseHeader: headerName => {
if (headerName === "Accept-Ranges") {
return "bytes";
} else if (headerName === "Content-Encoding") {
return null;
} else if (headerName === "Content-Length") {
return "eight";
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})
).toEqual({
allowRangeRequests: false,
suggestedLength: undefined,
});
});
it('rejects file sizes that are too small for range requests', function() {
expect(validateRangeRequestCapabilities({
disableRange: false,
isHttp: true,
getResponseHeader: (headerName) => {
if (headerName === 'Accept-Ranges') {
return 'bytes';
} else if (headerName === 'Content-Encoding') {
return null;
} else if (headerName === 'Content-Length') {
return 8;
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})).toEqual({
it("rejects file sizes that are too small for range requests", function() {
expect(
validateRangeRequestCapabilities({
disableRange: false,
isHttp: true,
getResponseHeader: headerName => {
if (headerName === "Accept-Ranges") {
return "bytes";
} else if (headerName === "Content-Encoding") {
return null;
} else if (headerName === "Content-Length") {
return 8;
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})
).toEqual({
allowRangeRequests: false,
suggestedLength: 8,
});
});
it('accepts file sizes large enough for range requests', function() {
expect(validateRangeRequestCapabilities({
disableRange: false,
isHttp: true,
getResponseHeader: (headerName) => {
if (headerName === 'Accept-Ranges') {
return 'bytes';
} else if (headerName === 'Content-Encoding') {
return null;
} else if (headerName === 'Content-Length') {
return 8192;
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})).toEqual({
it("accepts file sizes large enough for range requests", function() {
expect(
validateRangeRequestCapabilities({
disableRange: false,
isHttp: true,
getResponseHeader: headerName => {
if (headerName === "Accept-Ranges") {
return "bytes";
} else if (headerName === "Content-Encoding") {
return null;
} else if (headerName === "Content-Length") {
return 8192;
}
throw new Error(`Unexpected headerName: ${headerName}`);
},
rangeChunkSize: 64,
})
).toEqual({
allowRangeRequests: true,
suggestedLength: 8192,
});
});
});
describe('extractFilenameFromHeader', function() {
it('returns null when content disposition header is blank', function() {
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return null;
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toBeNull();
describe("extractFilenameFromHeader", function() {
it("returns null when content disposition header is blank", function() {
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return null;
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toBeNull();
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return undefined;
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toBeNull();
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return undefined;
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toBeNull();
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return '';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toBeNull();
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return "";
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toBeNull();
});
it('gets the filename from the response header', function() {
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'inline';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toBeNull();
it("gets the filename from the response header", function() {
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return "inline";
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toBeNull();
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toBeNull();
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return "attachment";
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toBeNull();
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename="filename.pdf"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('filename.pdf');
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return 'attachment; filename="filename.pdf"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("filename.pdf");
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename="filename.pdf and spaces.pdf"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('filename.pdf and spaces.pdf');
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return 'attachment; filename="filename.pdf and spaces.pdf"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("filename.pdf and spaces.pdf");
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename="tl;dr.pdf"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('tl;dr.pdf');
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return 'attachment; filename="tl;dr.pdf"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("tl;dr.pdf");
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename=filename.pdf';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('filename.pdf');
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return "attachment; filename=filename.pdf";
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("filename.pdf");
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename=filename.pdf someotherparam';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('filename.pdf');
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return "attachment; filename=filename.pdf someotherparam";
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("filename.pdf");
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename="%e4%b8%ad%e6%96%87.pdf"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('中文.pdf');
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return 'attachment; filename="%e4%b8%ad%e6%96%87.pdf"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("中文.pdf");
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename="100%.pdf"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('100%.pdf');
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return 'attachment; filename="100%.pdf"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("100%.pdf");
});
it('gets the filename from the response header (RFC 6266)', function() {
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename*=filename.pdf';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('filename.pdf');
it("gets the filename from the response header (RFC 6266)", function() {
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return "attachment; filename*=filename.pdf";
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("filename.pdf");
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename*=\'\'filename.pdf';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('filename.pdf');
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return "attachment; filename*=''filename.pdf";
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("filename.pdf");
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename*=utf-8\'\'filename.pdf';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('filename.pdf');
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return "attachment; filename*=utf-8''filename.pdf";
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("filename.pdf");
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename=no.pdf; filename*=utf-8\'\'filename.pdf';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('filename.pdf');
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return "attachment; filename=no.pdf; filename*=utf-8''filename.pdf";
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("filename.pdf");
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename*=utf-8\'\'filename.pdf; filename=no.pdf';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('filename.pdf');
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return "attachment; filename*=utf-8''filename.pdf; filename=no.pdf";
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("filename.pdf");
});
it('gets the filename from the response header (RFC 2231)', function() {
it("gets the filename from the response header (RFC 2231)", function() {
// Tests continuations (RFC 2231 section 3, via RFC 5987 section 3.1).
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename*0=filename; filename*1=.pdf';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('filename.pdf');
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return "attachment; filename*0=filename; filename*1=.pdf";
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("filename.pdf");
});
it('only extracts filename with pdf extension', function () {
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'attachment; filename="filename.png"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toBeNull();
it("only extracts filename with pdf extension", function() {
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return 'attachment; filename="filename.png"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toBeNull();
});
it('extension validation is case insensitive', function () {
expect(extractFilenameFromHeader((headerName) => {
if (headerName === 'Content-Disposition') {
return 'form-data; name="fieldName"; filename="file.PdF"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})).toEqual('file.PdF');
it("extension validation is case insensitive", function() {
expect(
extractFilenameFromHeader(headerName => {
if (headerName === "Content-Disposition") {
return 'form-data; name="fieldName"; filename="file.PdF"';
}
throw new Error(`Unexpected headerName: ${headerName}`);
})
).toEqual("file.PdF");
});
});
describe('createResponseStatusError', function() {
it('handles missing PDF file responses', function() {
expect(createResponseStatusError(404, 'https://foo.com/bar.pdf')).toEqual(
describe("createResponseStatusError", function() {
it("handles missing PDF file responses", function() {
expect(createResponseStatusError(404, "https://foo.com/bar.pdf")).toEqual(
new MissingPDFException('Missing PDF "https://foo.com/bar.pdf".')
);
expect(createResponseStatusError(0, 'file://foo.pdf')).toEqual(
expect(createResponseStatusError(0, "file://foo.pdf")).toEqual(
new MissingPDFException('Missing PDF "file://foo.pdf".')
);
});
it('handles unexpected responses', function() {
expect(createResponseStatusError(302, 'https://foo.com/bar.pdf')).toEqual(
it("handles unexpected responses", function() {
expect(createResponseStatusError(302, "https://foo.com/bar.pdf")).toEqual(
new UnexpectedResponseException(
'Unexpected server response (302) while retrieving PDF ' +
'"https://foo.com/bar.pdf".'
"Unexpected server response (302) while retrieving PDF " +
'"https://foo.com/bar.pdf".'
)
);
expect(createResponseStatusError(0, 'https://foo.com/bar.pdf')).toEqual(
expect(createResponseStatusError(0, "https://foo.com/bar.pdf")).toEqual(
new UnexpectedResponseException(
'Unexpected server response (0) while retrieving PDF ' +
'"https://foo.com/bar.pdf".'
"Unexpected server response (0) while retrieving PDF " +
'"https://foo.com/bar.pdf".'
)
);
});
});
describe('validateResponseStatus', function() {
it('accepts valid response statuses', function() {
describe("validateResponseStatus", function() {
it("accepts valid response statuses", function() {
expect(validateResponseStatus(200)).toEqual(true);
expect(validateResponseStatus(206)).toEqual(true);
});
it('rejects invalid response statuses', function() {
it("rejects invalid response statuses", function() {
expect(validateResponseStatus(302)).toEqual(false);
expect(validateResponseStatus(404)).toEqual(false);
expect(validateResponseStatus(null)).toEqual(false);

View file

@ -14,68 +14,75 @@
*/
/* globals __non_webpack_require__ */
import { AbortException, assert } from '../../src/shared/util';
import { isNodeJS } from '../../src/shared/is_node';
import { PDFNodeStream } from '../../src/display/node_stream';
import { AbortException, assert } from "../../src/shared/util";
import { isNodeJS } from "../../src/shared/is_node";
import { PDFNodeStream } from "../../src/display/node_stream";
// Make sure that we only running this script is Node.js environments.
assert(isNodeJS);
let path = __non_webpack_require__('path');
let url = __non_webpack_require__('url');
let http = __non_webpack_require__('http');
let fs = __non_webpack_require__('fs');
let path = __non_webpack_require__("path");
let url = __non_webpack_require__("url");
let http = __non_webpack_require__("http");
let fs = __non_webpack_require__("fs");
describe('node_stream', function() {
describe("node_stream", function() {
let server = null;
let port = null;
let pdf = url.parse(encodeURI('file://' + path.join(process.cwd(),
'./test/pdfs/tracemonkey.pdf'))).href;
let pdf = url.parse(
encodeURI(
"file://" + path.join(process.cwd(), "./test/pdfs/tracemonkey.pdf")
)
).href;
let pdfLength = 1016315;
beforeAll((done) => {
beforeAll(done => {
// Create http server to serve pdf data for tests.
server = http.createServer((request, response) => {
let filePath = process.cwd() + '/test/pdfs' + request.url;
fs.lstat(filePath, (error, stat) => {
if (error) {
response.writeHead(404);
response.end(`File ${request.url} not found!`);
return;
}
if (!request.headers['range']) {
let contentLength = stat.size;
let stream = fs.createReadStream(filePath);
response.writeHead(200, {
'Content-Type': 'application/pdf',
'Content-Length': contentLength,
'Accept-Ranges': 'bytes',
});
stream.pipe(response);
} else {
let [start, end] =
request.headers['range'].split('=')[1].split('-').map((x) => {
return Number(x);
server = http
.createServer((request, response) => {
let filePath = process.cwd() + "/test/pdfs" + request.url;
fs.lstat(filePath, (error, stat) => {
if (error) {
response.writeHead(404);
response.end(`File ${request.url} not found!`);
return;
}
if (!request.headers["range"]) {
let contentLength = stat.size;
let stream = fs.createReadStream(filePath);
response.writeHead(200, {
"Content-Type": "application/pdf",
"Content-Length": contentLength,
"Accept-Ranges": "bytes",
});
let stream = fs.createReadStream(filePath, { start, end, });
response.writeHead(206, {
'Content-Type': 'application/pdf',
});
stream.pipe(response);
}
});
}).listen(0); /* Listen on a random free port */
stream.pipe(response);
} else {
let [start, end] = request.headers["range"]
.split("=")[1]
.split("-")
.map(x => {
return Number(x);
});
let stream = fs.createReadStream(filePath, { start, end });
response.writeHead(206, {
"Content-Type": "application/pdf",
});
stream.pipe(response);
}
});
})
.listen(0); /* Listen on a random free port */
port = server.address().port;
done();
});
afterAll((done) => {
afterAll(done => {
// Close the server from accepting new connections after all test finishes.
server.close();
done();
});
it('read both http(s) and filesystem pdf files', function(done) {
it("read both http(s) and filesystem pdf files", function(done) {
let stream1 = new PDFNodeStream({
url: `http://127.0.0.1:${port}/tracemonkey.pdf`,
rangeChunkSize: 65536,
@ -105,9 +112,10 @@ describe('node_stream', function() {
isRangeSupported2 = fullReader2.isRangeSupported;
});
let len1 = 0, len2 = 0;
let read1 = function () {
return fullReader1.read().then(function (result) {
let len1 = 0,
len2 = 0;
let read1 = function() {
return fullReader1.read().then(function(result) {
if (result.done) {
return undefined;
}
@ -115,8 +123,8 @@ describe('node_stream', function() {
return read1();
});
};
let read2 = function () {
return fullReader2.read().then(function (result) {
let read2 = function() {
return fullReader2.read().then(function(result) {
if (result.done) {
return undefined;
}
@ -126,21 +134,22 @@ describe('node_stream', function() {
};
let readPromise = Promise.all([read1(), read2(), promise1, promise2]);
readPromise.then((result) => {
expect(isStreamingSupported1).toEqual(false);
expect(isRangeSupported1).toEqual(false);
expect(isStreamingSupported2).toEqual(false);
expect(isRangeSupported2).toEqual(false);
expect(len1).toEqual(pdfLength);
expect(len1).toEqual(len2);
done();
}).catch((reason) => {
done.fail(reason);
});
readPromise
.then(result => {
expect(isStreamingSupported1).toEqual(false);
expect(isRangeSupported1).toEqual(false);
expect(isStreamingSupported2).toEqual(false);
expect(isRangeSupported2).toEqual(false);
expect(len1).toEqual(pdfLength);
expect(len1).toEqual(len2);
done();
})
.catch(reason => {
done.fail(reason);
});
});
it('read custom ranges for both http(s) and filesystem urls',
function(done) {
it("read custom ranges for both http(s) and filesystem urls", function(done) {
let rangeSize = 32768;
let stream1 = new PDFNodeStream({
url: `http://127.0.0.1:${port}/tracemonkey.pdf`,
@ -163,37 +172,43 @@ describe('node_stream', function() {
let isStreamingSupported1, isRangeSupported1, fullReaderCancelled1;
let isStreamingSupported2, isRangeSupported2, fullReaderCancelled2;
let promise1 = fullReader1.headersReady.then(function () {
let promise1 = fullReader1.headersReady.then(function() {
isStreamingSupported1 = fullReader1.isStreamingSupported;
isRangeSupported1 = fullReader1.isRangeSupported;
// we shall be able to close the full reader without issues
fullReader1.cancel(new AbortException('Don\'t need fullReader1.'));
fullReader1.cancel(new AbortException("Don't need fullReader1."));
fullReaderCancelled1 = true;
});
let promise2 = fullReader2.headersReady.then(function () {
let promise2 = fullReader2.headersReady.then(function() {
isStreamingSupported2 = fullReader2.isStreamingSupported;
isRangeSupported2 = fullReader2.isRangeSupported;
fullReader2.cancel(new AbortException('Don\'t need fullReader2.'));
fullReader2.cancel(new AbortException("Don't need fullReader2."));
fullReaderCancelled2 = true;
});
// Skipping fullReader results, requesting something from the PDF end.
let tailSize = (pdfLength % rangeSize) || rangeSize;
let tailSize = pdfLength % rangeSize || rangeSize;
let range11Reader = stream1.getRangeReader(pdfLength - tailSize - rangeSize,
pdfLength - tailSize);
let range11Reader = stream1.getRangeReader(
pdfLength - tailSize - rangeSize,
pdfLength - tailSize
);
let range12Reader = stream1.getRangeReader(pdfLength - tailSize, pdfLength);
let range21Reader = stream2.getRangeReader(pdfLength - tailSize - rangeSize,
pdfLength - tailSize);
let range21Reader = stream2.getRangeReader(
pdfLength - tailSize - rangeSize,
pdfLength - tailSize
);
let range22Reader = stream2.getRangeReader(pdfLength - tailSize, pdfLength);
let result11 = { value: 0, }, result12 = { value: 0, };
let result21 = { value: 0, }, result22 = { value: 0, };
let result11 = { value: 0 },
result12 = { value: 0 };
let result21 = { value: 0 },
result22 = { value: 0 };
let read = function (reader, lenResult) {
return reader.read().then(function (result) {
let read = function(reader, lenResult) {
return reader.read().then(function(result) {
if (result.done) {
return undefined;
}
@ -202,26 +217,31 @@ describe('node_stream', function() {
});
};
let readPromises = Promise.all([read(range11Reader, result11),
read(range12Reader, result12),
read(range21Reader, result21),
read(range22Reader, result22),
promise1, promise2]);
let readPromises = Promise.all([
read(range11Reader, result11),
read(range12Reader, result12),
read(range21Reader, result21),
read(range22Reader, result22),
promise1,
promise2,
]);
readPromises.then(function () {
expect(result11.value).toEqual(rangeSize);
expect(result12.value).toEqual(tailSize);
expect(result21.value).toEqual(rangeSize);
expect(result22.value).toEqual(tailSize);
expect(isStreamingSupported1).toEqual(false);
expect(isRangeSupported1).toEqual(true);
expect(fullReaderCancelled1).toEqual(true);
expect(isStreamingSupported2).toEqual(false);
expect(isRangeSupported2).toEqual(true);
expect(fullReaderCancelled2).toEqual(true);
done();
}).catch(function (reason) {
done.fail(reason);
});
readPromises
.then(function() {
expect(result11.value).toEqual(rangeSize);
expect(result12.value).toEqual(tailSize);
expect(result21.value).toEqual(rangeSize);
expect(result22.value).toEqual(tailSize);
expect(isStreamingSupported1).toEqual(false);
expect(isRangeSupported1).toEqual(true);
expect(fullReaderCancelled1).toEqual(true);
expect(isStreamingSupported2).toEqual(false);
expect(isRangeSupported2).toEqual(true);
expect(fullReaderCancelled2).toEqual(true);
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
});

View file

@ -14,17 +14,18 @@
*/
/* eslint no-var: error */
import { Lexer, Linearization, Parser } from '../../src/core/parser';
import { FormatError } from '../../src/shared/util';
import { Name } from '../../src/core/primitives';
import { StringStream } from '../../src/core/stream';
import { Lexer, Linearization, Parser } from "../../src/core/parser";
import { FormatError } from "../../src/shared/util";
import { Name } from "../../src/core/primitives";
import { StringStream } from "../../src/core/stream";
describe('parser', function() {
describe('Parser', function() {
describe('inlineStreamSkipEI', function() {
it('should skip over the EI marker if it is found', function() {
const string = 'q 1 0 0 1 0 0 cm BI /W 10 /H 10 /BPC 1 ' +
'/F /A85 ID abc123~> EI Q';
describe("parser", function() {
describe("Parser", function() {
describe("inlineStreamSkipEI", function() {
it("should skip over the EI marker if it is found", function() {
const string =
"q 1 0 0 1 0 0 cm BI /W 10 /H 10 /BPC 1 " +
"/F /A85 ID abc123~> EI Q";
const input = new StringStream(string);
const parser = new Parser({
lexer: new Lexer(input),
@ -33,14 +34,13 @@ describe('parser', function() {
});
parser.inlineStreamSkipEI(input);
expect(input.pos).toEqual(string.indexOf('Q'));
expect(input.pos).toEqual(string.indexOf("Q"));
expect(input.peekByte()).toEqual(0x51); // 'Q'
});
it('should skip to the end of stream if the EI marker is not found',
function() {
const string = 'q 1 0 0 1 0 0 cm BI /W 10 /H 10 /BPC 1 ' +
'/F /A85 ID abc123~> Q';
it("should skip to the end of stream if the EI marker is not found", function() {
const string =
"q 1 0 0 1 0 0 cm BI /W 10 /H 10 /BPC 1 " + "/F /A85 ID abc123~> Q";
const input = new StringStream(string);
const parser = new Parser({
lexer: new Lexer(input),
@ -55,62 +55,73 @@ describe('parser', function() {
});
});
describe('Lexer', function() {
describe('nextChar', function() {
it('should return and set -1 when the end of the stream is reached',
function() {
const input = new StringStream('');
describe("Lexer", function() {
describe("nextChar", function() {
it("should return and set -1 when the end of the stream is reached", function() {
const input = new StringStream("");
const lexer = new Lexer(input);
expect(lexer.nextChar()).toEqual(-1);
expect(lexer.currentChar).toEqual(-1);
});
it('should return and set the character after the current position',
function() {
const input = new StringStream('123');
it("should return and set the character after the current position", function() {
const input = new StringStream("123");
const lexer = new Lexer(input);
expect(lexer.nextChar()).toEqual(0x32); // '2'
expect(lexer.currentChar).toEqual(0x32); // '2'
});
});
describe('peekChar', function() {
it('should only return -1 when the end of the stream is reached',
function() {
const input = new StringStream('');
describe("peekChar", function() {
it("should only return -1 when the end of the stream is reached", function() {
const input = new StringStream("");
const lexer = new Lexer(input);
expect(lexer.peekChar()).toEqual(-1);
expect(lexer.currentChar).toEqual(-1);
});
it('should only return the character after the current position',
function() {
const input = new StringStream('123');
it("should only return the character after the current position", function() {
const input = new StringStream("123");
const lexer = new Lexer(input);
expect(lexer.peekChar()).toEqual(0x32); // '2'
expect(lexer.currentChar).toEqual(0x31); // '1'
});
});
describe('getNumber', function() {
it('should stop parsing numbers at the end of stream', function() {
const input = new StringStream('11.234');
describe("getNumber", function() {
it("should stop parsing numbers at the end of stream", function() {
const input = new StringStream("11.234");
const lexer = new Lexer(input);
expect(lexer.getNumber()).toEqual(11.234);
});
it('should parse PostScript numbers', function() {
const numbers = ['-.002', '34.5', '-3.62', '123.6e10', '1E-5', '-1.',
'0.0', '123', '-98', '43445', '0', '+17'];
it("should parse PostScript numbers", function() {
const numbers = [
"-.002",
"34.5",
"-3.62",
"123.6e10",
"1E-5",
"-1.",
"0.0",
"123",
"-98",
"43445",
"0",
"+17",
];
for (const number of numbers) {
const input = new StringStream(number);
const lexer = new Lexer(input);
const result = lexer.getNumber(), expected = parseFloat(number);
const result = lexer.getNumber(),
expected = parseFloat(number);
if (result !== expected && Math.abs(result - expected) < 1e-15) {
console.error(`Fuzzy matching "${result}" with "${expected}" to ` +
'work-around rounding bugs in Chromium browsers.');
console.error(
`Fuzzy matching "${result}" with "${expected}" to ` +
"work-around rounding bugs in Chromium browsers."
);
expect(true).toEqual(true);
continue;
@ -119,35 +130,34 @@ describe('parser', function() {
}
});
it('should ignore double negative before number', function() {
const input = new StringStream('--205.88');
it("should ignore double negative before number", function() {
const input = new StringStream("--205.88");
const lexer = new Lexer(input);
expect(lexer.getNumber()).toEqual(-205.88);
});
it('should ignore minus signs in the middle of number', function() {
const input = new StringStream('205--.88');
it("should ignore minus signs in the middle of number", function() {
const input = new StringStream("205--.88");
const lexer = new Lexer(input);
expect(lexer.getNumber()).toEqual(205.88);
});
it('should ignore line-breaks between operator and digit in number',
function() {
const minusInput = new StringStream('-\r\n205.88');
it("should ignore line-breaks between operator and digit in number", function() {
const minusInput = new StringStream("-\r\n205.88");
const minusLexer = new Lexer(minusInput);
expect(minusLexer.getNumber()).toEqual(-205.88);
const plusInput = new StringStream('+\r\n205.88');
const plusInput = new StringStream("+\r\n205.88");
const plusLexer = new Lexer(plusInput);
expect(plusLexer.getNumber()).toEqual(205.88);
});
it('should treat a single decimal point as zero', function() {
const input = new StringStream('.');
it("should treat a single decimal point as zero", function() {
const input = new StringStream(".");
const lexer = new Lexer(input);
expect(lexer.getNumber()).toEqual(0);
const numbers = ['..', '-.', '+.', '-\r\n.', '+\r\n.'];
const numbers = ["..", "-.", "+.", "-\r\n.", "+\r\n."];
for (const number of numbers) {
const input = new StringStream(number);
const lexer = new Lexer(input);
@ -158,8 +168,8 @@ describe('parser', function() {
}
});
it('should handle glued numbers and operators', function() {
const input = new StringStream('123ET');
it("should handle glued numbers and operators", function() {
const input = new StringStream("123ET");
const lexer = new Lexer(input);
expect(lexer.getNumber()).toEqual(123);
// The lexer must not have consumed the 'E'
@ -167,40 +177,39 @@ describe('parser', function() {
});
});
describe('getString', function() {
it('should stop parsing strings at the end of stream', function() {
const input = new StringStream('(1$4)');
describe("getString", function() {
it("should stop parsing strings at the end of stream", function() {
const input = new StringStream("(1$4)");
input.getByte = function(super_getByte) {
// Simulating end of file using null (see issue 2766).
const ch = super_getByte.call(input);
return (ch === 0x24 /* '$' */ ? -1 : ch);
return ch === 0x24 /* '$' */ ? -1 : ch;
}.bind(input, input.getByte);
const lexer = new Lexer(input);
expect(lexer.getString()).toEqual('1');
expect(lexer.getString()).toEqual("1");
});
it('should ignore escaped CR and LF', function() {
it("should ignore escaped CR and LF", function() {
// '(\101\<CR><LF>\102)' should be parsed as 'AB'.
const input = new StringStream('(\\101\\\r\n\\102\\\r\\103\\\n\\104)');
const input = new StringStream("(\\101\\\r\n\\102\\\r\\103\\\n\\104)");
const lexer = new Lexer(input);
expect(lexer.getString()).toEqual('ABCD');
expect(lexer.getString()).toEqual("ABCD");
});
});
describe('getHexString', function() {
it('should not throw exception on bad input', function() {
describe("getHexString", function() {
it("should not throw exception on bad input", function() {
// '7 0 2 15 5 2 2 2 4 3 2 4' should be parsed as '70 21 55 22 24 32'.
const input = new StringStream('<7 0 2 15 5 2 2 2 4 3 2 4>');
const input = new StringStream("<7 0 2 15 5 2 2 2 4 3 2 4>");
const lexer = new Lexer(input);
expect(lexer.getHexString()).toEqual('p!U"$2');
});
});
describe('getName', function() {
it('should handle Names with invalid usage of NUMBER SIGN (#)',
function() {
const inputNames = ['/# 680 0 R', '/#AQwerty', '/#A<</B'];
const expectedNames = ['#', '#AQwerty', '#A'];
describe("getName", function() {
it("should handle Names with invalid usage of NUMBER SIGN (#)", function() {
const inputNames = ["/# 680 0 R", "/#AQwerty", "/#A<</B"];
const expectedNames = ["#", "#AQwerty", "#A"];
for (let i = 0, ii = inputNames.length; i < ii; i++) {
const input = new StringStream(inputNames[i]);
@ -211,46 +220,46 @@ describe('parser', function() {
});
});
describe('Linearization', function() {
it('should not find a linearization dictionary', function() {
describe("Linearization", function() {
it("should not find a linearization dictionary", function() {
// Not an actual linearization dictionary.
// prettier-ignore
const stream1 = new StringStream(
'3 0 obj\n' +
'<<\n' +
'/Length 4622\n' +
'/Filter /FlateDecode\n' +
'>>\n' +
'endobj'
"3 0 obj\n" +
"<<\n" +
"/Length 4622\n" +
"/Filter /FlateDecode\n" +
">>\n" +
"endobj"
);
expect(Linearization.create(stream1)).toEqual(null);
// Linearization dictionary with invalid version number.
// prettier-ignore
const stream2 = new StringStream(
'1 0 obj\n' +
'<<\n' +
'/Linearized 0\n' +
'>>\n' +
'endobj'
"1 0 obj\n" +
"<<\n" +
"/Linearized 0\n" +
">>\n" +
"endobj"
);
expect(Linearization.create(stream2)).toEqual(null);
});
it('should accept a valid linearization dictionary', function() {
it("should accept a valid linearization dictionary", function() {
// prettier-ignore
const stream = new StringStream(
'131 0 obj\n' +
'<<\n' +
'/Linearized 1\n' +
'/O 133\n' +
'/H [ 1388 863 ]\n' +
'/L 90\n' +
'/E 43573\n' +
'/N 18\n' +
'/T 193883\n' +
'>>\n' +
'endobj'
"131 0 obj\n" +
"<<\n" +
"/Linearized 1\n" +
"/O 133\n" +
"/H [ 1388 863 ]\n" +
"/L 90\n" +
"/E 43573\n" +
"/N 18\n" +
"/T 193883\n" +
">>\n" +
"endobj"
);
const expectedLinearizationDict = {
length: 90,
@ -264,130 +273,145 @@ describe('parser', function() {
expect(Linearization.create(stream)).toEqual(expectedLinearizationDict);
});
it('should reject a linearization dictionary with invalid ' +
'integer parameters', function() {
// The /L parameter should be equal to the stream length.
// prettier-ignore
const stream1 = new StringStream(
'1 0 obj\n' +
'<<\n' +
'/Linearized 1\n' +
'/O 133\n' +
'/H [ 1388 863 ]\n' +
'/L 196622\n' +
'/E 43573\n' +
'/N 18\n' +
'/T 193883\n' +
'>>\n' +
'endobj'
it(
"should reject a linearization dictionary with invalid " +
"integer parameters",
function() {
// The /L parameter should be equal to the stream length.
// prettier-ignore
const stream1 = new StringStream(
"1 0 obj\n" +
"<<\n" +
"/Linearized 1\n" +
"/O 133\n" +
"/H [ 1388 863 ]\n" +
"/L 196622\n" +
"/E 43573\n" +
"/N 18\n" +
"/T 193883\n" +
">>\n" +
"endobj"
);
expect(function() {
return Linearization.create(stream1);
}).toThrow(new Error('The "L" parameter in the linearization ' +
'dictionary does not equal the stream length.'));
expect(function() {
return Linearization.create(stream1);
}).toThrow(
new Error(
'The "L" parameter in the linearization ' +
"dictionary does not equal the stream length."
)
);
// The /E parameter should not be zero.
// prettier-ignore
const stream2 = new StringStream(
'1 0 obj\n' +
'<<\n' +
'/Linearized 1\n' +
'/O 133\n' +
'/H [ 1388 863 ]\n' +
'/L 84\n' +
'/E 0\n' +
'/N 18\n' +
'/T 193883\n' +
'>>\n' +
'endobj'
// The /E parameter should not be zero.
// prettier-ignore
const stream2 = new StringStream(
"1 0 obj\n" +
"<<\n" +
"/Linearized 1\n" +
"/O 133\n" +
"/H [ 1388 863 ]\n" +
"/L 84\n" +
"/E 0\n" +
"/N 18\n" +
"/T 193883\n" +
">>\n" +
"endobj"
);
expect(function() {
return Linearization.create(stream2);
}).toThrow(new Error('The "E" parameter in the linearization ' +
'dictionary is invalid.'));
expect(function() {
return Linearization.create(stream2);
}).toThrow(
new Error(
'The "E" parameter in the linearization ' + "dictionary is invalid."
)
);
// The /O parameter should be an integer.
// prettier-ignore
const stream3 = new StringStream(
'1 0 obj\n' +
'<<\n' +
'/Linearized 1\n' +
'/O /abc\n' +
'/H [ 1388 863 ]\n' +
'/L 89\n' +
'/E 43573\n' +
'/N 18\n' +
'/T 193883\n' +
'>>\n' +
'endobj'
// The /O parameter should be an integer.
// prettier-ignore
const stream3 = new StringStream(
"1 0 obj\n" +
"<<\n" +
"/Linearized 1\n" +
"/O /abc\n" +
"/H [ 1388 863 ]\n" +
"/L 89\n" +
"/E 43573\n" +
"/N 18\n" +
"/T 193883\n" +
">>\n" +
"endobj"
);
expect(function() {
return Linearization.create(stream3);
}).toThrow(new Error('The "O" parameter in the linearization ' +
'dictionary is invalid.'));
});
expect(function() {
return Linearization.create(stream3);
}).toThrow(
new Error(
'The "O" parameter in the linearization ' + "dictionary is invalid."
)
);
}
);
it('should reject a linearization dictionary with invalid hint parameters',
function() {
it("should reject a linearization dictionary with invalid hint parameters", function() {
// The /H parameter should be an array.
// prettier-ignore
const stream1 = new StringStream(
'1 0 obj\n' +
'<<\n' +
'/Linearized 1\n' +
'/O 133\n' +
'/H 1388\n' +
'/L 80\n' +
'/E 43573\n' +
'/N 18\n' +
'/T 193883\n' +
'>>\n' +
'endobj'
"1 0 obj\n" +
"<<\n" +
"/Linearized 1\n" +
"/O 133\n" +
"/H 1388\n" +
"/L 80\n" +
"/E 43573\n" +
"/N 18\n" +
"/T 193883\n" +
">>\n" +
"endobj"
);
expect(function() {
return Linearization.create(stream1);
}).toThrow(new Error('Hint array in the linearization dictionary ' +
'is invalid.'));
}).toThrow(
new Error("Hint array in the linearization dictionary " + "is invalid.")
);
// The hint array should contain two, or four, elements.
// prettier-ignore
const stream2 = new StringStream(
'1 0 obj\n' +
'<<\n' +
'/Linearized 1\n' +
'/O 133\n' +
'/H [ 1388 ]\n' +
'/L 84\n' +
'/E 43573\n' +
'/N 18\n' +
'/T 193883\n' +
'>>\n' +
'endobj'
"1 0 obj\n" +
"<<\n" +
"/Linearized 1\n" +
"/O 133\n" +
"/H [ 1388 ]\n" +
"/L 84\n" +
"/E 43573\n" +
"/N 18\n" +
"/T 193883\n" +
">>\n" +
"endobj"
);
expect(function() {
return Linearization.create(stream2);
}).toThrow(new Error('Hint array in the linearization dictionary ' +
'is invalid.'));
}).toThrow(
new Error("Hint array in the linearization dictionary " + "is invalid.")
);
// The hint array should not contain zero.
// prettier-ignore
const stream3 = new StringStream(
'1 0 obj\n' +
'<<\n' +
'/Linearized 1\n' +
'/O 133\n' +
'/H [ 1388 863 0 234]\n' +
'/L 93\n' +
'/E 43573\n' +
'/N 18\n' +
'/T 193883\n' +
'>>\n' +
'endobj'
"1 0 obj\n" +
"<<\n" +
"/Linearized 1\n" +
"/O 133\n" +
"/H [ 1388 863 0 234]\n" +
"/L 93\n" +
"/E 43573\n" +
"/N 18\n" +
"/T 193883\n" +
">>\n" +
"endobj"
);
expect(function() {
return Linearization.create(stream3);
}).toThrow(new Error('Hint (2) in the linearization dictionary ' +
'is invalid.'));
}).toThrow(
new Error("Hint (2) in the linearization dictionary " + "is invalid.")
);
});
});
});

View file

@ -13,11 +13,11 @@
* limitations under the License.
*/
import { buildGetDocumentParams } from './test_utils';
import { EventBus } from '../../web/ui_utils';
import { getDocument } from '../../src/display/api';
import { PDFFindController } from '../../web/pdf_find_controller';
import { SimpleLinkService } from '../../web/pdf_link_service';
import { buildGetDocumentParams } from "./test_utils";
import { EventBus } from "../../web/ui_utils";
import { getDocument } from "../../src/display/api";
import { PDFFindController } from "../../web/pdf_find_controller";
import { SimpleLinkService } from "../../web/pdf_link_service";
class MockLinkService extends SimpleLinkService {
constructor() {
@ -44,12 +44,12 @@ class MockLinkService extends SimpleLinkService {
}
}
describe('pdf_find_controller', function() {
describe("pdf_find_controller", function() {
let eventBus;
let pdfFindController;
beforeEach(function(done) {
const loadingTask = getDocument(buildGetDocumentParams('tracemonkey.pdf'));
const loadingTask = getDocument(buildGetDocumentParams("tracemonkey.pdf"));
loadingTask.promise.then(function(pdfDocument) {
eventBus = new EventBus();
@ -71,9 +71,9 @@ describe('pdf_find_controller', function() {
pdfFindController = null;
});
function testSearch({ parameters, matchesPerPage, selectedMatch, }) {
function testSearch({ parameters, matchesPerPage, selectedMatch }) {
return new Promise(function(resolve) {
pdfFindController.executeCommand('find', parameters);
pdfFindController.executeCommand("find", parameters);
// The `updatefindmatchescount` event is only emitted if the page contains
// at least one match for the query, so the last non-zero item in the
@ -98,32 +98,36 @@ describe('pdf_find_controller', function() {
return a + b;
});
eventBus.on('updatefindmatchescount',
function onUpdateFindMatchesCount(evt) {
eventBus.on("updatefindmatchescount", function onUpdateFindMatchesCount(
evt
) {
if (pdfFindController.pageMatches.length !== totalPages) {
return;
}
eventBus.off('updatefindmatchescount', onUpdateFindMatchesCount);
eventBus.off("updatefindmatchescount", onUpdateFindMatchesCount);
expect(evt.matchesCount.total).toBe(totalMatches);
for (let i = 0; i < totalPages; i++) {
expect(pdfFindController.pageMatches[i].length)
.toEqual(matchesPerPage[i]);
expect(pdfFindController.pageMatches[i].length).toEqual(
matchesPerPage[i]
);
}
expect(pdfFindController.selected.pageIdx)
.toEqual(selectedMatch.pageIndex);
expect(pdfFindController.selected.matchIdx)
.toEqual(selectedMatch.matchIndex);
expect(pdfFindController.selected.pageIdx).toEqual(
selectedMatch.pageIndex
);
expect(pdfFindController.selected.matchIdx).toEqual(
selectedMatch.matchIndex
);
resolve();
});
});
}
it('performs a normal search', function(done) {
it("performs a normal search", function(done) {
testSearch({
parameters: {
query: 'Dynamic',
query: "Dynamic",
caseSensitive: false,
entireWord: false,
phraseSearch: true,
@ -137,13 +141,13 @@ describe('pdf_find_controller', function() {
}).then(done);
});
it('performs a normal search and finds the previous result', function(done) {
it("performs a normal search and finds the previous result", function(done) {
// Page 14 (with page index 13) contains five results. By default, the
// first result (match index 0) is selected, so the previous result
// should be the fifth result (match index 4).
testSearch({
parameters: {
query: 'conference',
query: "conference",
caseSensitive: false,
entireWord: false,
phraseSearch: true,
@ -157,10 +161,10 @@ describe('pdf_find_controller', function() {
}).then(done);
});
it('performs a case sensitive search', function(done) {
it("performs a case sensitive search", function(done) {
testSearch({
parameters: {
query: 'Dynamic',
query: "Dynamic",
caseSensitive: true,
entireWord: false,
phraseSearch: true,
@ -174,12 +178,12 @@ describe('pdf_find_controller', function() {
}).then(done);
});
it('performs an entire word search', function(done) {
it("performs an entire word search", function(done) {
// Page 13 contains both 'Government' and 'Governmental', so the latter
// should not be found with entire word search.
testSearch({
parameters: {
query: 'Government',
query: "Government",
caseSensitive: false,
entireWord: true,
phraseSearch: true,
@ -193,12 +197,12 @@ describe('pdf_find_controller', function() {
}).then(done);
});
it('performs a multiple term (no phrase) search', function(done) {
it("performs a multiple term (no phrase) search", function(done) {
// Page 9 contains 'alternate' and pages 6 and 9 contain 'solution'.
// Both should be found for multiple term (no phrase) search.
testSearch({
parameters: {
query: 'alternate solution',
query: "alternate solution",
caseSensitive: false,
entireWord: false,
phraseSearch: false,

View file

@ -13,36 +13,36 @@
* limitations under the License.
*/
import { CharacterType, getCharacterType } from '../../web/pdf_find_utils';
import { CharacterType, getCharacterType } from "../../web/pdf_find_utils";
describe('pdf_find_utils', function() {
describe('getCharacterType', function() {
it('gets expected character types', function() {
describe("pdf_find_utils", function() {
describe("getCharacterType", function() {
it("gets expected character types", function() {
const characters = {
'A': CharacterType.ALPHA_LETTER,
'a': CharacterType.ALPHA_LETTER,
'0': CharacterType.ALPHA_LETTER,
'5': CharacterType.ALPHA_LETTER,
'\xC4': CharacterType.ALPHA_LETTER, // 'Ä'
'\xE4': CharacterType.ALPHA_LETTER, // 'ä'
'_': CharacterType.ALPHA_LETTER,
' ': CharacterType.SPACE,
'\t': CharacterType.SPACE,
'\r': CharacterType.SPACE,
'\n': CharacterType.SPACE,
'\xA0': CharacterType.SPACE,
'-': CharacterType.PUNCT,
',': CharacterType.PUNCT,
'.': CharacterType.PUNCT,
';': CharacterType.PUNCT,
':': CharacterType.PUNCT,
'\u2122': CharacterType.ALPHA_LETTER, // trademark
'\u0E25': CharacterType.THAI_LETTER,
'\u4000': CharacterType.HAN_LETTER,
'\uF950': CharacterType.HAN_LETTER,
'\u30C0': CharacterType.KATAKANA_LETTER,
'\u3050': CharacterType.HIRAGANA_LETTER,
'\uFF80': CharacterType.HALFWIDTH_KATAKANA_LETTER,
A: CharacterType.ALPHA_LETTER,
a: CharacterType.ALPHA_LETTER,
"0": CharacterType.ALPHA_LETTER,
"5": CharacterType.ALPHA_LETTER,
Ä: CharacterType.ALPHA_LETTER, // 'Ä'
ä: CharacterType.ALPHA_LETTER, // 'ä'
_: CharacterType.ALPHA_LETTER,
" ": CharacterType.SPACE,
"\t": CharacterType.SPACE,
"\r": CharacterType.SPACE,
"\n": CharacterType.SPACE,
"\xA0": CharacterType.SPACE,
"-": CharacterType.PUNCT,
",": CharacterType.PUNCT,
".": CharacterType.PUNCT,
";": CharacterType.PUNCT,
":": CharacterType.PUNCT,
"\u2122": CharacterType.ALPHA_LETTER, // trademark
: CharacterType.THAI_LETTER,
䀀: CharacterType.HAN_LETTER,
: CharacterType.HAN_LETTER,
: CharacterType.KATAKANA_LETTER,
: CharacterType.HIRAGANA_LETTER,
: CharacterType.HALFWIDTH_KATAKANA_LETTER,
};
for (const character in characters) {

View file

@ -13,43 +13,51 @@
* limitations under the License.
*/
import { isDestArraysEqual, isDestHashesEqual } from '../../web/pdf_history';
import { isDestArraysEqual, isDestHashesEqual } from "../../web/pdf_history";
describe('pdf_history', function() {
describe('isDestHashesEqual', function() {
it('should reject non-equal destination hashes', function() {
expect(isDestHashesEqual(null, 'page.157')).toEqual(false);
expect(isDestHashesEqual('title.0', 'page.157')).toEqual(false);
expect(isDestHashesEqual('page=1&zoom=auto', 'page.157')).toEqual(false);
describe("pdf_history", function() {
describe("isDestHashesEqual", function() {
it("should reject non-equal destination hashes", function() {
expect(isDestHashesEqual(null, "page.157")).toEqual(false);
expect(isDestHashesEqual("title.0", "page.157")).toEqual(false);
expect(isDestHashesEqual("page=1&zoom=auto", "page.157")).toEqual(false);
expect(isDestHashesEqual('nameddest-page.157', 'page.157')).
toEqual(false);
expect(isDestHashesEqual('page.157', 'nameddest=page.157')).
toEqual(false);
expect(isDestHashesEqual("nameddest-page.157", "page.157")).toEqual(
false
);
expect(isDestHashesEqual("page.157", "nameddest=page.157")).toEqual(
false
);
let destArrayString = JSON.stringify(
[{ num: 3757, gen: 0, }, { name: 'XYZ', }, 92.918, 748.972, null]);
expect(isDestHashesEqual(destArrayString, 'page.157')).toEqual(false);
expect(isDestHashesEqual('page.157', destArrayString)).toEqual(false);
let destArrayString = JSON.stringify([
{ num: 3757, gen: 0 },
{ name: "XYZ" },
92.918,
748.972,
null,
]);
expect(isDestHashesEqual(destArrayString, "page.157")).toEqual(false);
expect(isDestHashesEqual("page.157", destArrayString)).toEqual(false);
});
it('should accept equal destination hashes', function() {
expect(isDestHashesEqual('page.157', 'page.157')).toEqual(true);
expect(isDestHashesEqual('nameddest=page.157', 'page.157')).toEqual(true);
it("should accept equal destination hashes", function() {
expect(isDestHashesEqual("page.157", "page.157")).toEqual(true);
expect(isDestHashesEqual("nameddest=page.157", "page.157")).toEqual(true);
expect(isDestHashesEqual('nameddest=page.157&zoom=100', 'page.157')).
toEqual(true);
expect(
isDestHashesEqual("nameddest=page.157&zoom=100", "page.157")
).toEqual(true);
});
});
describe('isDestArraysEqual', function() {
let firstDest = [{ num: 1, gen: 0, }, { name: 'XYZ', }, 0, 375, null];
let secondDest = [{ num: 5, gen: 0, }, { name: 'XYZ', }, 0, 375, null];
let thirdDest = [{ num: 1, gen: 0, }, { name: 'XYZ', }, 750, 0, null];
let fourthDest = [{ num: 1, gen: 0, }, { name: 'XYZ', }, 0, 375, 1.0];
let fifthDest = [{ gen: 0, num: 1, }, { name: 'XYZ', }, 0, 375, null];
describe("isDestArraysEqual", function() {
let firstDest = [{ num: 1, gen: 0 }, { name: "XYZ" }, 0, 375, null];
let secondDest = [{ num: 5, gen: 0 }, { name: "XYZ" }, 0, 375, null];
let thirdDest = [{ num: 1, gen: 0 }, { name: "XYZ" }, 750, 0, null];
let fourthDest = [{ num: 1, gen: 0 }, { name: "XYZ" }, 0, 375, 1.0];
let fifthDest = [{ gen: 0, num: 1 }, { name: "XYZ" }, 0, 375, null];
it('should reject non-equal destination arrays', function() {
it("should reject non-equal destination arrays", function() {
expect(isDestArraysEqual(firstDest, undefined)).toEqual(false);
expect(isDestArraysEqual(firstDest, [1, 2, 3, 4, 5])).toEqual(false);
@ -58,7 +66,7 @@ describe('pdf_history', function() {
expect(isDestArraysEqual(firstDest, fourthDest)).toEqual(false);
});
it('should accept equal destination arrays', function() {
it("should accept equal destination arrays", function() {
expect(isDestArraysEqual(firstDest, firstDest)).toEqual(true);
expect(isDestArraysEqual(firstDest, fifthDest)).toEqual(true);

View file

@ -14,23 +14,32 @@
*/
import {
Cmd, Dict, isCmd, isDict, isName, isRef, isRefsEqual, Name, Ref, RefSet
} from '../../src/core/primitives';
import { XRefMock } from './test_utils';
Cmd,
Dict,
isCmd,
isDict,
isName,
isRef,
isRefsEqual,
Name,
Ref,
RefSet,
} from "../../src/core/primitives";
import { XRefMock } from "./test_utils";
describe('primitives', function() {
describe('Name', function() {
it('should retain the given name', function() {
var givenName = 'Font';
describe("primitives", function() {
describe("Name", function() {
it("should retain the given name", function() {
var givenName = "Font";
var name = Name.get(givenName);
expect(name.name).toEqual(givenName);
});
it('should create only one object for a name and cache it', function () {
var firstFont = Name.get('Font');
var secondFont = Name.get('Font');
var firstSubtype = Name.get('Subtype');
var secondSubtype = Name.get('Subtype');
it("should create only one object for a name and cache it", function() {
var firstFont = Name.get("Font");
var secondFont = Name.get("Font");
var firstSubtype = Name.get("Subtype");
var secondSubtype = Name.get("Subtype");
expect(firstFont).toBe(secondFont);
expect(firstSubtype).toBe(secondSubtype);
@ -38,18 +47,18 @@ describe('primitives', function() {
});
});
describe('Cmd', function() {
it('should retain the given cmd name', function() {
var givenCmd = 'BT';
describe("Cmd", function() {
it("should retain the given cmd name", function() {
var givenCmd = "BT";
var cmd = Cmd.get(givenCmd);
expect(cmd.cmd).toEqual(givenCmd);
});
it('should create only one object for a command and cache it', function() {
var firstBT = Cmd.get('BT');
var secondBT = Cmd.get('BT');
var firstET = Cmd.get('ET');
var secondET = Cmd.get('ET');
it("should create only one object for a command and cache it", function() {
var firstBT = Cmd.get("BT");
var secondBT = Cmd.get("BT");
var firstET = Cmd.get("ET");
var secondET = Cmd.get("ET");
expect(firstBT).toBe(secondBT);
expect(firstET).toBe(secondET);
@ -57,176 +66,189 @@ describe('primitives', function() {
});
});
describe('Dict', function() {
describe("Dict", function() {
var checkInvalidHasValues = function(dict) {
expect(dict.has()).toBeFalsy();
expect(dict.has('Prev')).toBeFalsy();
expect(dict.has("Prev")).toBeFalsy();
};
var checkInvalidKeyValues = function(dict) {
expect(dict.get()).toBeUndefined();
expect(dict.get('Prev')).toBeUndefined();
expect(dict.get('Decode', 'D')).toBeUndefined();
expect(dict.get('FontFile', 'FontFile2', 'FontFile3')).toBeUndefined();
expect(dict.get("Prev")).toBeUndefined();
expect(dict.get("Decode", "D")).toBeUndefined();
expect(dict.get("FontFile", "FontFile2", "FontFile3")).toBeUndefined();
};
var emptyDict, dictWithSizeKey, dictWithManyKeys;
var storedSize = 42;
var testFontFile = 'file1';
var testFontFile2 = 'file2';
var testFontFile3 = 'file3';
var testFontFile = "file1";
var testFontFile2 = "file2";
var testFontFile3 = "file3";
beforeAll(function (done) {
beforeAll(function(done) {
emptyDict = new Dict();
dictWithSizeKey = new Dict();
dictWithSizeKey.set('Size', storedSize);
dictWithSizeKey.set("Size", storedSize);
dictWithManyKeys = new Dict();
dictWithManyKeys.set('FontFile', testFontFile);
dictWithManyKeys.set('FontFile2', testFontFile2);
dictWithManyKeys.set('FontFile3', testFontFile3);
dictWithManyKeys.set("FontFile", testFontFile);
dictWithManyKeys.set("FontFile2", testFontFile2);
dictWithManyKeys.set("FontFile3", testFontFile3);
done();
});
afterAll(function () {
afterAll(function() {
emptyDict = dictWithSizeKey = dictWithManyKeys = null;
});
it('should return invalid values for unknown keys', function() {
it("should return invalid values for unknown keys", function() {
checkInvalidHasValues(emptyDict);
checkInvalidKeyValues(emptyDict);
});
it('should return correct value for stored Size key', function() {
expect(dictWithSizeKey.has('Size')).toBeTruthy();
it("should return correct value for stored Size key", function() {
expect(dictWithSizeKey.has("Size")).toBeTruthy();
expect(dictWithSizeKey.get('Size')).toEqual(storedSize);
expect(dictWithSizeKey.get('Prev', 'Size')).toEqual(storedSize);
expect(dictWithSizeKey.get('Prev', 'Root', 'Size')).toEqual(storedSize);
expect(dictWithSizeKey.get("Size")).toEqual(storedSize);
expect(dictWithSizeKey.get("Prev", "Size")).toEqual(storedSize);
expect(dictWithSizeKey.get("Prev", "Root", "Size")).toEqual(storedSize);
});
it('should return invalid values for unknown keys when Size key is stored',
function() {
it("should return invalid values for unknown keys when Size key is stored", function() {
checkInvalidHasValues(dictWithSizeKey);
checkInvalidKeyValues(dictWithSizeKey);
});
it('should return correct value for stored Size key with undefined value',
function() {
it("should return correct value for stored Size key with undefined value", function() {
var dict = new Dict();
dict.set('Size');
dict.set("Size");
expect(dict.has('Size')).toBeTruthy();
expect(dict.has("Size")).toBeTruthy();
checkInvalidKeyValues(dict);
});
it('should return correct values for multiple stored keys', function() {
expect(dictWithManyKeys.has('FontFile')).toBeTruthy();
expect(dictWithManyKeys.has('FontFile2')).toBeTruthy();
expect(dictWithManyKeys.has('FontFile3')).toBeTruthy();
it("should return correct values for multiple stored keys", function() {
expect(dictWithManyKeys.has("FontFile")).toBeTruthy();
expect(dictWithManyKeys.has("FontFile2")).toBeTruthy();
expect(dictWithManyKeys.has("FontFile3")).toBeTruthy();
expect(dictWithManyKeys.get('FontFile3')).toEqual(testFontFile3);
expect(dictWithManyKeys.get('FontFile2', 'FontFile3'))
.toEqual(testFontFile2);
expect(dictWithManyKeys.get('FontFile', 'FontFile2', 'FontFile3'))
.toEqual(testFontFile);
expect(dictWithManyKeys.get("FontFile3")).toEqual(testFontFile3);
expect(dictWithManyKeys.get("FontFile2", "FontFile3")).toEqual(
testFontFile2
);
expect(
dictWithManyKeys.get("FontFile", "FontFile2", "FontFile3")
).toEqual(testFontFile);
});
it('should asynchronously fetch unknown keys', function (done) {
it("should asynchronously fetch unknown keys", function(done) {
var keyPromises = [
dictWithManyKeys.getAsync('Size'),
dictWithSizeKey.getAsync('FontFile', 'FontFile2', 'FontFile3')
dictWithManyKeys.getAsync("Size"),
dictWithSizeKey.getAsync("FontFile", "FontFile2", "FontFile3"),
];
Promise.all(keyPromises).then(function (values) {
expect(values[0]).toBeUndefined();
expect(values[1]).toBeUndefined();
done();
}).catch(function (reason) {
done.fail(reason);
});
Promise.all(keyPromises)
.then(function(values) {
expect(values[0]).toBeUndefined();
expect(values[1]).toBeUndefined();
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('should asynchronously fetch correct values for multiple stored keys',
function (done) {
it("should asynchronously fetch correct values for multiple stored keys", function(done) {
var keyPromises = [
dictWithManyKeys.getAsync('FontFile3'),
dictWithManyKeys.getAsync('FontFile2', 'FontFile3'),
dictWithManyKeys.getAsync('FontFile', 'FontFile2', 'FontFile3')
dictWithManyKeys.getAsync("FontFile3"),
dictWithManyKeys.getAsync("FontFile2", "FontFile3"),
dictWithManyKeys.getAsync("FontFile", "FontFile2", "FontFile3"),
];
Promise.all(keyPromises).then(function (values) {
expect(values[0]).toEqual(testFontFile3);
expect(values[1]).toEqual(testFontFile2);
expect(values[2]).toEqual(testFontFile);
done();
}).catch(function (reason) {
done.fail(reason);
});
Promise.all(keyPromises)
.then(function(values) {
expect(values[0]).toEqual(testFontFile3);
expect(values[1]).toEqual(testFontFile2);
expect(values[2]).toEqual(testFontFile);
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('should callback for each stored key', function() {
var callbackSpy = jasmine.createSpy('spy on callback in dictionary');
it("should callback for each stored key", function() {
var callbackSpy = jasmine.createSpy("spy on callback in dictionary");
dictWithManyKeys.forEach(callbackSpy);
expect(callbackSpy).toHaveBeenCalled();
var callbackSpyCalls = callbackSpy.calls;
expect(callbackSpyCalls.argsFor(0)).toEqual(['FontFile', testFontFile]);
expect(callbackSpyCalls.argsFor(1)).toEqual(['FontFile2', testFontFile2]);
expect(callbackSpyCalls.argsFor(2)).toEqual(['FontFile3', testFontFile3]);
expect(callbackSpyCalls.argsFor(0)).toEqual(["FontFile", testFontFile]);
expect(callbackSpyCalls.argsFor(1)).toEqual(["FontFile2", testFontFile2]);
expect(callbackSpyCalls.argsFor(2)).toEqual(["FontFile3", testFontFile3]);
expect(callbackSpyCalls.count()).toEqual(3);
});
it('should handle keys pointing to indirect objects, both sync and async',
function (done) {
it("should handle keys pointing to indirect objects, both sync and async", function(done) {
var fontRef = Ref.get(1, 0);
var xref = new XRefMock([
{ ref: fontRef, data: testFontFile, }
]);
var xref = new XRefMock([{ ref: fontRef, data: testFontFile }]);
var fontDict = new Dict(xref);
fontDict.set('FontFile', fontRef);
fontDict.set("FontFile", fontRef);
expect(fontDict.getRaw('FontFile')).toEqual(fontRef);
expect(fontDict.get('FontFile', 'FontFile2', 'FontFile3')).
toEqual(testFontFile);
expect(fontDict.getRaw("FontFile")).toEqual(fontRef);
expect(fontDict.get("FontFile", "FontFile2", "FontFile3")).toEqual(
testFontFile
);
fontDict.getAsync('FontFile', 'FontFile2', 'FontFile3').then(
function (value) {
expect(value).toEqual(testFontFile);
done();
}).catch(function (reason) {
done.fail(reason);
});
fontDict
.getAsync("FontFile", "FontFile2", "FontFile3")
.then(function(value) {
expect(value).toEqual(testFontFile);
done();
})
.catch(function(reason) {
done.fail(reason);
});
});
it('should handle arrays containing indirect objects', function () {
var minCoordRef = Ref.get(1, 0), maxCoordRef = Ref.get(2, 0);
var minCoord = 0, maxCoord = 1;
it("should handle arrays containing indirect objects", function() {
var minCoordRef = Ref.get(1, 0),
maxCoordRef = Ref.get(2, 0);
var minCoord = 0,
maxCoord = 1;
var xref = new XRefMock([
{ ref: minCoordRef, data: minCoord, },
{ ref: maxCoordRef, data: maxCoord, }
{ ref: minCoordRef, data: minCoord },
{ ref: maxCoordRef, data: maxCoord },
]);
var xObjectDict = new Dict(xref);
xObjectDict.set('BBox', [minCoord, maxCoord, minCoordRef, maxCoordRef]);
xObjectDict.set("BBox", [minCoord, maxCoord, minCoordRef, maxCoordRef]);
expect(xObjectDict.get('BBox')).toEqual(
[minCoord, maxCoord, minCoordRef, maxCoordRef]);
expect(xObjectDict.getArray('BBox')).toEqual(
[minCoord, maxCoord, minCoord, maxCoord]);
expect(xObjectDict.get("BBox")).toEqual([
minCoord,
maxCoord,
minCoordRef,
maxCoordRef,
]);
expect(xObjectDict.getArray("BBox")).toEqual([
minCoord,
maxCoord,
minCoord,
maxCoord,
]);
});
it('should get all key names', function () {
var expectedKeys = ['FontFile', 'FontFile2', 'FontFile3'];
it("should get all key names", function() {
var expectedKeys = ["FontFile", "FontFile2", "FontFile3"];
var keys = dictWithManyKeys.getKeys();
expect(keys.sort()).toEqual(expectedKeys);
});
it('should create only one object for Dict.empty', function () {
it("should create only one object for Dict.empty", function() {
var firstDictEmpty = Dict.empty;
var secondDictEmpty = Dict.empty;
@ -234,22 +256,25 @@ describe('primitives', function() {
expect(firstDictEmpty).not.toBe(emptyDict);
});
it('should correctly merge dictionaries', function () {
var expectedKeys = ['FontFile', 'FontFile2', 'FontFile3', 'Size'];
it("should correctly merge dictionaries", function() {
var expectedKeys = ["FontFile", "FontFile2", "FontFile3", "Size"];
var fontFileDict = new Dict();
fontFileDict.set('FontFile', 'Type1 font file');
var mergedDict = Dict.merge(null,
[dictWithManyKeys, dictWithSizeKey, fontFileDict]);
fontFileDict.set("FontFile", "Type1 font file");
var mergedDict = Dict.merge(null, [
dictWithManyKeys,
dictWithSizeKey,
fontFileDict,
]);
var mergedKeys = mergedDict.getKeys();
expect(mergedKeys.sort()).toEqual(expectedKeys);
expect(mergedDict.get('FontFile')).toEqual(testFontFile);
expect(mergedDict.get("FontFile")).toEqual(testFontFile);
});
});
describe('Ref', function() {
it('should retain the stored values', function() {
describe("Ref", function() {
it("should retain the stored values", function() {
var storedNum = 4;
var storedGen = 2;
var ref = Ref.get(storedNum, storedGen);
@ -258,14 +283,14 @@ describe('primitives', function() {
});
});
describe('RefSet', function() {
it('should have a stored value', function() {
describe("RefSet", function() {
it("should have a stored value", function() {
var ref = Ref.get(4, 2);
var refset = new RefSet();
refset.put(ref);
expect(refset.has(ref)).toBeTruthy();
});
it('should not have an unknown value', function() {
it("should not have an unknown value", function() {
var ref = Ref.get(4, 2);
var refset = new RefSet();
expect(refset.has(ref)).toBeFalsy();
@ -276,82 +301,82 @@ describe('primitives', function() {
});
});
describe('isName', function () {
it('handles non-names', function () {
describe("isName", function() {
it("handles non-names", function() {
var nonName = {};
expect(isName(nonName)).toEqual(false);
});
it('handles names', function () {
var name = Name.get('Font');
it("handles names", function() {
var name = Name.get("Font");
expect(isName(name)).toEqual(true);
});
it('handles names with name check', function () {
var name = Name.get('Font');
expect(isName(name, 'Font')).toEqual(true);
expect(isName(name, 'Subtype')).toEqual(false);
it("handles names with name check", function() {
var name = Name.get("Font");
expect(isName(name, "Font")).toEqual(true);
expect(isName(name, "Subtype")).toEqual(false);
});
});
describe('isCmd', function () {
it('handles non-commands', function () {
describe("isCmd", function() {
it("handles non-commands", function() {
var nonCmd = {};
expect(isCmd(nonCmd)).toEqual(false);
});
it('handles commands', function () {
var cmd = Cmd.get('BT');
it("handles commands", function() {
var cmd = Cmd.get("BT");
expect(isCmd(cmd)).toEqual(true);
});
it('handles commands with cmd check', function () {
var cmd = Cmd.get('BT');
expect(isCmd(cmd, 'BT')).toEqual(true);
expect(isCmd(cmd, 'ET')).toEqual(false);
it("handles commands with cmd check", function() {
var cmd = Cmd.get("BT");
expect(isCmd(cmd, "BT")).toEqual(true);
expect(isCmd(cmd, "ET")).toEqual(false);
});
});
describe('isDict', function() {
it('handles non-dictionaries', function () {
describe("isDict", function() {
it("handles non-dictionaries", function() {
var nonDict = {};
expect(isDict(nonDict)).toEqual(false);
});
it('handles empty dictionaries with type check', function() {
it("handles empty dictionaries with type check", function() {
var dict = Dict.empty;
expect(isDict(dict)).toEqual(true);
expect(isDict(dict, 'Page')).toEqual(false);
expect(isDict(dict, "Page")).toEqual(false);
});
it('handles dictionaries with type check', function() {
it("handles dictionaries with type check", function() {
var dict = new Dict();
dict.set('Type', Name.get('Page'));
expect(isDict(dict, 'Page')).toEqual(true);
expect(isDict(dict, 'Contents')).toEqual(false);
dict.set("Type", Name.get("Page"));
expect(isDict(dict, "Page")).toEqual(true);
expect(isDict(dict, "Contents")).toEqual(false);
});
});
describe('isRef', function () {
it('handles non-refs', function () {
describe("isRef", function() {
it("handles non-refs", function() {
var nonRef = {};
expect(isRef(nonRef)).toEqual(false);
});
it('handles refs', function () {
it("handles refs", function() {
var ref = Ref.get(1, 0);
expect(isRef(ref)).toEqual(true);
});
});
describe('isRefsEqual', function () {
it('should handle Refs pointing to the same object', function () {
describe("isRefsEqual", function() {
it("should handle Refs pointing to the same object", function() {
var ref1 = Ref.get(1, 0);
var ref2 = Ref.get(1, 0);
expect(isRefsEqual(ref1, ref2)).toEqual(true);
});
it('should handle Refs pointing to different objects', function () {
it("should handle Refs pointing to different objects", function() {
var ref1 = Ref.get(1, 0);
var ref2 = Ref.get(2, 0);
expect(isRefsEqual(ref1, ref2)).toEqual(false);

View file

@ -13,10 +13,10 @@
* limitations under the License.
*/
import { PredictorStream, Stream } from '../../src/core/stream';
import { Dict } from '../../src/core/primitives';
import { PredictorStream, Stream } from "../../src/core/stream";
import { Dict } from "../../src/core/primitives";
describe('stream', function() {
describe("stream", function() {
beforeEach(function() {
jasmine.addMatchers({
toMatchTypedArray(util, customEqualityTesters) {
@ -25,13 +25,17 @@ describe('stream', function() {
var result = {};
if (actual.length !== expected.length) {
result.pass = false;
result.message = 'Array length: ' + actual.length +
', expected: ' + expected.length;
result.message =
"Array length: " +
actual.length +
", expected: " +
expected.length;
return result;
}
result.pass = true;
for (var i = 0, ii = expected.length; i < ii; i++) {
var a = actual[i], b = expected[i];
var a = actual[i],
b = expected[i];
if (a !== b) {
result.pass = false;
break;
@ -43,16 +47,20 @@ describe('stream', function() {
},
});
});
describe('PredictorStream', function() {
it('should decode simple predictor data', function() {
describe("PredictorStream", function() {
it("should decode simple predictor data", function() {
var dict = new Dict();
dict.set('Predictor', 12);
dict.set('Colors', 1);
dict.set('BitsPerComponent', 8);
dict.set('Columns', 2);
dict.set("Predictor", 12);
dict.set("Colors", 1);
dict.set("BitsPerComponent", 8);
dict.set("Columns", 2);
var input = new Stream(new Uint8Array([2, 100, 3, 2, 1, 255, 2, 1, 255]),
0, 9, dict);
var input = new Stream(
new Uint8Array([2, 100, 3, 2, 1, 255, 2, 1, 255]),
0,
9,
dict
);
var predictor = new PredictorStream(input, /* length = */ 9, dict);
var result = predictor.getBytes(6);
@ -63,7 +71,8 @@ describe('stream', function() {
predictor.reset();
let clampedResult = predictor.getBytes(6, /* forceClamped = */ true);
expect(clampedResult).toEqual(
new Uint8ClampedArray([100, 3, 101, 2, 102, 1]));
new Uint8ClampedArray([100, 3, 101, 2, 102, 1])
);
});
});
});

View file

@ -13,10 +13,10 @@
* limitations under the License.
*/
import { assert, CMapCompressionType } from '../../src/shared/util';
import { isNodeJS } from '../../src/shared/is_node';
import { isRef } from '../../src/core/primitives';
import { Page } from '../../src/core/document';
import { assert, CMapCompressionType } from "../../src/shared/util";
import { isNodeJS } from "../../src/shared/is_node";
import { isRef } from "../../src/core/primitives";
import { Page } from "../../src/core/document";
class DOMFileReaderFactory {
static async fetch(params) {
@ -30,7 +30,7 @@ class DOMFileReaderFactory {
class NodeFileReaderFactory {
static async fetch(params) {
const fs = require('fs');
const fs = require("fs");
return new Promise((resolve, reject) => {
fs.readFile(params.path, (error, data) => {
@ -45,8 +45,8 @@ class NodeFileReaderFactory {
}
const TEST_PDFS_PATH = {
dom: '../pdfs/',
node: './test/pdfs/',
dom: "../pdfs/",
node: "./test/pdfs/",
};
function buildGetDocumentParams(filename, options) {
@ -64,26 +64,26 @@ function buildGetDocumentParams(filename, options) {
class NodeCanvasFactory {
create(width, height) {
assert(width > 0 && height > 0, 'Invalid canvas size');
assert(width > 0 && height > 0, "Invalid canvas size");
const Canvas = require('canvas');
const Canvas = require("canvas");
const canvas = Canvas.createCanvas(width, height);
return {
canvas,
context: canvas.getContext('2d'),
context: canvas.getContext("2d"),
};
}
reset(canvasAndContext, width, height) {
assert(canvasAndContext.canvas, 'Canvas is not specified');
assert(width > 0 && height > 0, 'Invalid canvas size');
assert(canvasAndContext.canvas, "Canvas is not specified");
assert(width > 0 && height > 0, "Invalid canvas size");
canvasAndContext.canvas.width = width;
canvasAndContext.canvas.height = height;
}
destroy(canvasAndContext) {
assert(canvasAndContext.canvas, 'Canvas is not specified');
assert(canvasAndContext.canvas, "Canvas is not specified");
// Zeroing the width and height cause Firefox to release graphics
// resources immediately, which can greatly reduce memory consumption.
@ -95,36 +95,40 @@ class NodeCanvasFactory {
}
class NodeCMapReaderFactory {
constructor({ baseUrl = null, isCompressed = false, }) {
constructor({ baseUrl = null, isCompressed = false }) {
this.baseUrl = baseUrl;
this.isCompressed = isCompressed;
}
async fetch({ name, }) {
async fetch({ name }) {
if (!this.baseUrl) {
throw new Error(
'The CMap "baseUrl" parameter must be specified, ensure that ' +
'the "cMapUrl" and "cMapPacked" API parameters are provided.');
'the "cMapUrl" and "cMapPacked" API parameters are provided.'
);
}
if (!name) {
throw new Error('CMap name must be specified.');
throw new Error("CMap name must be specified.");
}
const url = this.baseUrl + name + (this.isCompressed ? '.bcmap' : '');
const compressionType = (this.isCompressed ? CMapCompressionType.BINARY :
CMapCompressionType.NONE);
const url = this.baseUrl + name + (this.isCompressed ? ".bcmap" : "");
const compressionType = this.isCompressed
? CMapCompressionType.BINARY
: CMapCompressionType.NONE;
return new Promise((resolve, reject) => {
const fs = require('fs');
const fs = require("fs");
fs.readFile(url, (error, data) => {
if (error || !data) {
reject(new Error(error));
return;
}
resolve({ cMapData: new Uint8Array(data), compressionType, });
resolve({ cMapData: new Uint8Array(data), compressionType });
});
}).catch((reason) => {
throw new Error(`Unable to load ${this.isCompressed ? 'binary ' : ''}` +
`CMap at: ${url}`);
}).catch(reason => {
throw new Error(
`Unable to load ${this.isCompressed ? "binary " : ""}` +
`CMap at: ${url}`
);
});
}
}
@ -163,7 +167,7 @@ function createIdFactory(pageIndex) {
const page = new Page({
pdfManager: {
get docId() {
return 'd0';
return "d0";
},
},
pageIndex,

View file

@ -1,12 +1,12 @@
'use strict';
"use strict";
// eslint-disable-next-line no-unused-vars
var TestReporter = function(browser, appPath) {
function send(action, json, cb) {
var r = new XMLHttpRequest();
// (The POST URI is ignored atm.)
r.open('POST', action, true);
r.setRequestHeader('Content-Type', 'application/json');
r.open("POST", action, true);
r.setRequestHeader("Content-Type", "application/json");
r.onreadystatechange = function sendTaskResultOnreadystatechange(e) {
if (r.readyState === 4) {
// Retry until successful
@ -19,12 +19,12 @@ var TestReporter = function(browser, appPath) {
}
}
};
json['browser'] = browser;
json["browser"] = browser;
r.send(JSON.stringify(json));
}
function sendInfo(message) {
send('/info', { message, });
send("/info", { message });
}
function sendResult(status, description, error) {
@ -32,14 +32,14 @@ var TestReporter = function(browser, appPath) {
status,
description,
};
if (typeof error !== 'undefined') {
message['error'] = error;
if (typeof error !== "undefined") {
message["error"] = error;
}
send('/submit_task_results', message);
send("/submit_task_results", message);
}
function sendQuitRequest() {
send('/tellMeToQuit?path=' + escape(appPath), {});
send("/tellMeToQuit?path=" + escape(appPath), {});
}
this.now = function() {
@ -48,27 +48,27 @@ var TestReporter = function(browser, appPath) {
this.jasmineStarted = function(suiteInfo) {
this.runnerStartTime = this.now();
sendInfo('Started tests for ' + browser + '.');
sendInfo("Started tests for " + browser + ".");
};
this.suiteStarted = function(result) { };
this.suiteStarted = function(result) {};
this.specStarted = function(result) { };
this.specStarted = function(result) {};
this.specDone = function(result) {
if (result.failedExpectations.length === 0) {
sendResult('TEST-PASSED', result.description);
sendResult("TEST-PASSED", result.description);
} else {
var failedMessages = '';
var failedMessages = "";
var items = result.failedExpectations;
for (var i = 0, ii = items.length; i < ii; i++) {
failedMessages += items[i].message + ' ';
failedMessages += items[i].message + " ";
}
sendResult('TEST-UNEXPECTED-FAIL', result.description, failedMessages);
sendResult("TEST-UNEXPECTED-FAIL", result.description, failedMessages);
}
};
this.suiteDone = function(result) { };
this.suiteDone = function(result) {};
this.jasmineDone = function() {
// Give the test.py some time process any queued up requests

View file

@ -13,106 +13,110 @@
* limitations under the License.
*/
import { SEAC_ANALYSIS_ENABLED } from '../../src/core/fonts';
import { StringStream } from '../../src/core/stream';
import { Type1Parser } from '../../src/core/type1_parser';
import { SEAC_ANALYSIS_ENABLED } from "../../src/core/fonts";
import { StringStream } from "../../src/core/stream";
import { Type1Parser } from "../../src/core/type1_parser";
describe('Type1Parser', function() {
it('splits tokens', function() {
var stream = new StringStream('/BlueValues[-17 0]noaccess def');
describe("Type1Parser", function() {
it("splits tokens", function() {
var stream = new StringStream("/BlueValues[-17 0]noaccess def");
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.getToken()).toEqual('/');
expect(parser.getToken()).toEqual('BlueValues');
expect(parser.getToken()).toEqual('[');
expect(parser.getToken()).toEqual('-17');
expect(parser.getToken()).toEqual('0');
expect(parser.getToken()).toEqual(']');
expect(parser.getToken()).toEqual('noaccess');
expect(parser.getToken()).toEqual('def');
expect(parser.getToken()).toEqual("/");
expect(parser.getToken()).toEqual("BlueValues");
expect(parser.getToken()).toEqual("[");
expect(parser.getToken()).toEqual("-17");
expect(parser.getToken()).toEqual("0");
expect(parser.getToken()).toEqual("]");
expect(parser.getToken()).toEqual("noaccess");
expect(parser.getToken()).toEqual("def");
expect(parser.getToken()).toEqual(null);
});
it('handles glued tokens', function() {
var stream = new StringStream('dup/CharStrings');
it("handles glued tokens", function() {
var stream = new StringStream("dup/CharStrings");
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.getToken()).toEqual('dup');
expect(parser.getToken()).toEqual('/');
expect(parser.getToken()).toEqual('CharStrings');
expect(parser.getToken()).toEqual("dup");
expect(parser.getToken()).toEqual("/");
expect(parser.getToken()).toEqual("CharStrings");
});
it('ignores whitespace', function() {
var stream = new StringStream('\nab c\t');
it("ignores whitespace", function() {
var stream = new StringStream("\nab c\t");
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.getToken()).toEqual('ab');
expect(parser.getToken()).toEqual('c');
expect(parser.getToken()).toEqual("ab");
expect(parser.getToken()).toEqual("c");
});
it('parses numbers', function() {
var stream = new StringStream('123');
it("parses numbers", function() {
var stream = new StringStream("123");
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.readNumber()).toEqual(123);
});
it('parses booleans', function() {
var stream = new StringStream('true false');
it("parses booleans", function() {
var stream = new StringStream("true false");
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.readBoolean()).toEqual(1);
expect(parser.readBoolean()).toEqual(0);
});
it('parses number arrays', function() {
var stream = new StringStream('[1 2]');
it("parses number arrays", function() {
var stream = new StringStream("[1 2]");
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.readNumberArray()).toEqual([1, 2]);
// Variation on spacing.
stream = new StringStream('[ 1 2 ]');
stream = new StringStream("[ 1 2 ]");
parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.readNumberArray()).toEqual([1, 2]);
});
it('skips comments', function() {
it("skips comments", function() {
var stream = new StringStream(
'%!PS-AdobeFont-1.0: CMSY10 003.002\n' +
'%%Title: CMSY10\n' +
'%Version: 003.002\n' +
'FontDirectory');
"%!PS-AdobeFont-1.0: CMSY10 003.002\n" +
"%%Title: CMSY10\n" +
"%Version: 003.002\n" +
"FontDirectory"
);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.getToken()).toEqual('FontDirectory');
expect(parser.getToken()).toEqual("FontDirectory");
});
it('parses font program', function() {
it("parses font program", function() {
var stream = new StringStream(
'/ExpansionFactor 99\n' +
'/Subrs 1 array\n' +
'dup 0 1 RD x noaccess put\n' +
'end\n' +
'/CharStrings 46 dict dup begin\n' +
'/.notdef 1 RD x ND\n' +
'end');
"/ExpansionFactor 99\n" +
"/Subrs 1 array\n" +
"dup 0 1 RD x noaccess put\n" +
"end\n" +
"/CharStrings 46 dict dup begin\n" +
"/.notdef 1 RD x ND\n" +
"end"
);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
var program = parser.extractFontProgram({});
expect(program.charstrings.length).toEqual(1);
expect(program.properties.privateData.ExpansionFactor).toEqual(99);
});
it('parses font header font matrix', function() {
it("parses font header font matrix", function() {
var stream = new StringStream(
'/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def\n');
"/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def\n"
);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
var props = {};
parser.extractFontHeader(props);
expect(props.fontMatrix).toEqual([0.001, 0, 0, 0.001, 0, 0]);
});
it('parses font header encoding', function() {
it("parses font header encoding", function() {
var stream = new StringStream(
'/Encoding 256 array\n' +
'0 1 255 {1 index exch /.notdef put} for\n' +
'dup 33 /arrowright put\n' +
'readonly def\n');
"/Encoding 256 array\n" +
"0 1 255 {1 index exch /.notdef put} for\n" +
"dup 33 /arrowright put\n" +
"readonly def\n"
);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
var props = { overridableEncoding: true, };
var props = { overridableEncoding: true };
parser.extractFontHeader(props);
expect(props.builtInEncoding[33]).toEqual('arrowright');
expect(props.builtInEncoding[33]).toEqual("arrowright");
});
});

File diff suppressed because it is too large Load diff

View file

@ -14,101 +14,104 @@
*/
import {
getDingbatsGlyphsUnicode, getGlyphsUnicode
} from '../../src/core/glyphlist';
getDingbatsGlyphsUnicode,
getGlyphsUnicode,
} from "../../src/core/glyphlist";
import {
getNormalizedUnicodes, getUnicodeForGlyph, getUnicodeRangeFor,
mapSpecialUnicodeValues, reverseIfRtl
} from '../../src/core/unicode';
getNormalizedUnicodes,
getUnicodeForGlyph,
getUnicodeRangeFor,
mapSpecialUnicodeValues,
reverseIfRtl,
} from "../../src/core/unicode";
describe('unicode', function () {
describe('mapSpecialUnicodeValues', function () {
it('should not re-map normal Unicode values', function () {
describe("unicode", function() {
describe("mapSpecialUnicodeValues", function() {
it("should not re-map normal Unicode values", function() {
// A
expect(mapSpecialUnicodeValues(0x0041)).toEqual(0x0041);
// fi
expect(mapSpecialUnicodeValues(0xFB01)).toEqual(0xFB01);
expect(mapSpecialUnicodeValues(0xfb01)).toEqual(0xfb01);
});
it('should re-map special Unicode values', function () {
it("should re-map special Unicode values", function() {
// copyrightsans => copyright
expect(mapSpecialUnicodeValues(0xF8E9)).toEqual(0x00A9);
expect(mapSpecialUnicodeValues(0xf8e9)).toEqual(0x00a9);
// Private Use Area characters
expect(mapSpecialUnicodeValues(0xFFFF)).toEqual(0);
expect(mapSpecialUnicodeValues(0xffff)).toEqual(0);
});
});
describe('getUnicodeForGlyph', function () {
describe("getUnicodeForGlyph", function() {
var standardMap, dingbatsMap;
beforeAll(function (done) {
beforeAll(function(done) {
standardMap = getGlyphsUnicode();
dingbatsMap = getDingbatsGlyphsUnicode();
done();
});
afterAll(function () {
afterAll(function() {
standardMap = dingbatsMap = null;
});
it('should get Unicode values for valid glyph names', function () {
expect(getUnicodeForGlyph('A', standardMap)).toEqual(0x0041);
expect(getUnicodeForGlyph('a1', dingbatsMap)).toEqual(0x2701);
it("should get Unicode values for valid glyph names", function() {
expect(getUnicodeForGlyph("A", standardMap)).toEqual(0x0041);
expect(getUnicodeForGlyph("a1", dingbatsMap)).toEqual(0x2701);
});
it('should recover Unicode values from uniXXXX/uXXXX{XX} glyph names',
function () {
expect(getUnicodeForGlyph('uni0041', standardMap)).toEqual(0x0041);
expect(getUnicodeForGlyph('u0041', standardMap)).toEqual(0x0041);
it("should recover Unicode values from uniXXXX/uXXXX{XX} glyph names", function() {
expect(getUnicodeForGlyph("uni0041", standardMap)).toEqual(0x0041);
expect(getUnicodeForGlyph("u0041", standardMap)).toEqual(0x0041);
expect(getUnicodeForGlyph('uni2701', dingbatsMap)).toEqual(0x2701);
expect(getUnicodeForGlyph('u2701', dingbatsMap)).toEqual(0x2701);
expect(getUnicodeForGlyph("uni2701", dingbatsMap)).toEqual(0x2701);
expect(getUnicodeForGlyph("u2701", dingbatsMap)).toEqual(0x2701);
});
it('should not get Unicode values for invalid glyph names', function () {
expect(getUnicodeForGlyph('Qwerty', standardMap)).toEqual(-1);
expect(getUnicodeForGlyph('Qwerty', dingbatsMap)).toEqual(-1);
it("should not get Unicode values for invalid glyph names", function() {
expect(getUnicodeForGlyph("Qwerty", standardMap)).toEqual(-1);
expect(getUnicodeForGlyph("Qwerty", dingbatsMap)).toEqual(-1);
});
});
describe('getUnicodeRangeFor', function () {
it('should get correct Unicode range', function () {
describe("getUnicodeRangeFor", function() {
it("should get correct Unicode range", function() {
// A (Basic Latin)
expect(getUnicodeRangeFor(0x0041)).toEqual(0);
// fi (Alphabetic Presentation Forms)
expect(getUnicodeRangeFor(0xFB01)).toEqual(62);
expect(getUnicodeRangeFor(0xfb01)).toEqual(62);
});
it('should not get a Unicode range', function () {
expect(getUnicodeRangeFor(0x05FF)).toEqual(-1);
it("should not get a Unicode range", function() {
expect(getUnicodeRangeFor(0x05ff)).toEqual(-1);
});
});
describe('getNormalizedUnicodes', function () {
describe("getNormalizedUnicodes", function() {
var NormalizedUnicodes;
beforeAll(function (done) {
beforeAll(function(done) {
NormalizedUnicodes = getNormalizedUnicodes();
done();
});
afterAll(function () {
afterAll(function() {
NormalizedUnicodes = null;
});
it('should get normalized Unicode values for ligatures', function () {
it("should get normalized Unicode values for ligatures", function() {
// fi => f + i
expect(NormalizedUnicodes['\uFB01']).toEqual('fi');
expect(NormalizedUnicodes["\uFB01"]).toEqual("fi");
// Arabic
expect(NormalizedUnicodes['\u0675']).toEqual('\u0627\u0674');
expect(NormalizedUnicodes["\u0675"]).toEqual("\u0627\u0674");
});
it('should not normalize standard characters', function () {
expect(NormalizedUnicodes['A']).toEqual(undefined);
it("should not normalize standard characters", function() {
expect(NormalizedUnicodes["A"]).toEqual(undefined);
});
});
describe('reverseIfRtl', function () {
describe("reverseIfRtl", function() {
var NormalizedUnicodes;
function getGlyphUnicode(char) {
@ -118,30 +121,30 @@ describe('unicode', function () {
return char;
}
beforeAll(function (done) {
beforeAll(function(done) {
NormalizedUnicodes = getNormalizedUnicodes();
done();
});
afterAll(function () {
afterAll(function() {
NormalizedUnicodes = null;
});
it('should not reverse LTR characters', function () {
var A = getGlyphUnicode('A');
expect(reverseIfRtl(A)).toEqual('A');
it("should not reverse LTR characters", function() {
var A = getGlyphUnicode("A");
expect(reverseIfRtl(A)).toEqual("A");
var fi = getGlyphUnicode('\uFB01');
expect(reverseIfRtl(fi)).toEqual('fi');
var fi = getGlyphUnicode("\uFB01");
expect(reverseIfRtl(fi)).toEqual("fi");
});
it('should reverse RTL characters', function () {
it("should reverse RTL characters", function() {
// Hebrew (no-op, since it's not a combined character)
var heAlef = getGlyphUnicode('\u05D0');
expect(reverseIfRtl(heAlef)).toEqual('\u05D0');
var heAlef = getGlyphUnicode("\u05D0");
expect(reverseIfRtl(heAlef)).toEqual("\u05D0");
// Arabic
var arAlef = getGlyphUnicode('\u0675');
expect(reverseIfRtl(arAlef)).toEqual('\u0674\u0627');
var arAlef = getGlyphUnicode("\u0675");
expect(reverseIfRtl(arAlef)).toEqual("\u0674\u0627");
});
});
});

View file

@ -14,66 +14,76 @@
*/
import {
bytesToString, createPromiseCapability, createValidAbsoluteUrl, isArrayBuffer,
isBool, isEmptyObj, isNum, isSameOrigin, isSpace, isString, log2,
removeNullCharacters, string32, stringToBytes, stringToPDFString
} from '../../src/shared/util';
bytesToString,
createPromiseCapability,
createValidAbsoluteUrl,
isArrayBuffer,
isBool,
isEmptyObj,
isNum,
isSameOrigin,
isSpace,
isString,
log2,
removeNullCharacters,
string32,
stringToBytes,
stringToPDFString,
} from "../../src/shared/util";
describe('util', function() {
describe('bytesToString', function() {
it('handles non-array arguments', function() {
describe("util", function() {
describe("bytesToString", function() {
it("handles non-array arguments", function() {
expect(function() {
bytesToString(null);
}).toThrow(new Error('Invalid argument for bytesToString'));
}).toThrow(new Error("Invalid argument for bytesToString"));
});
it('handles array arguments with a length not exceeding the maximum',
function() {
expect(bytesToString(new Uint8Array([]))).toEqual('');
expect(bytesToString(new Uint8Array([102, 111, 111]))).toEqual('foo');
it("handles array arguments with a length not exceeding the maximum", function() {
expect(bytesToString(new Uint8Array([]))).toEqual("");
expect(bytesToString(new Uint8Array([102, 111, 111]))).toEqual("foo");
});
it('handles array arguments with a length exceeding the maximum',
function() {
it("handles array arguments with a length exceeding the maximum", function() {
const length = 10000; // Larger than MAX_ARGUMENT_COUNT = 8192.
// Create an array with `length` 'a' character codes.
let bytes = new Uint8Array(length);
for (let i = 0; i < length; i++) {
bytes[i] = 'a'.charCodeAt(0);
bytes[i] = "a".charCodeAt(0);
}
// Create a string with `length` 'a' characters. We need an array of size
// `length + 1` since `join` puts the argument between the array elements.
let string = Array(length + 1).join('a');
let string = Array(length + 1).join("a");
expect(bytesToString(bytes)).toEqual(string);
});
});
describe('isArrayBuffer', function() {
it('handles array buffer values', function() {
describe("isArrayBuffer", function() {
it("handles array buffer values", function() {
expect(isArrayBuffer(new ArrayBuffer(0))).toEqual(true);
expect(isArrayBuffer(new Uint8Array(0))).toEqual(true);
});
it('handles non-array buffer values', function() {
expect(isArrayBuffer('true')).toEqual(false);
it("handles non-array buffer values", function() {
expect(isArrayBuffer("true")).toEqual(false);
expect(isArrayBuffer(1)).toEqual(false);
expect(isArrayBuffer(null)).toEqual(false);
expect(isArrayBuffer(undefined)).toEqual(false);
});
});
describe('isBool', function() {
it('handles boolean values', function() {
describe("isBool", function() {
it("handles boolean values", function() {
expect(isBool(true)).toEqual(true);
expect(isBool(false)).toEqual(true);
});
it('handles non-boolean values', function() {
expect(isBool('true')).toEqual(false);
expect(isBool('false')).toEqual(false);
it("handles non-boolean values", function() {
expect(isBool("true")).toEqual(false);
expect(isBool("false")).toEqual(false);
expect(isBool(1)).toEqual(false);
expect(isBool(0)).toEqual(false);
expect(isBool(null)).toEqual(false);
@ -81,18 +91,18 @@ describe('util', function() {
});
});
describe('isEmptyObj', function() {
it('handles empty objects', function() {
describe("isEmptyObj", function() {
it("handles empty objects", function() {
expect(isEmptyObj({})).toEqual(true);
});
it('handles non-empty objects', function() {
expect(isEmptyObj({ foo: 'bar', })).toEqual(false);
it("handles non-empty objects", function() {
expect(isEmptyObj({ foo: "bar" })).toEqual(false);
});
});
describe('isNum', function() {
it('handles numeric values', function() {
describe("isNum", function() {
it("handles numeric values", function() {
expect(isNum(1)).toEqual(true);
expect(isNum(0)).toEqual(true);
expect(isNum(-1)).toEqual(true);
@ -100,36 +110,36 @@ describe('util', function() {
expect(isNum(12.34)).toEqual(true);
});
it('handles non-numeric values', function() {
expect(isNum('true')).toEqual(false);
it("handles non-numeric values", function() {
expect(isNum("true")).toEqual(false);
expect(isNum(true)).toEqual(false);
expect(isNum(null)).toEqual(false);
expect(isNum(undefined)).toEqual(false);
});
});
describe('isSpace', function() {
it('handles space characters', function() {
describe("isSpace", function() {
it("handles space characters", function() {
expect(isSpace(0x20)).toEqual(true);
expect(isSpace(0x09)).toEqual(true);
expect(isSpace(0x0D)).toEqual(true);
expect(isSpace(0x0A)).toEqual(true);
expect(isSpace(0x0d)).toEqual(true);
expect(isSpace(0x0a)).toEqual(true);
});
it('handles non-space characters', function() {
expect(isSpace(0x0B)).toEqual(false);
it("handles non-space characters", function() {
expect(isSpace(0x0b)).toEqual(false);
expect(isSpace(null)).toEqual(false);
expect(isSpace(undefined)).toEqual(false);
});
});
describe('isString', function() {
it('handles string values', function() {
expect(isString('foo')).toEqual(true);
expect(isString('')).toEqual(true);
describe("isString", function() {
it("handles string values", function() {
expect(isString("foo")).toEqual(true);
expect(isString("")).toEqual(true);
});
it('handles non-string values', function() {
it("handles non-string values", function() {
expect(isString(true)).toEqual(false);
expect(isString(1)).toEqual(false);
expect(isString(null)).toEqual(false);
@ -137,13 +147,13 @@ describe('util', function() {
});
});
describe('log2', function() {
it('handles values smaller than/equal to zero', function() {
describe("log2", function() {
it("handles values smaller than/equal to zero", function() {
expect(log2(0)).toEqual(0);
expect(log2(-1)).toEqual(0);
});
it('handles values larger than zero', function() {
it("handles values larger than zero", function() {
expect(log2(1)).toEqual(0);
expect(log2(2)).toEqual(1);
expect(log2(3)).toEqual(2);
@ -151,181 +161,197 @@ describe('util', function() {
});
});
describe('string32', function() {
it('converts unsigned 32-bit integers to strings', function() {
expect(string32(0x74727565)).toEqual('true');
expect(string32(0x74797031)).toEqual('typ1');
expect(string32(0x4F54544F)).toEqual('OTTO');
describe("string32", function() {
it("converts unsigned 32-bit integers to strings", function() {
expect(string32(0x74727565)).toEqual("true");
expect(string32(0x74797031)).toEqual("typ1");
expect(string32(0x4f54544f)).toEqual("OTTO");
});
});
describe('stringToBytes', function() {
it('handles non-string arguments', function() {
describe("stringToBytes", function() {
it("handles non-string arguments", function() {
expect(function() {
stringToBytes(null);
}).toThrow(new Error('Invalid argument for stringToBytes'));
}).toThrow(new Error("Invalid argument for stringToBytes"));
});
it('handles string arguments', function() {
expect(stringToBytes('')).toEqual(new Uint8Array([]));
expect(stringToBytes('foo')).toEqual(new Uint8Array([102, 111, 111]));
it("handles string arguments", function() {
expect(stringToBytes("")).toEqual(new Uint8Array([]));
expect(stringToBytes("foo")).toEqual(new Uint8Array([102, 111, 111]));
});
});
describe('stringToPDFString', function() {
it('handles ISO Latin 1 strings', function() {
let str = '\x8Dstring\x8E';
expect(stringToPDFString(str)).toEqual('\u201Cstring\u201D');
describe("stringToPDFString", function() {
it("handles ISO Latin 1 strings", function() {
let str = "\x8Dstring\x8E";
expect(stringToPDFString(str)).toEqual("\u201Cstring\u201D");
});
it('handles UTF-16 big-endian strings', function() {
let str = '\xFE\xFF\x00\x73\x00\x74\x00\x72\x00\x69\x00\x6E\x00\x67';
expect(stringToPDFString(str)).toEqual('string');
it("handles UTF-16 big-endian strings", function() {
let str = "\xFE\xFF\x00\x73\x00\x74\x00\x72\x00\x69\x00\x6E\x00\x67";
expect(stringToPDFString(str)).toEqual("string");
});
it('handles UTF-16 little-endian strings', function() {
let str = '\xFF\xFE\x73\x00\x74\x00\x72\x00\x69\x00\x6E\x00\x67\x00';
expect(stringToPDFString(str)).toEqual('string');
it("handles UTF-16 little-endian strings", function() {
let str = "\xFF\xFE\x73\x00\x74\x00\x72\x00\x69\x00\x6E\x00\x67\x00";
expect(stringToPDFString(str)).toEqual("string");
});
it('handles empty strings', function() {
it("handles empty strings", function() {
// ISO Latin 1
let str1 = '';
expect(stringToPDFString(str1)).toEqual('');
let str1 = "";
expect(stringToPDFString(str1)).toEqual("");
// UTF-16BE
let str2 = '\xFE\xFF';
expect(stringToPDFString(str2)).toEqual('');
let str2 = "\xFE\xFF";
expect(stringToPDFString(str2)).toEqual("");
// UTF-16LE
let str3 = '\xFF\xFE';
expect(stringToPDFString(str3)).toEqual('');
let str3 = "\xFF\xFE";
expect(stringToPDFString(str3)).toEqual("");
});
});
describe('removeNullCharacters', function() {
it('should not modify string without null characters', function() {
let str = 'string without null chars';
expect(removeNullCharacters(str)).toEqual('string without null chars');
describe("removeNullCharacters", function() {
it("should not modify string without null characters", function() {
let str = "string without null chars";
expect(removeNullCharacters(str)).toEqual("string without null chars");
});
it('should modify string with null characters', function() {
let str = 'string\x00With\x00Null\x00Chars';
expect(removeNullCharacters(str)).toEqual('stringWithNullChars');
it("should modify string with null characters", function() {
let str = "string\x00With\x00Null\x00Chars";
expect(removeNullCharacters(str)).toEqual("stringWithNullChars");
});
});
describe('ReadableStream', function() {
it('should return an Object', function () {
describe("ReadableStream", function() {
it("should return an Object", function() {
let readable = new ReadableStream();
expect(typeof readable).toEqual('object');
expect(typeof readable).toEqual("object");
});
it('should have property getReader', function () {
it("should have property getReader", function() {
let readable = new ReadableStream();
expect(typeof readable.getReader).toEqual('function');
expect(typeof readable.getReader).toEqual("function");
});
});
describe('URL', function() {
it('should return an Object', function() {
const url = new URL('https://example.com');
expect(typeof url).toEqual('object');
describe("URL", function() {
it("should return an Object", function() {
const url = new URL("https://example.com");
expect(typeof url).toEqual("object");
});
it('should have property `href`', function() {
const url = new URL('https://example.com');
expect(typeof url.href).toEqual('string');
it("should have property `href`", function() {
const url = new URL("https://example.com");
expect(typeof url.href).toEqual("string");
});
});
describe('isSameOrigin', function() {
it('handles invalid base URLs', function() {
describe("isSameOrigin", function() {
it("handles invalid base URLs", function() {
// The base URL is not valid.
expect(isSameOrigin('/foo', '/bar')).toEqual(false);
expect(isSameOrigin("/foo", "/bar")).toEqual(false);
// The base URL has no origin.
expect(isSameOrigin('blob:foo', '/bar')).toEqual(false);
expect(isSameOrigin("blob:foo", "/bar")).toEqual(false);
});
it('correctly checks if the origin of both URLs matches', function() {
expect(isSameOrigin('https://www.mozilla.org/foo',
'https://www.mozilla.org/bar')).toEqual(true);
expect(isSameOrigin('https://www.mozilla.org/foo',
'https://www.example.com/bar')).toEqual(false);
it("correctly checks if the origin of both URLs matches", function() {
expect(
isSameOrigin(
"https://www.mozilla.org/foo",
"https://www.mozilla.org/bar"
)
).toEqual(true);
expect(
isSameOrigin(
"https://www.mozilla.org/foo",
"https://www.example.com/bar"
)
).toEqual(false);
});
});
describe('createValidAbsoluteUrl', function() {
it('handles invalid URLs', function() {
describe("createValidAbsoluteUrl", function() {
it("handles invalid URLs", function() {
expect(createValidAbsoluteUrl(undefined, undefined)).toEqual(null);
expect(createValidAbsoluteUrl(null, null)).toEqual(null);
expect(createValidAbsoluteUrl('/foo', '/bar')).toEqual(null);
expect(createValidAbsoluteUrl("/foo", "/bar")).toEqual(null);
});
it('handles URLs that do not use a whitelisted protocol', function() {
expect(createValidAbsoluteUrl('magnet:?foo', null)).toEqual(null);
it("handles URLs that do not use a whitelisted protocol", function() {
expect(createValidAbsoluteUrl("magnet:?foo", null)).toEqual(null);
});
it('correctly creates a valid URL for whitelisted protocols', function() {
it("correctly creates a valid URL for whitelisted protocols", function() {
// `http` protocol
expect(createValidAbsoluteUrl('http://www.mozilla.org/foo', null))
.toEqual(new URL('http://www.mozilla.org/foo'));
expect(createValidAbsoluteUrl('/foo', 'http://www.mozilla.org'))
.toEqual(new URL('http://www.mozilla.org/foo'));
expect(
createValidAbsoluteUrl("http://www.mozilla.org/foo", null)
).toEqual(new URL("http://www.mozilla.org/foo"));
expect(createValidAbsoluteUrl("/foo", "http://www.mozilla.org")).toEqual(
new URL("http://www.mozilla.org/foo")
);
// `https` protocol
expect(createValidAbsoluteUrl('https://www.mozilla.org/foo', null))
.toEqual(new URL('https://www.mozilla.org/foo'));
expect(createValidAbsoluteUrl('/foo', 'https://www.mozilla.org'))
.toEqual(new URL('https://www.mozilla.org/foo'));
expect(
createValidAbsoluteUrl("https://www.mozilla.org/foo", null)
).toEqual(new URL("https://www.mozilla.org/foo"));
expect(createValidAbsoluteUrl("/foo", "https://www.mozilla.org")).toEqual(
new URL("https://www.mozilla.org/foo")
);
// `ftp` protocol
expect(createValidAbsoluteUrl('ftp://www.mozilla.org/foo', null))
.toEqual(new URL('ftp://www.mozilla.org/foo'));
expect(createValidAbsoluteUrl('/foo', 'ftp://www.mozilla.org'))
.toEqual(new URL('ftp://www.mozilla.org/foo'));
expect(createValidAbsoluteUrl("ftp://www.mozilla.org/foo", null)).toEqual(
new URL("ftp://www.mozilla.org/foo")
);
expect(createValidAbsoluteUrl("/foo", "ftp://www.mozilla.org")).toEqual(
new URL("ftp://www.mozilla.org/foo")
);
// `mailto` protocol (base URLs have no meaning and should yield `null`)
expect(createValidAbsoluteUrl('mailto:foo@bar.baz', null))
.toEqual(new URL('mailto:foo@bar.baz'));
expect(createValidAbsoluteUrl('/foo', 'mailto:foo@bar.baz'))
.toEqual(null);
expect(createValidAbsoluteUrl("mailto:foo@bar.baz", null)).toEqual(
new URL("mailto:foo@bar.baz")
);
expect(createValidAbsoluteUrl("/foo", "mailto:foo@bar.baz")).toEqual(
null
);
// `tel` protocol (base URLs have no meaning and should yield `null`)
expect(createValidAbsoluteUrl('tel:+0123456789', null))
.toEqual(new URL('tel:+0123456789'));
expect(createValidAbsoluteUrl('/foo', 'tel:0123456789'))
.toEqual(null);
expect(createValidAbsoluteUrl("tel:+0123456789", null)).toEqual(
new URL("tel:+0123456789")
);
expect(createValidAbsoluteUrl("/foo", "tel:0123456789")).toEqual(null);
});
});
describe('createPromiseCapability', function() {
it('should resolve with correct data', function(done) {
describe("createPromiseCapability", function() {
it("should resolve with correct data", function(done) {
const promiseCapability = createPromiseCapability();
expect(promiseCapability.settled).toEqual(false);
promiseCapability.resolve({ test: 'abc', });
promiseCapability.resolve({ test: "abc" });
promiseCapability.promise.then(function(data) {
expect(promiseCapability.settled).toEqual(true);
expect(data).toEqual({ test: 'abc', });
expect(data).toEqual({ test: "abc" });
done();
}, done.fail);
});
it('should reject with correct reason', function(done) {
it("should reject with correct reason", function(done) {
const promiseCapability = createPromiseCapability();
expect(promiseCapability.settled).toEqual(false);
promiseCapability.reject(new Error('reason'));
promiseCapability.reject(new Error("reason"));
promiseCapability.promise.then(done.fail, function(reason) {
expect(promiseCapability.settled).toEqual(true);
expect(reason instanceof Error).toEqual(true);
expect(reason.message).toEqual('reason');
expect(reason.message).toEqual("reason");
done();
});
});

View file

@ -15,16 +15,16 @@
*/
/* eslint-disable object-shorthand */
'use strict';
"use strict";
var os = require('os');
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;
var testUtils = require('./testutils.js');
var crypto = require('crypto');
var os = require("os");
var fs = require("fs");
var path = require("path");
var spawn = require("child_process").spawn;
var testUtils = require("./testutils.js");
var crypto = require("crypto");
var tempDirPrefix = 'pdfjs_';
var tempDirPrefix = "pdfjs_";
function WebBrowser(name, path, headless) {
this.name = name;
@ -38,19 +38,19 @@ function WebBrowser(name, path, headless) {
this.callback = null;
// Used to identify processes whose pid is lost. This string is directly used
// as a command-line argument, so it only consists of letters.
this.uniqStringId = 'webbrowser' + crypto.randomBytes(32).toString('hex');
this.uniqStringId = "webbrowser" + crypto.randomBytes(32).toString("hex");
}
WebBrowser.prototype = {
start: function (url) {
start: function(url) {
this.tmpDir = path.join(os.tmpdir(), tempDirPrefix + this.name);
if (!fs.existsSync(this.tmpDir)) {
fs.mkdirSync(this.tmpDir);
}
this.startProcess(url);
},
getProfileDir: function () {
getProfileDir: function() {
if (!this.profileDir) {
var profileDir = path.join(this.tmpDir, 'profile');
var profileDir = path.join(this.tmpDir, "profile");
if (fs.existsSync(profileDir)) {
testUtils.removeDirSync(profileDir);
}
@ -60,50 +60,56 @@ WebBrowser.prototype = {
}
return this.profileDir;
},
buildArguments: function (url) {
buildArguments: function(url) {
return [url];
},
setupProfileDir: function (dir) {
},
startProcess: function (url) {
console.assert(!this.process, 'startProcess may be called only once');
setupProfileDir: function(dir) {},
startProcess: function(url) {
console.assert(!this.process, "startProcess may be called only once");
var args = this.buildArguments(url);
args = args.concat('--' + this.uniqStringId);
args = args.concat("--" + this.uniqStringId);
this.process = spawn(this.path, args, { stdio: [process.stdin,
process.stdout,
process.stderr], });
this.process = spawn(this.path, args, {
stdio: [process.stdin, process.stdout, process.stderr],
});
this.process.on('exit', function (code, signal) {
this.process = null;
var exitInfo = code !== null ? ' with status ' + code :
' in response to signal ' + signal;
if (this.requestedExit) {
this.log('Browser process exited' + exitInfo);
} else {
// This was observed on Windows bots with Firefox. Apparently the
// Firefox Maintenance Service restarts Firefox shortly after starting
// up. When this happens, we no longer know the pid of the process.
this.log('Browser process unexpectedly exited' + exitInfo);
}
}.bind(this));
this.process.on(
"exit",
function(code, signal) {
this.process = null;
var exitInfo =
code !== null
? " with status " + code
: " in response to signal " + signal;
if (this.requestedExit) {
this.log("Browser process exited" + exitInfo);
} else {
// This was observed on Windows bots with Firefox. Apparently the
// Firefox Maintenance Service restarts Firefox shortly after starting
// up. When this happens, we no longer know the pid of the process.
this.log("Browser process unexpectedly exited" + exitInfo);
}
}.bind(this)
);
},
cleanup: function () {
console.assert(this.requestedExit,
'cleanup should only be called after an explicit stop() request');
cleanup: function() {
console.assert(
this.requestedExit,
"cleanup should only be called after an explicit stop() request"
);
try {
testUtils.removeDirSync(this.tmpDir);
} catch (e) {
if (e.code !== 'ENOENT') {
this.log('Failed to remove profile directory: ' + e);
if (e.code !== "ENOENT") {
this.log("Failed to remove profile directory: " + e);
if (!this.cleanupFailStart) {
this.cleanupFailStart = Date.now();
} else if (Date.now() - this.cleanupFailStart > 10000) {
throw new Error('Failed to remove profile dir within 10 seconds');
throw new Error("Failed to remove profile dir within 10 seconds");
}
this.log('Retrying in a second...');
this.log("Retrying in a second...");
setTimeout(this.cleanup.bind(this), 1000);
return;
}
@ -111,57 +117,63 @@ WebBrowser.prototype = {
// because the post-condition of cleanup is that the profile directory is
// gone. If the directory does not exists, then this post-condition is
// satisfied.
this.log('Cannot remove non-existent directory: ' + e);
this.log("Cannot remove non-existent directory: " + e);
}
this.finished = true;
this.log('Clean-up finished. Going to call callback...');
this.log("Clean-up finished. Going to call callback...");
this.callback();
},
stop: function (callback) {
console.assert(this.tmpDir, '.start() must be called before stop()');
stop: function(callback) {
console.assert(this.tmpDir, ".start() must be called before stop()");
// Require the callback to ensure that callers do not make any assumptions
// on the state of this browser instance until the callback is called.
console.assert(typeof callback === 'function', 'callback is required');
console.assert(!this.requestedExit, '.stop() may be called only once');
console.assert(typeof callback === "function", "callback is required");
console.assert(!this.requestedExit, ".stop() may be called only once");
this.requestedExit = true;
if (this.finished) {
this.log('Browser already stopped, invoking callback...');
this.log("Browser already stopped, invoking callback...");
callback();
} else if (this.process) {
this.log('Going to wait until the browser process has exited.');
this.log("Going to wait until the browser process has exited.");
this.callback = callback;
this.process.once('exit', this.cleanup.bind(this));
this.process.kill('SIGTERM');
this.process.once("exit", this.cleanup.bind(this));
this.process.kill("SIGTERM");
} else {
this.log('Process already exited, checking if the process restarted...');
this.log("Process already exited, checking if the process restarted...");
this.callback = callback;
this.killProcessUnknownPid(this.cleanup.bind(this));
}
},
killProcessUnknownPid: function(callback) {
this.log('pid unknown, killing processes matching ' + this.uniqStringId);
this.log("pid unknown, killing processes matching " + this.uniqStringId);
var cmdKillAll, cmdCheckAllKilled, isAllKilled;
if (process.platform === 'win32') {
var wmicPrefix = ['process', 'where', '"not Name = \'cmd.exe\' ' +
'and not Name like \'%wmic%\' ' +
'and CommandLine like \'%' + this.uniqStringId + '%\'"'];
if (process.platform === "win32") {
var wmicPrefix = [
"process",
"where",
"\"not Name = 'cmd.exe' " +
"and not Name like '%wmic%' " +
"and CommandLine like '%" +
this.uniqStringId +
"%'\"",
];
cmdKillAll = {
file: 'wmic',
args: wmicPrefix.concat(['call', 'terminate']),
file: "wmic",
args: wmicPrefix.concat(["call", "terminate"]),
};
cmdCheckAllKilled = {
file: 'wmic',
args: wmicPrefix.concat(['get', 'CommandLine']),
file: "wmic",
args: wmicPrefix.concat(["get", "CommandLine"]),
};
isAllKilled = function(exitCode, stdout) {
return !stdout.includes(this.uniqStringId);
}.bind(this);
} else {
cmdKillAll = { file: 'pkill', args: ['-f', this.uniqStringId], };
cmdCheckAllKilled = { file: 'pgrep', args: ['-f', this.uniqStringId], };
cmdKillAll = { file: "pkill", args: ["-f", this.uniqStringId] };
cmdCheckAllKilled = { file: "pgrep", args: ["-f", this.uniqStringId] };
isAllKilled = function(pgrepStatus) {
return pgrepStatus === 1; // "No process matched.", per man pgrep.
};
@ -169,109 +181,119 @@ WebBrowser.prototype = {
function execAsyncNoStdin(cmd, onExit) {
var proc = spawn(cmd.file, cmd.args, {
shell: true,
stdio: 'pipe',
stdio: "pipe",
});
// Close stdin, otherwise wmic won't run.
proc.stdin.end();
var stdout = '';
proc.stdout.on('data', (data) => {
var stdout = "";
proc.stdout.on("data", data => {
stdout += data;
});
proc.on('close', (code) => {
proc.on("close", code => {
onExit(code, stdout);
});
}
var killDateStart = Date.now();
// Note: First process' output it shown, the later outputs are suppressed.
execAsyncNoStdin(cmdKillAll, function checkAlive(exitCode, firstStdout) {
execAsyncNoStdin(cmdCheckAllKilled, function(exitCode, stdout) {
if (isAllKilled(exitCode, stdout)) {
callback();
} else if (Date.now() - killDateStart > 10000) {
// Should finish termination within 10 (generous) seconds.
if (firstStdout) {
this.log('Output of first command:\n' + firstStdout);
}
if (stdout) {
this.log('Output of last command:\n' + stdout);
}
throw new Error('Failed to kill process of ' + this.name);
} else {
setTimeout(checkAlive.bind(this), 500);
}
}.bind(this));
}.bind(this));
execAsyncNoStdin(
cmdKillAll,
function checkAlive(exitCode, firstStdout) {
execAsyncNoStdin(
cmdCheckAllKilled,
function(exitCode, stdout) {
if (isAllKilled(exitCode, stdout)) {
callback();
} else if (Date.now() - killDateStart > 10000) {
// Should finish termination within 10 (generous) seconds.
if (firstStdout) {
this.log("Output of first command:\n" + firstStdout);
}
if (stdout) {
this.log("Output of last command:\n" + stdout);
}
throw new Error("Failed to kill process of " + this.name);
} else {
setTimeout(checkAlive.bind(this), 500);
}
}.bind(this)
);
}.bind(this)
);
},
log: function(msg) {
console.log('[' + this.name + '] ' + msg);
console.log("[" + this.name + "] " + msg);
},
};
var firefoxResourceDir = path.join(__dirname, 'resources', 'firefox');
var firefoxResourceDir = path.join(__dirname, "resources", "firefox");
function FirefoxBrowser(name, path, headless) {
if (os.platform() === 'darwin') {
if (os.platform() === "darwin") {
var m = /([^.\/]+)\.app(\/?)$/.exec(path);
if (m) {
path += (m[2] ? '' : '/') + 'Contents/MacOS/firefox';
path += (m[2] ? "" : "/") + "Contents/MacOS/firefox";
}
}
WebBrowser.call(this, name, path, headless);
}
FirefoxBrowser.prototype = Object.create(WebBrowser.prototype);
FirefoxBrowser.prototype.buildArguments = function (url) {
FirefoxBrowser.prototype.buildArguments = function(url) {
var profileDir = this.getProfileDir();
var args = [];
if (os.platform() === 'darwin') {
args.push('-foreground');
if (os.platform() === "darwin") {
args.push("-foreground");
}
if (this.headless) {
args.push('--headless');
args.push("--headless");
}
args.push('-no-remote', '-profile', profileDir, url);
args.push("-no-remote", "-profile", profileDir, url);
return args;
};
FirefoxBrowser.prototype.setupProfileDir = function (dir) {
FirefoxBrowser.prototype.setupProfileDir = function(dir) {
testUtils.copySubtreeSync(firefoxResourceDir, dir);
};
function ChromiumBrowser(name, path, headless) {
if (os.platform() === 'darwin') {
if (os.platform() === "darwin") {
var m = /([^.\/]+)\.app(\/?)$/.exec(path);
if (m) {
path += (m[2] ? '' : '/') + 'Contents/MacOS/' + m[1];
path += (m[2] ? "" : "/") + "Contents/MacOS/" + m[1];
console.log(path);
}
}
WebBrowser.call(this, name, path, headless);
}
ChromiumBrowser.prototype = Object.create(WebBrowser.prototype);
ChromiumBrowser.prototype.buildArguments = function (url) {
ChromiumBrowser.prototype.buildArguments = function(url) {
var profileDir = this.getProfileDir();
var crashDumpsDir = path.join(this.tmpDir, 'crash_dumps');
var args = ['--user-data-dir=' + profileDir,
'--no-first-run',
'--disable-sync',
'--no-default-browser-check',
'--disable-device-discovery-notifications',
'--disable-translate',
'--disable-background-timer-throttling',
'--disable-renderer-backgrounding'];
var crashDumpsDir = path.join(this.tmpDir, "crash_dumps");
var args = [
"--user-data-dir=" + profileDir,
"--no-first-run",
"--disable-sync",
"--no-default-browser-check",
"--disable-device-discovery-notifications",
"--disable-translate",
"--disable-background-timer-throttling",
"--disable-renderer-backgrounding",
];
if (this.headless) {
args.push('--headless',
'--crash-dumps-dir=' + crashDumpsDir,
'--disable-gpu',
'--remote-debugging-port=9222');
args.push(
"--headless",
"--crash-dumps-dir=" + crashDumpsDir,
"--disable-gpu",
"--remote-debugging-port=9222"
);
}
args.push(url);
return args;
};
WebBrowser.create = function (desc) {
WebBrowser.create = function(desc) {
var name = desc.name;
var path = fs.realpathSync(desc.path);
if (!path) {
throw new Error('Browser executable not found: ' + desc.path);
throw new Error("Browser executable not found: " + desc.path);
}
if (/firefox/i.test(name)) {

View file

@ -15,56 +15,57 @@
*/
/* eslint-disable object-shorthand */
'use strict';
"use strict";
var http = require('http');
var path = require('path');
var fs = require('fs');
var http = require("http");
var path = require("path");
var fs = require("fs");
var mimeTypes = {
'.css': 'text/css',
'.html': 'text/html',
'.js': 'application/javascript',
'.json': 'application/json',
'.svg': 'image/svg+xml',
'.pdf': 'application/pdf',
'.xhtml': 'application/xhtml+xml',
'.gif': 'image/gif',
'.ico': 'image/x-icon',
'.png': 'image/png',
'.log': 'text/plain',
'.bcmap': 'application/octet-stream',
'.properties': 'text/plain',
".css": "text/css",
".html": "text/html",
".js": "application/javascript",
".json": "application/json",
".svg": "image/svg+xml",
".pdf": "application/pdf",
".xhtml": "application/xhtml+xml",
".gif": "image/gif",
".ico": "image/x-icon",
".png": "image/png",
".log": "text/plain",
".bcmap": "application/octet-stream",
".properties": "text/plain",
};
var defaultMimeType = 'application/octet-stream';
var defaultMimeType = "application/octet-stream";
function WebServer() {
this.root = '.';
this.host = 'localhost';
this.root = ".";
this.host = "localhost";
this.port = 0;
this.server = null;
this.verbose = false;
this.cacheExpirationTime = 0;
this.disableRangeRequests = false;
this.hooks = {
'GET': [crossOriginHandler],
'POST': [],
GET: [crossOriginHandler],
POST: [],
};
}
WebServer.prototype = {
start: function (callback) {
start: function(callback) {
this._ensureNonZeroPort();
this.server = http.createServer(this._handler.bind(this));
this.server.listen(this.port, this.host, callback);
console.log(
'Server running at http://' + this.host + ':' + this.port + '/');
"Server running at http://" + this.host + ":" + this.port + "/"
);
},
stop: function (callback) {
stop: function(callback) {
this.server.close(callback);
this.server = null;
},
_ensureNonZeroPort: function () {
_ensureNonZeroPort: function() {
if (!this.port) {
// If port is 0, a random port will be chosen instead. Do not set a host
// name to make sure that the port is synchronously set by .listen().
@ -77,8 +78,8 @@ WebServer.prototype = {
server.close();
}
},
_handler: function (req, res) {
var url = req.url.replace(/\/\//g, '/');
_handler: function(req, res) {
var url = req.url.replace(/\/\//g, "/");
var urlParts = /([^?]*)((?:\?(.*))?)/.exec(url);
try {
// Guard against directory traversal attacks such as
@ -88,13 +89,13 @@ WebServer.prototype = {
// path.normalize returns a path on the basis of the current platform.
// Windows paths cause issues in statFile and serverDirectoryIndex.
// Converting to unix path would avoid platform checks in said functions.
pathPart = pathPart.replace(/\\/g, '/');
pathPart = pathPart.replace(/\\/g, "/");
} catch (ex) {
// If the URI cannot be decoded, a `URIError` is thrown. This happens for
// malformed URIs such as `http://localhost:8888/%s%s` and should be
// handled as a bad request.
res.writeHead(400);
res.end('Bad request', 'utf8');
res.end("Bad request", "utf8");
return;
}
var queryPart = urlParts[3];
@ -103,19 +104,21 @@ WebServer.prototype = {
var methodHooks = this.hooks[req.method];
if (!methodHooks) {
res.writeHead(405);
res.end('Unsupported request method', 'utf8');
res.end("Unsupported request method", "utf8");
return;
}
var handled = methodHooks.some(function (hook) {
var handled = methodHooks.some(function(hook) {
return hook(req, res);
});
if (handled) {
return;
}
if (pathPart === '/favicon.ico') {
fs.realpath(path.join(this.root, 'test/resources/favicon.ico'),
checkFile);
if (pathPart === "/favicon.ico") {
fs.realpath(
path.join(this.root, "test/resources/favicon.ico"),
checkFile
);
return;
}
@ -130,7 +133,7 @@ WebServer.prototype = {
res.writeHead(404);
res.end();
if (verbose) {
console.error(url + ': not found');
console.error(url + ": not found");
}
return;
}
@ -150,9 +153,9 @@ WebServer.prototype = {
fileSize = stats.size;
var isDir = stats.isDirectory();
if (isDir && !/\/$/.test(pathPart)) {
res.setHeader('Location', pathPart + '/' + urlParts[2]);
res.setHeader("Location", pathPart + "/" + urlParts[2]);
res.writeHead(301);
res.end('Redirected', 'utf8');
res.end("Redirected", "utf8");
return;
}
if (isDir) {
@ -160,12 +163,12 @@ WebServer.prototype = {
return;
}
var range = req.headers['range'];
var range = req.headers["range"];
if (range && !disableRangeRequests) {
var rangesMatches = /^bytes=(\d+)\-(\d+)?/.exec(range);
if (!rangesMatches) {
res.writeHead(501);
res.end('Bad range', 'utf8');
res.end("Bad range", "utf8");
if (verbose) {
console.error(url + ': bad range: "' + range + '"');
}
@ -174,11 +177,13 @@ WebServer.prototype = {
var start = +rangesMatches[1];
var end = +rangesMatches[2];
if (verbose) {
console.log(url + ': range ' + start + ' - ' + end);
console.log(url + ": range " + start + " - " + end);
}
serveRequestedFileRange(filePath,
start,
isNaN(end) ? fileSize : (end + 1));
serveRequestedFileRange(
filePath,
start,
isNaN(end) ? fileSize : end + 1
);
return;
}
if (verbose) {
@ -191,53 +196,61 @@ WebServer.prototype = {
// Escape untrusted input so that it can safely be used in a HTML response
// in HTML and in HTML attributes.
return untrusted
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function serveDirectoryIndex(dir) {
res.setHeader('Content-Type', 'text/html');
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
if (queryPart === 'frame') {
res.end('<html><frameset cols=*,200><frame name=pdf>' +
'<frame src=\"' + encodeURI(pathPart) +
'?side\"></frameset></html>', 'utf8');
if (queryPart === "frame") {
res.end(
"<html><frameset cols=*,200><frame name=pdf>" +
'<frame src="' +
encodeURI(pathPart) +
'?side"></frameset></html>',
"utf8"
);
return;
}
var all = queryPart === 'all';
fs.readdir(dir, function (err, files) {
var all = queryPart === "all";
fs.readdir(dir, function(err, files) {
if (err) {
res.end();
return;
}
res.write('<html><head><meta charset=\"utf-8\"></head><body>' +
'<h1>PDFs of ' + pathPart + '</h1>\n');
if (pathPart !== '/') {
res.write('<a href=\"..\">..</a><br>\n');
res.write(
'<html><head><meta charset="utf-8"></head><body>' +
"<h1>PDFs of " +
pathPart +
"</h1>\n"
);
if (pathPart !== "/") {
res.write('<a href="..">..</a><br>\n');
}
files.forEach(function (file) {
files.forEach(function(file) {
var stat;
var item = pathPart + file;
var href = '';
var label = '';
var extraAttributes = '';
var href = "";
var label = "";
var extraAttributes = "";
try {
stat = fs.statSync(path.join(dir, file));
} catch (e) {
href = encodeURI(item);
label = file + ' (' + e + ')';
label = file + " (" + e + ")";
extraAttributes = ' style="color:red"';
}
if (stat) {
if (stat.isDirectory()) {
href = encodeURI(item);
label = file;
} else if (path.extname(file).toLowerCase() === '.pdf') {
href = '/web/viewer.html?file=' + encodeURIComponent(item);
} else if (path.extname(file).toLowerCase() === ".pdf") {
href = "/web/viewer.html?file=" + encodeURIComponent(item);
label = file;
extraAttributes = ' target="pdf"';
} else if (all) {
@ -246,25 +259,34 @@ WebServer.prototype = {
}
}
if (label) {
res.write('<a href=\"' + escapeHTML(href) + '\"' +
extraAttributes + '>' + escapeHTML(label) + '</a><br>\n');
res.write(
'<a href="' +
escapeHTML(href) +
'"' +
extraAttributes +
">" +
escapeHTML(label) +
"</a><br>\n"
);
}
});
if (files.length === 0) {
res.write('<p>no files found</p>\n');
res.write("<p>no files found</p>\n");
}
if (!all && queryPart !== 'side') {
res.write('<hr><p>(only PDF files are shown, ' +
'<a href=\"?all\">show all</a>)</p>\n');
if (!all && queryPart !== "side") {
res.write(
"<hr><p>(only PDF files are shown, " +
'<a href="?all">show all</a>)</p>\n'
);
}
res.end('</body></html>');
res.end("</body></html>");
});
}
function serveRequestedFile(filePath) {
var stream = fs.createReadStream(filePath, { flags: 'rs', });
var stream = fs.createReadStream(filePath, { flags: "rs" });
stream.on('error', function (error) {
stream.on("error", function(error) {
res.writeHead(500);
res.end();
});
@ -273,14 +295,14 @@ WebServer.prototype = {
var contentType = mimeTypes[ext] || defaultMimeType;
if (!disableRangeRequests) {
res.setHeader('Accept-Ranges', 'bytes');
res.setHeader("Accept-Ranges", "bytes");
}
res.setHeader('Content-Type', contentType);
res.setHeader('Content-Length', fileSize);
res.setHeader("Content-Type", contentType);
res.setHeader("Content-Length", fileSize);
if (cacheExpirationTime > 0) {
var expireTime = new Date();
expireTime.setSeconds(expireTime.getSeconds() + cacheExpirationTime);
res.setHeader('Expires', expireTime.toUTCString());
res.setHeader("Expires", expireTime.toUTCString());
}
res.writeHead(200);
@ -289,9 +311,12 @@ WebServer.prototype = {
function serveRequestedFileRange(filePath, start, end) {
var stream = fs.createReadStream(filePath, {
flags: 'rs', start: start, end: end - 1, });
flags: "rs",
start: start,
end: end - 1,
});
stream.on('error', function (error) {
stream.on("error", function(error) {
res.writeHead(500);
res.end();
});
@ -299,16 +324,17 @@ WebServer.prototype = {
var ext = path.extname(filePath).toLowerCase();
var contentType = mimeTypes[ext] || defaultMimeType;
res.setHeader('Accept-Ranges', 'bytes');
res.setHeader('Content-Type', contentType);
res.setHeader('Content-Length', (end - start));
res.setHeader('Content-Range',
'bytes ' + start + '-' + (end - 1) + '/' + fileSize);
res.setHeader("Accept-Ranges", "bytes");
res.setHeader("Content-Type", contentType);
res.setHeader("Content-Length", end - start);
res.setHeader(
"Content-Range",
"bytes " + start + "-" + (end - 1) + "/" + fileSize
);
res.writeHead(206);
stream.pipe(res);
}
},
};
@ -317,12 +343,12 @@ WebServer.prototype = {
// expected if the user does "gulp server" and then visits
// http://localhost:8888/test/unit/unit_test.html?spec=Cross-origin
function crossOriginHandler(req, res) {
if (req.url === '/test/pdfs/basicapi.pdf?cors=withCredentials') {
res.setHeader('Access-Control-Allow-Origin', req.headers['origin']);
res.setHeader('Access-Control-Allow-Credentials', 'true');
if (req.url === "/test/pdfs/basicapi.pdf?cors=withCredentials") {
res.setHeader("Access-Control-Allow-Origin", req.headers["origin"]);
res.setHeader("Access-Control-Allow-Credentials", "true");
}
if (req.url === '/test/pdfs/basicapi.pdf?cors=withoutCredentials') {
res.setHeader('Access-Control-Allow-Origin', req.headers['origin']);
if (req.url === "/test/pdfs/basicapi.pdf?cors=withoutCredentials") {
res.setHeader("Access-Control-Allow-Origin", req.headers["origin"]);
}
}