Skip to content

Commit 08eb056

Browse files
committed
Validate additional font-dictionary properties
1 parent 85e64b5 commit 08eb056

File tree

4 files changed

+72
-26
lines changed

4 files changed

+72
-26
lines changed

src/core/core_utils.js

+16
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,21 @@ function isWhiteSpace(ch) {
218218
return ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a;
219219
}
220220

221+
/**
222+
* Checks if something is an Array containing only numbers,
223+
* and (optionally) checks its length.
224+
* @param {any} arr
225+
* @param {number | null} len
226+
* @returns {boolean}
227+
*/
228+
function isNumberArray(arr, len) {
229+
return (
230+
Array.isArray(arr) &&
231+
(len === null || arr.length === len) &&
232+
arr.every(x => typeof x === "number")
233+
);
234+
}
235+
221236
/**
222237
* AcroForm field names use an array like notation to refer to
223238
* repeated XFA elements e.g. foo.bar[nnn].
@@ -637,6 +652,7 @@ export {
637652
getRotationMatrix,
638653
getSizeInBytes,
639654
isAscii,
655+
isNumberArray,
640656
isWhiteSpace,
641657
log2,
642658
MissingDataException,

src/core/evaluator.js

+50-15
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import { getGlyphsUnicode } from "./glyphlist.js";
7373
import { getMetrics } from "./metrics.js";
7474
import { getUnicodeForGlyph } from "./unicode.js";
7575
import { ImageResizer } from "./image_resizer.js";
76+
import { isNumberArray } from "./core_utils.js";
7677
import { MurmurHash3_64 } from "../shared/murmurhash3.js";
7778
import { OperatorList } from "./operator_list.js";
7879
import { PDFImage } from "./image.js";
@@ -4077,8 +4078,14 @@ class PartialEvaluator {
40774078
composite = true;
40784079
}
40794080

4080-
const firstChar = dict.get("FirstChar") || 0,
4081-
lastChar = dict.get("LastChar") || (composite ? 0xffff : 0xff);
4081+
let firstChar = dict.get("FirstChar");
4082+
if (!Number.isInteger(firstChar)) {
4083+
firstChar = 0;
4084+
}
4085+
let lastChar = dict.get("LastChar");
4086+
if (!Number.isInteger(lastChar)) {
4087+
lastChar = composite ? 0xffff : 0xff;
4088+
}
40824089
const descriptor = dict.get("FontDescriptor");
40834090
const toUnicode = dict.get("ToUnicode") || baseDict.get("ToUnicode");
40844091

@@ -4206,11 +4213,15 @@ class PartialEvaluator {
42064213

42074214
if (!descriptor) {
42084215
if (isType3Font) {
4216+
let bbox = dict.getArray("FontBBox");
4217+
if (!isNumberArray(bbox, 4)) {
4218+
bbox = [0, 0, 0, 0];
4219+
}
42094220
// FontDescriptor is only required for Type3 fonts when the document
42104221
// is a tagged pdf. Create a barbebones one to get by.
42114222
descriptor = new Dict(null);
42124223
descriptor.set("FontName", Name.get(type));
4213-
descriptor.set("FontBBox", dict.getArray("FontBBox") || [0, 0, 0, 0]);
4224+
descriptor.set("FontBBox", bbox);
42144225
} else {
42154226
// Before PDF 1.5 if the font was one of the base 14 fonts, having a
42164227
// FontDescriptor was not required.
@@ -4392,13 +4403,37 @@ class PartialEvaluator {
43924403
}
43934404

43944405
let fontMatrix = dict.getArray("FontMatrix");
4395-
if (
4396-
!Array.isArray(fontMatrix) ||
4397-
fontMatrix.length !== 6 ||
4398-
fontMatrix.some(x => typeof x !== "number")
4399-
) {
4406+
if (!isNumberArray(fontMatrix, 6)) {
44004407
fontMatrix = FONT_IDENTITY_MATRIX;
44014408
}
4409+
let bbox = descriptor.getArray("FontBBox") || dict.getArray("FontBBox");
4410+
if (!isNumberArray(bbox, 4)) {
4411+
bbox = undefined;
4412+
}
4413+
let ascent = descriptor.get("Ascent");
4414+
if (typeof ascent !== "number") {
4415+
ascent = undefined;
4416+
}
4417+
let descent = descriptor.get("Descent");
4418+
if (typeof descent !== "number") {
4419+
descent = undefined;
4420+
}
4421+
let xHeight = descriptor.get("XHeight");
4422+
if (typeof xHeight !== "number") {
4423+
xHeight = 0;
4424+
}
4425+
let capHeight = descriptor.get("CapHeight");
4426+
if (typeof capHeight !== "number") {
4427+
capHeight = 0;
4428+
}
4429+
let flags = descriptor.get("Flags");
4430+
if (!Number.isInteger(flags)) {
4431+
flags = 0;
4432+
}
4433+
let italicAngle = descriptor.get("ItalicAngle");
4434+
if (typeof italicAngle !== "number") {
4435+
italicAngle = 0;
4436+
}
44024437

44034438
const properties = {
44044439
type,
@@ -4416,13 +4451,13 @@ class PartialEvaluator {
44164451
firstChar,
44174452
lastChar,
44184453
toUnicode,
4419-
bbox: descriptor.getArray("FontBBox") || dict.getArray("FontBBox"),
4420-
ascent: descriptor.get("Ascent"),
4421-
descent: descriptor.get("Descent"),
4422-
xHeight: descriptor.get("XHeight") || 0,
4423-
capHeight: descriptor.get("CapHeight") || 0,
4424-
flags: descriptor.get("Flags"),
4425-
italicAngle: descriptor.get("ItalicAngle") || 0,
4454+
bbox,
4455+
ascent,
4456+
descent,
4457+
xHeight,
4458+
capHeight,
4459+
flags,
4460+
italicAngle,
44264461
isType3Font,
44274462
cssFontInfo,
44284463
scaleFactors: glyphScaleFactors,

src/core/font_renderer.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from "../shared/util.js";
2424
import { CFFParser } from "./cff_parser.js";
2525
import { getGlyphsUnicode } from "./glyphlist.js";
26+
import { isNumberArray } from "./core_utils.js";
2627
import { StandardEncoding } from "./encodings.js";
2728
import { Stream } from "./stream.js";
2829

@@ -750,7 +751,7 @@ class Commands {
750751

751752
add(cmd, args) {
752753
if (args) {
753-
if (args.some(arg => typeof arg !== "number")) {
754+
if (!isNumberArray(args, null)) {
754755
warn(
755756
`Commands.add - "${cmd}" has at least one non-number arg: "${args}".`
756757
);

src/core/function.js

+4-10
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from "../shared/util.js";
2424
import { PostScriptLexer, PostScriptParser } from "./ps_parser.js";
2525
import { BaseStream } from "./base_stream.js";
26+
import { isNumberArray } from "./core_utils.js";
2627
import { LocalFunctionCache } from "./image_utils.js";
2728

2829
class PDFFunctionFactory {
@@ -117,16 +118,9 @@ function toNumberArray(arr) {
117118
if (!Array.isArray(arr)) {
118119
return null;
119120
}
120-
const length = arr.length;
121-
for (let i = 0; i < length; i++) {
122-
if (typeof arr[i] !== "number") {
123-
// Non-number is found -- convert all items to numbers.
124-
const result = new Array(length);
125-
for (let j = 0; j < length; j++) {
126-
result[j] = +arr[j];
127-
}
128-
return result;
129-
}
121+
if (!isNumberArray(arr, null)) {
122+
// Non-number is found -- convert all items to numbers.
123+
return arr.map(x => +x);
130124
}
131125
return arr;
132126
}

0 commit comments

Comments
 (0)