Merge pull request #12530 from calixteman/js_utils
JS -- Add 'util' object
This commit is contained in:
commit
018fd43096
@ -1273,7 +1273,7 @@ function buildLib(defines, dir) {
|
||||
return merge([
|
||||
gulp.src(
|
||||
[
|
||||
"src/{core,display,shared}/*.js",
|
||||
"src/{core,display,scripting_api,shared}/*.js",
|
||||
"!src/shared/{cffStandardStrings,fonts_utils}.js",
|
||||
"src/{pdf,pdf.worker}.js",
|
||||
],
|
||||
|
@ -20,11 +20,562 @@ class Util extends PDFObject {
|
||||
super(data);
|
||||
|
||||
this._crackURL = data.crackURL;
|
||||
this._scandCache = new Map();
|
||||
this._months = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
];
|
||||
this._days = [
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
];
|
||||
this.MILLISECONDS_IN_DAY = 86400000;
|
||||
this.MILLISECONDS_IN_WEEK = 604800000;
|
||||
}
|
||||
|
||||
crackURL(cURL) {
|
||||
return this._crackURL(cURL);
|
||||
}
|
||||
|
||||
printf(...args) {
|
||||
if (args.length === 0) {
|
||||
throw new Error("Invalid number of params in printf");
|
||||
}
|
||||
|
||||
if (typeof args[0] !== "string") {
|
||||
throw new TypeError("First argument of printf must be a string");
|
||||
}
|
||||
|
||||
const pattern = /%(,[0-4])?([\+ 0#]+)?([0-9]+)?(\.[0-9]+)?(.)/g;
|
||||
const PLUS = 1;
|
||||
const SPACE = 2;
|
||||
const ZERO = 4;
|
||||
const HASH = 8;
|
||||
let i = 0;
|
||||
return args[0].replace(pattern, function (
|
||||
match,
|
||||
nDecSep,
|
||||
cFlags,
|
||||
nWidth,
|
||||
nPrecision,
|
||||
cConvChar
|
||||
) {
|
||||
// cConvChar must be one of d, f, s, x
|
||||
if (
|
||||
cConvChar !== "d" &&
|
||||
cConvChar !== "f" &&
|
||||
cConvChar !== "s" &&
|
||||
cConvChar !== "x"
|
||||
) {
|
||||
const buf = ["%"];
|
||||
for (const str of [nDecSep, cFlags, nWidth, nPrecision, cConvChar]) {
|
||||
if (str) {
|
||||
buf.push(str);
|
||||
}
|
||||
}
|
||||
return buf.join("");
|
||||
}
|
||||
|
||||
i++;
|
||||
if (i === args.length) {
|
||||
throw new Error("Not enough arguments in printf");
|
||||
}
|
||||
const arg = args[i];
|
||||
|
||||
if (cConvChar === "s") {
|
||||
return arg.toString();
|
||||
}
|
||||
|
||||
let flags = 0;
|
||||
if (cFlags) {
|
||||
for (const flag of cFlags) {
|
||||
switch (flag) {
|
||||
case "+":
|
||||
flags |= PLUS;
|
||||
break;
|
||||
case " ":
|
||||
flags |= SPACE;
|
||||
break;
|
||||
case "0":
|
||||
flags |= ZERO;
|
||||
break;
|
||||
case "#":
|
||||
flags |= HASH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
cFlags = flags;
|
||||
|
||||
if (nWidth) {
|
||||
nWidth = parseInt(nWidth);
|
||||
}
|
||||
|
||||
let intPart = Math.trunc(arg);
|
||||
|
||||
if (cConvChar === "x") {
|
||||
let hex = Math.abs(intPart).toString(16).toUpperCase();
|
||||
if (nWidth !== undefined) {
|
||||
hex = hex.padStart(nWidth, cFlags & ZERO ? "0" : " ");
|
||||
}
|
||||
if (cFlags & HASH) {
|
||||
hex = `0x${hex}`;
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
if (nPrecision) {
|
||||
nPrecision = parseInt(nPrecision.substring(1));
|
||||
}
|
||||
|
||||
nDecSep = nDecSep ? nDecSep.substring(1) : "0";
|
||||
const separators = {
|
||||
0: [",", "."],
|
||||
1: ["", "."],
|
||||
2: [".", ","],
|
||||
3: ["", ","],
|
||||
4: ["'", "."],
|
||||
};
|
||||
const [thousandSep, decimalSep] = separators[nDecSep];
|
||||
|
||||
let decPart = "";
|
||||
if (cConvChar === "f") {
|
||||
if (nPrecision !== undefined) {
|
||||
decPart = (arg - intPart).toFixed(nPrecision);
|
||||
} else {
|
||||
decPart = (arg - intPart).toString();
|
||||
}
|
||||
if (decPart.length > 2) {
|
||||
decPart = `${decimalSep}${decPart.substring(2)}`;
|
||||
} else if (cFlags & HASH) {
|
||||
decPart = ".";
|
||||
} else {
|
||||
decPart = "";
|
||||
}
|
||||
}
|
||||
|
||||
let sign = "";
|
||||
if (intPart < 0) {
|
||||
sign = "-";
|
||||
intPart = -intPart;
|
||||
} else if (cFlags & PLUS) {
|
||||
sign = "+";
|
||||
} else if (cFlags & SPACE) {
|
||||
sign = " ";
|
||||
}
|
||||
|
||||
if (thousandSep && intPart >= 1000) {
|
||||
const buf = [];
|
||||
while (true) {
|
||||
buf.push((intPart % 1000).toString().padStart(3, "0"));
|
||||
intPart = Math.trunc(intPart / 1000);
|
||||
if (intPart < 1000) {
|
||||
buf.push(intPart.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
intPart = buf.reverse().join(thousandSep);
|
||||
} else {
|
||||
intPart = intPart.toString();
|
||||
}
|
||||
|
||||
let n = `${intPart}${decPart}`;
|
||||
if (nWidth !== undefined) {
|
||||
n = n.padStart(nWidth - sign.length, cFlags & ZERO ? "0" : " ");
|
||||
}
|
||||
|
||||
return `${sign}${n}`;
|
||||
});
|
||||
}
|
||||
|
||||
iconStreamFromIcon() {
|
||||
/* Not implemented */
|
||||
}
|
||||
|
||||
printd(cFormat, oDate) {
|
||||
switch (cFormat) {
|
||||
case 0:
|
||||
return this.printd("D:yyyymmddHHMMss", oDate);
|
||||
case 1:
|
||||
return this.printd("yyyy.mm.dd HH:MM:ss", oDate);
|
||||
case 2:
|
||||
return this.printd("m/d/yy h:MM:ss tt", oDate);
|
||||
}
|
||||
|
||||
const handlers = {
|
||||
mmmm: data => {
|
||||
return this._months[data.month];
|
||||
},
|
||||
mmm: data => {
|
||||
return this._months[data.month].substring(0, 3);
|
||||
},
|
||||
mm: data => {
|
||||
return (data.month + 1).toString().padStart(2, "0");
|
||||
},
|
||||
m: data => {
|
||||
return (data.month + 1).toString();
|
||||
},
|
||||
dddd: data => {
|
||||
return this._days[data.dayOfWeek];
|
||||
},
|
||||
ddd: data => {
|
||||
return this._days[data.dayOfWeek].substring(0, 3);
|
||||
},
|
||||
dd: data => {
|
||||
return data.day.toString().padStart(2, "0");
|
||||
},
|
||||
d: data => {
|
||||
return data.day.toString();
|
||||
},
|
||||
yyyy: data => {
|
||||
return data.year.toString();
|
||||
},
|
||||
yy: data => {
|
||||
return (data.year % 100).toString().padStart(2, "0");
|
||||
},
|
||||
HH: data => {
|
||||
return data.hours.toString().padStart(2, "0");
|
||||
},
|
||||
H: data => {
|
||||
return data.hours.toString();
|
||||
},
|
||||
hh: data => {
|
||||
return (1 + ((data.hours + 11) % 12)).toString().padStart(2, "0");
|
||||
},
|
||||
h: data => {
|
||||
return (1 + ((data.hours + 11) % 12)).toString();
|
||||
},
|
||||
MM: data => {
|
||||
return data.minutes.toString().padStart(2, "0");
|
||||
},
|
||||
M: data => {
|
||||
return data.minutes.toString();
|
||||
},
|
||||
ss: data => {
|
||||
return data.seconds.toString().padStart(2, "0");
|
||||
},
|
||||
s: data => {
|
||||
return data.seconds.toString();
|
||||
},
|
||||
tt: data => {
|
||||
return data.hours < 12 ? "am" : "pm";
|
||||
},
|
||||
t: data => {
|
||||
return data.hours < 12 ? "a" : "p";
|
||||
},
|
||||
};
|
||||
|
||||
const data = {
|
||||
year: oDate.getFullYear(),
|
||||
month: oDate.getMonth(),
|
||||
day: oDate.getDate(),
|
||||
dayOfWeek: oDate.getDay(),
|
||||
hours: oDate.getHours(),
|
||||
minutes: oDate.getMinutes(),
|
||||
seconds: oDate.getSeconds(),
|
||||
};
|
||||
|
||||
const patterns = /(mmmm|mmm|mm|m|dddd|ddd|dd|d|yyyy|yy|HH|H|hh|h|MM|M|ss|s|tt|t|\\.)/g;
|
||||
return cFormat.replace(patterns, function (match, pattern) {
|
||||
if (pattern in handlers) {
|
||||
return handlers[pattern](data);
|
||||
}
|
||||
return pattern.charCodeAt(1);
|
||||
});
|
||||
}
|
||||
|
||||
printx(cFormat, cSource) {
|
||||
// case
|
||||
const handlers = [x => x, x => x.toUpperCase(), x => x.toLowerCase()];
|
||||
const buf = [];
|
||||
let i = 0;
|
||||
const ii = cSource.length;
|
||||
let currCase = handlers[0];
|
||||
let escaped = false;
|
||||
|
||||
for (const command of cFormat) {
|
||||
if (escaped) {
|
||||
buf.push(command);
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
if (i >= ii) {
|
||||
break;
|
||||
}
|
||||
switch (command) {
|
||||
case "?":
|
||||
buf.push(currCase(cSource.charAt(i++)));
|
||||
break;
|
||||
case "X":
|
||||
while (i < ii) {
|
||||
const char = cSource.charAt(i++);
|
||||
if (
|
||||
("a" <= char && char <= "z") ||
|
||||
("A" <= char && char <= "Z") ||
|
||||
("0" <= char && char <= "9")
|
||||
) {
|
||||
buf.push(currCase(char));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "A":
|
||||
while (i < ii) {
|
||||
const char = cSource.charAt(i++);
|
||||
if (("a" <= char && char <= "z") || ("A" <= char && char <= "Z")) {
|
||||
buf.push(currCase(char));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "9":
|
||||
while (i < ii) {
|
||||
const char = cSource.charAt(i++);
|
||||
if ("0" <= char && char <= "9") {
|
||||
buf.push(char);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "*":
|
||||
while (i < ii) {
|
||||
buf.push(currCase(cSource.charAt(i++)));
|
||||
}
|
||||
break;
|
||||
case "\\":
|
||||
escaped = true;
|
||||
break;
|
||||
case ">":
|
||||
currCase = handlers[1];
|
||||
break;
|
||||
case "<":
|
||||
currCase = handlers[2];
|
||||
break;
|
||||
case "=":
|
||||
currCase = handlers[0];
|
||||
break;
|
||||
default:
|
||||
buf.push(command);
|
||||
}
|
||||
}
|
||||
|
||||
return buf.join("");
|
||||
}
|
||||
|
||||
scand(cFormat, cDate) {
|
||||
switch (cFormat) {
|
||||
case 0:
|
||||
return this.scand("D:yyyymmddHHMMss", cDate);
|
||||
case 1:
|
||||
return this.scand("yyyy.mm.dd HH:MM:ss", cDate);
|
||||
case 2:
|
||||
return this.scand("m/d/yy h:MM:ss tt", cDate);
|
||||
}
|
||||
|
||||
if (!this._scandCache.has(cFormat)) {
|
||||
const months = this._months;
|
||||
const days = this._days;
|
||||
|
||||
const handlers = {
|
||||
mmmm: {
|
||||
pattern: `(${months.join("|")})`,
|
||||
action: (value, data) => {
|
||||
data.month = months.indexOf(value);
|
||||
},
|
||||
},
|
||||
mmm: {
|
||||
pattern: `(${months.map(month => month.substring(0, 3)).join("|")})`,
|
||||
action: (value, data) => {
|
||||
data.month = months.findIndex(
|
||||
month => month.substring(0, 3) === value
|
||||
);
|
||||
},
|
||||
},
|
||||
mm: {
|
||||
pattern: `([0-9]{2})`,
|
||||
action: (value, data) => {
|
||||
data.month = parseInt(value) - 1;
|
||||
},
|
||||
},
|
||||
m: {
|
||||
pattern: `([0-9]{1,2})`,
|
||||
action: (value, data) => {
|
||||
data.month = parseInt(value) - 1;
|
||||
},
|
||||
},
|
||||
dddd: {
|
||||
pattern: `(${days.join("|")})`,
|
||||
action: (value, data) => {
|
||||
data.day = days.indexOf(value);
|
||||
},
|
||||
},
|
||||
ddd: {
|
||||
pattern: `(${days.map(day => day.substring(0, 3)).join("|")})`,
|
||||
action: (value, data) => {
|
||||
data.day = days.findIndex(day => day.substring(0, 3) === value);
|
||||
},
|
||||
},
|
||||
dd: {
|
||||
pattern: "([0-9]{2})",
|
||||
action: (value, data) => {
|
||||
data.day = parseInt(value);
|
||||
},
|
||||
},
|
||||
d: {
|
||||
pattern: "([0-9]{1,2})",
|
||||
action: (value, data) => {
|
||||
data.day = parseInt(value);
|
||||
},
|
||||
},
|
||||
yyyy: {
|
||||
pattern: "([0-9]{4})",
|
||||
action: (value, data) => {
|
||||
data.year = parseInt(value);
|
||||
},
|
||||
},
|
||||
yy: {
|
||||
pattern: "([0-9]{2})",
|
||||
action: (value, data) => {
|
||||
data.year = 2000 + parseInt(value);
|
||||
},
|
||||
},
|
||||
HH: {
|
||||
pattern: "([0-9]{2})",
|
||||
action: (value, data) => {
|
||||
data.hours = parseInt(value);
|
||||
},
|
||||
},
|
||||
H: {
|
||||
pattern: "([0-9]{1,2})",
|
||||
action: (value, data) => {
|
||||
data.hours = parseInt(value);
|
||||
},
|
||||
},
|
||||
hh: {
|
||||
pattern: "([0-9]{2})",
|
||||
action: (value, data) => {
|
||||
data.hours = parseInt(value);
|
||||
},
|
||||
},
|
||||
h: {
|
||||
pattern: "([0-9]{1,2})",
|
||||
action: (value, data) => {
|
||||
data.hours = parseInt(value);
|
||||
},
|
||||
},
|
||||
MM: {
|
||||
pattern: "([0-9]{2})",
|
||||
action: (value, data) => {
|
||||
data.minutes = parseInt(value);
|
||||
},
|
||||
},
|
||||
M: {
|
||||
pattern: "([0-9]{1,2})",
|
||||
action: (value, data) => {
|
||||
data.minutes = parseInt(value);
|
||||
},
|
||||
},
|
||||
ss: {
|
||||
pattern: "([0-9]{2})",
|
||||
action: (value, data) => {
|
||||
data.seconds = parseInt(value);
|
||||
},
|
||||
},
|
||||
s: {
|
||||
pattern: "([0-9]{1,2})",
|
||||
action: (value, data) => {
|
||||
data.seconds = parseInt(value);
|
||||
},
|
||||
},
|
||||
tt: {
|
||||
pattern: "([aApP][mM])",
|
||||
action: (value, data) => {
|
||||
const char = value.charAt(0);
|
||||
data.am = char === "a" || char === "A";
|
||||
},
|
||||
},
|
||||
t: {
|
||||
pattern: "([aApP])",
|
||||
action: (value, data) => {
|
||||
data.am = value === "a" || value === "A";
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// escape the string
|
||||
const escapedFormat = cFormat.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
|
||||
const patterns = /(mmmm|mmm|mm|m|dddd|ddd|dd|d|yyyy|yy|HH|H|hh|h|MM|M|ss|s|tt|t)/g;
|
||||
const actions = [];
|
||||
|
||||
const re = escapedFormat.replace(patterns, function (
|
||||
match,
|
||||
patternElement
|
||||
) {
|
||||
const { pattern, action } = handlers[patternElement];
|
||||
actions.push(action);
|
||||
return pattern;
|
||||
});
|
||||
|
||||
this._scandCache.set(cFormat, [new RegExp(re, "g"), actions]);
|
||||
}
|
||||
|
||||
const [regexForFormat, actions] = this._scandCache.get(cFormat);
|
||||
|
||||
const matches = regexForFormat.exec(cDate);
|
||||
if (matches.length !== actions.length + 1) {
|
||||
throw new Error("Invalid date in util.scand");
|
||||
}
|
||||
|
||||
const data = {
|
||||
year: 0,
|
||||
month: 0,
|
||||
day: 0,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
seconds: 0,
|
||||
am: null,
|
||||
};
|
||||
actions.forEach((action, i) => action(matches[i + 1], data));
|
||||
if (data.am !== null) {
|
||||
data.hours = (data.hours % 12) + (data.am ? 0 : 12);
|
||||
}
|
||||
|
||||
return new Date(
|
||||
data.year,
|
||||
data.month,
|
||||
data.day,
|
||||
data.hours,
|
||||
data.minutes,
|
||||
data.seconds
|
||||
);
|
||||
}
|
||||
|
||||
spansToXML() {
|
||||
/* Not implemented */
|
||||
}
|
||||
|
||||
stringFromStream() {
|
||||
/* Not implemented */
|
||||
}
|
||||
|
||||
xmlToSpans() {
|
||||
/* Not implemented */
|
||||
}
|
||||
}
|
||||
|
||||
export { Util };
|
||||
|
@ -32,6 +32,7 @@
|
||||
"pdf_find_utils_spec.js",
|
||||
"pdf_history_spec.js",
|
||||
"primitives_spec.js",
|
||||
"scripting_spec.js",
|
||||
"stream_spec.js",
|
||||
"type1_parser_spec.js",
|
||||
"ui_utils_spec.js",
|
||||
|
@ -77,6 +77,7 @@ async function initializePDFJS(callback) {
|
||||
"pdfjs-test/unit/pdf_find_utils_spec.js",
|
||||
"pdfjs-test/unit/pdf_history_spec.js",
|
||||
"pdfjs-test/unit/primitives_spec.js",
|
||||
"pdfjs-test/unit/scripting_spec.js",
|
||||
"pdfjs-test/unit/stream_spec.js",
|
||||
"pdfjs-test/unit/type1_parser_spec.js",
|
||||
"pdfjs-test/unit/ui_utils_spec.js",
|
||||
|
98
test/unit/scripting_spec.js
Normal file
98
test/unit/scripting_spec.js
Normal file
@ -0,0 +1,98 @@
|
||||
/* Copyright 2020 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { initSandbox } from "../../src/scripting_api/initialization.js";
|
||||
|
||||
describe("Util", function () {
|
||||
let sandbox, util;
|
||||
|
||||
beforeAll(function (done) {
|
||||
sandbox = Object.create(null);
|
||||
const extra = { send: null, crackURL: null };
|
||||
const data = { objects: {}, calculationOrder: [] };
|
||||
initSandbox(data, extra, sandbox);
|
||||
util = sandbox.util;
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(function () {
|
||||
sandbox = util = null;
|
||||
});
|
||||
|
||||
describe("printd", function () {
|
||||
it("should print a date according to a format", function (done) {
|
||||
const date = new Date("April 15, 1707 3:14:15");
|
||||
expect(util.printd(0, date)).toEqual("D:17070415031415");
|
||||
expect(util.printd(1, date)).toEqual("1707.04.15 03:14:15");
|
||||
expect(util.printd(2, date)).toEqual("4/15/07 3:14:15 am");
|
||||
expect(util.printd("mmmm mmm mm m", date)).toEqual("April Apr 04 4");
|
||||
expect(util.printd("dddd ddd dd d", date)).toEqual("Friday Fri 15 15");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe("scand", function () {
|
||||
it("should parse a date according to a format", function (done) {
|
||||
const date = new Date("April 15, 1707 3:14:15");
|
||||
expect(util.scand(0, "D:17070415031415")).toEqual(date);
|
||||
expect(util.scand(1, "1707.04.15 03:14:15")).toEqual(date);
|
||||
expect(util.scand(2, "4/15/07 3:14:15 am")).toEqual(
|
||||
new Date("April 15, 2007 3:14:15")
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe("printf", function () {
|
||||
it("should print some data according to a format", function (done) {
|
||||
expect(util.printf("Integer numbers: %d, %d,...", 1.234, 56.789)).toEqual(
|
||||
"Integer numbers: 1, 56,..."
|
||||
);
|
||||
expect(util.printf("Hex numbers: %x, %x,...", 1234, 56789)).toEqual(
|
||||
"Hex numbers: 4D2, DDD5,..."
|
||||
);
|
||||
expect(
|
||||
util.printf("Hex numbers with 0x: %#x, %#x,...", 1234, 56789)
|
||||
).toEqual("Hex numbers with 0x: 0x4D2, 0xDDD5,...");
|
||||
expect(util.printf("Decimal number: %,0+.3f", 1234567.89123)).toEqual(
|
||||
"Decimal number: +1,234,567.891"
|
||||
);
|
||||
expect(util.printf("Decimal number: %,0+8.3f", 1.234567)).toEqual(
|
||||
"Decimal number: + 1.235"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
it("should print a string with no argument", function (done) {
|
||||
expect(util.printf("hello world")).toEqual("hello world");
|
||||
done();
|
||||
});
|
||||
|
||||
it("should print a string with a percent", function (done) {
|
||||
expect(util.printf("%%s")).toEqual("%%s");
|
||||
expect(util.printf("%%s", "hello")).toEqual("%%s");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe("printx", function () {
|
||||
it("should print some data according to a format", function (done) {
|
||||
expect(util.printx("9 (999) 999-9999", "aaa14159697489zzz")).toEqual(
|
||||
"1 (415) 969-7489"
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user