Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate additional font-dictionary properties #18014

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/core/core_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,21 @@ function isWhiteSpace(ch) {
return ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a;
}

/**
* Checks if something is an Array containing only numbers,
* and (optionally) checks its length.
* @param {any} arr
* @param {number | null} len
* @returns {boolean}
*/
function isNumberArray(arr, len) {
return (
Array.isArray(arr) &&
(len === null || arr.length === len) &&
arr.every(x => typeof x === "number")
);
}

/**
* AcroForm field names use an array like notation to refer to
* repeated XFA elements e.g. foo.bar[nnn].
Expand Down Expand Up @@ -637,6 +652,7 @@ export {
getRotationMatrix,
getSizeInBytes,
isAscii,
isNumberArray,
isWhiteSpace,
log2,
MissingDataException,
Expand Down
65 changes: 50 additions & 15 deletions src/core/evaluator.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import { getGlyphsUnicode } from "./glyphlist.js";
import { getMetrics } from "./metrics.js";
import { getUnicodeForGlyph } from "./unicode.js";
import { ImageResizer } from "./image_resizer.js";
import { isNumberArray } from "./core_utils.js";
import { MurmurHash3_64 } from "../shared/murmurhash3.js";
import { OperatorList } from "./operator_list.js";
import { PDFImage } from "./image.js";
Expand Down Expand Up @@ -4077,8 +4078,14 @@ class PartialEvaluator {
composite = true;
}

const firstChar = dict.get("FirstChar") || 0,
lastChar = dict.get("LastChar") || (composite ? 0xffff : 0xff);
let firstChar = dict.get("FirstChar");
if (!Number.isInteger(firstChar)) {
firstChar = 0;
}
let lastChar = dict.get("LastChar");
if (!Number.isInteger(lastChar)) {
lastChar = composite ? 0xffff : 0xff;
}
const descriptor = dict.get("FontDescriptor");
const toUnicode = dict.get("ToUnicode") || baseDict.get("ToUnicode");

Expand Down Expand Up @@ -4206,11 +4213,15 @@ class PartialEvaluator {

if (!descriptor) {
if (isType3Font) {
let bbox = dict.getArray("FontBBox");
if (!isNumberArray(bbox, 4)) {
bbox = [0, 0, 0, 0];
}
// FontDescriptor is only required for Type3 fonts when the document
// is a tagged pdf. Create a barbebones one to get by.
descriptor = new Dict(null);
descriptor.set("FontName", Name.get(type));
descriptor.set("FontBBox", dict.getArray("FontBBox") || [0, 0, 0, 0]);
descriptor.set("FontBBox", bbox);
} else {
// Before PDF 1.5 if the font was one of the base 14 fonts, having a
// FontDescriptor was not required.
Expand Down Expand Up @@ -4392,13 +4403,37 @@ class PartialEvaluator {
}

let fontMatrix = dict.getArray("FontMatrix");
if (
!Array.isArray(fontMatrix) ||
fontMatrix.length !== 6 ||
fontMatrix.some(x => typeof x !== "number")
) {
if (!isNumberArray(fontMatrix, 6)) {
fontMatrix = FONT_IDENTITY_MATRIX;
}
let bbox = descriptor.getArray("FontBBox") || dict.getArray("FontBBox");
if (!isNumberArray(bbox, 4)) {
bbox = undefined;
}
let ascent = descriptor.get("Ascent");
if (typeof ascent !== "number") {
ascent = undefined;
}
let descent = descriptor.get("Descent");
if (typeof descent !== "number") {
descent = undefined;
}
let xHeight = descriptor.get("XHeight");
if (typeof xHeight !== "number") {
xHeight = 0;
}
let capHeight = descriptor.get("CapHeight");
if (typeof capHeight !== "number") {
capHeight = 0;
}
let flags = descriptor.get("Flags");
if (!Number.isInteger(flags)) {
flags = 0;
}
let italicAngle = descriptor.get("ItalicAngle");
if (typeof italicAngle !== "number") {
italicAngle = 0;
}

const properties = {
type,
Expand All @@ -4416,13 +4451,13 @@ class PartialEvaluator {
firstChar,
lastChar,
toUnicode,
bbox: descriptor.getArray("FontBBox") || dict.getArray("FontBBox"),
ascent: descriptor.get("Ascent"),
descent: descriptor.get("Descent"),
xHeight: descriptor.get("XHeight") || 0,
capHeight: descriptor.get("CapHeight") || 0,
flags: descriptor.get("Flags"),
italicAngle: descriptor.get("ItalicAngle") || 0,
bbox,
ascent,
descent,
xHeight,
capHeight,
flags,
italicAngle,
isType3Font,
cssFontInfo,
scaleFactors: glyphScaleFactors,
Expand Down
3 changes: 2 additions & 1 deletion src/core/font_renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from "../shared/util.js";
import { CFFParser } from "./cff_parser.js";
import { getGlyphsUnicode } from "./glyphlist.js";
import { isNumberArray } from "./core_utils.js";
import { StandardEncoding } from "./encodings.js";
import { Stream } from "./stream.js";

Expand Down Expand Up @@ -750,7 +751,7 @@ class Commands {

add(cmd, args) {
if (args) {
if (args.some(arg => typeof arg !== "number")) {
if (!isNumberArray(args, null)) {
warn(
`Commands.add - "${cmd}" has at least one non-number arg: "${args}".`
);
Expand Down
14 changes: 4 additions & 10 deletions src/core/function.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from "../shared/util.js";
import { PostScriptLexer, PostScriptParser } from "./ps_parser.js";
import { BaseStream } from "./base_stream.js";
import { isNumberArray } from "./core_utils.js";
import { LocalFunctionCache } from "./image_utils.js";

class PDFFunctionFactory {
Expand Down Expand Up @@ -117,16 +118,9 @@ function toNumberArray(arr) {
if (!Array.isArray(arr)) {
return null;
}
const length = arr.length;
for (let i = 0; i < length; i++) {
if (typeof arr[i] !== "number") {
// Non-number is found -- convert all items to numbers.
const result = new Array(length);
for (let j = 0; j < length; j++) {
result[j] = +arr[j];
}
return result;
}
if (!isNumberArray(arr, null)) {
// Non-number is found -- convert all items to numbers.
return arr.map(x => +x);
}
return arr;
}
Expand Down