Validate additional font-dictionary properties

This commit is contained in:
Jonas Jenwald 2024-04-28 11:50:57 +02:00
parent 85e64b5c16
commit 08eb0566f7
4 changed files with 72 additions and 26 deletions

View file

@ -218,6 +218,21 @@ function isWhiteSpace(ch) {
return ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a; 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 * AcroForm field names use an array like notation to refer to
* repeated XFA elements e.g. foo.bar[nnn]. * repeated XFA elements e.g. foo.bar[nnn].
@ -637,6 +652,7 @@ export {
getRotationMatrix, getRotationMatrix,
getSizeInBytes, getSizeInBytes,
isAscii, isAscii,
isNumberArray,
isWhiteSpace, isWhiteSpace,
log2, log2,
MissingDataException, MissingDataException,

View file

@ -73,6 +73,7 @@ import { getGlyphsUnicode } from "./glyphlist.js";
import { getMetrics } from "./metrics.js"; import { getMetrics } from "./metrics.js";
import { getUnicodeForGlyph } from "./unicode.js"; import { getUnicodeForGlyph } from "./unicode.js";
import { ImageResizer } from "./image_resizer.js"; import { ImageResizer } from "./image_resizer.js";
import { isNumberArray } from "./core_utils.js";
import { MurmurHash3_64 } from "../shared/murmurhash3.js"; import { MurmurHash3_64 } from "../shared/murmurhash3.js";
import { OperatorList } from "./operator_list.js"; import { OperatorList } from "./operator_list.js";
import { PDFImage } from "./image.js"; import { PDFImage } from "./image.js";
@ -4077,8 +4078,14 @@ class PartialEvaluator {
composite = true; composite = true;
} }
const firstChar = dict.get("FirstChar") || 0, let firstChar = dict.get("FirstChar");
lastChar = dict.get("LastChar") || (composite ? 0xffff : 0xff); 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 descriptor = dict.get("FontDescriptor");
const toUnicode = dict.get("ToUnicode") || baseDict.get("ToUnicode"); const toUnicode = dict.get("ToUnicode") || baseDict.get("ToUnicode");
@ -4206,11 +4213,15 @@ class PartialEvaluator {
if (!descriptor) { if (!descriptor) {
if (isType3Font) { 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 // FontDescriptor is only required for Type3 fonts when the document
// is a tagged pdf. Create a barbebones one to get by. // is a tagged pdf. Create a barbebones one to get by.
descriptor = new Dict(null); descriptor = new Dict(null);
descriptor.set("FontName", Name.get(type)); descriptor.set("FontName", Name.get(type));
descriptor.set("FontBBox", dict.getArray("FontBBox") || [0, 0, 0, 0]); descriptor.set("FontBBox", bbox);
} else { } else {
// Before PDF 1.5 if the font was one of the base 14 fonts, having a // Before PDF 1.5 if the font was one of the base 14 fonts, having a
// FontDescriptor was not required. // FontDescriptor was not required.
@ -4392,13 +4403,37 @@ class PartialEvaluator {
} }
let fontMatrix = dict.getArray("FontMatrix"); let fontMatrix = dict.getArray("FontMatrix");
if ( if (!isNumberArray(fontMatrix, 6)) {
!Array.isArray(fontMatrix) ||
fontMatrix.length !== 6 ||
fontMatrix.some(x => typeof x !== "number")
) {
fontMatrix = FONT_IDENTITY_MATRIX; 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 = { const properties = {
type, type,
@ -4416,13 +4451,13 @@ class PartialEvaluator {
firstChar, firstChar,
lastChar, lastChar,
toUnicode, toUnicode,
bbox: descriptor.getArray("FontBBox") || dict.getArray("FontBBox"), bbox,
ascent: descriptor.get("Ascent"), ascent,
descent: descriptor.get("Descent"), descent,
xHeight: descriptor.get("XHeight") || 0, xHeight,
capHeight: descriptor.get("CapHeight") || 0, capHeight,
flags: descriptor.get("Flags"), flags,
italicAngle: descriptor.get("ItalicAngle") || 0, italicAngle,
isType3Font, isType3Font,
cssFontInfo, cssFontInfo,
scaleFactors: glyphScaleFactors, scaleFactors: glyphScaleFactors,

View file

@ -23,6 +23,7 @@ import {
} from "../shared/util.js"; } from "../shared/util.js";
import { CFFParser } from "./cff_parser.js"; import { CFFParser } from "./cff_parser.js";
import { getGlyphsUnicode } from "./glyphlist.js"; import { getGlyphsUnicode } from "./glyphlist.js";
import { isNumberArray } from "./core_utils.js";
import { StandardEncoding } from "./encodings.js"; import { StandardEncoding } from "./encodings.js";
import { Stream } from "./stream.js"; import { Stream } from "./stream.js";
@ -750,7 +751,7 @@ class Commands {
add(cmd, args) { add(cmd, args) {
if (args) { if (args) {
if (args.some(arg => typeof arg !== "number")) { if (!isNumberArray(args, null)) {
warn( warn(
`Commands.add - "${cmd}" has at least one non-number arg: "${args}".` `Commands.add - "${cmd}" has at least one non-number arg: "${args}".`
); );

View file

@ -23,6 +23,7 @@ import {
} from "../shared/util.js"; } from "../shared/util.js";
import { PostScriptLexer, PostScriptParser } from "./ps_parser.js"; import { PostScriptLexer, PostScriptParser } from "./ps_parser.js";
import { BaseStream } from "./base_stream.js"; import { BaseStream } from "./base_stream.js";
import { isNumberArray } from "./core_utils.js";
import { LocalFunctionCache } from "./image_utils.js"; import { LocalFunctionCache } from "./image_utils.js";
class PDFFunctionFactory { class PDFFunctionFactory {
@ -117,16 +118,9 @@ function toNumberArray(arr) {
if (!Array.isArray(arr)) { if (!Array.isArray(arr)) {
return null; return null;
} }
const length = arr.length; if (!isNumberArray(arr, null)) {
for (let i = 0; i < length; i++) { // Non-number is found -- convert all items to numbers.
if (typeof arr[i] !== "number") { return arr.map(x => +x);
// 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;
}
} }
return arr; return arr;
} }