From 52f7ff155d554742540c13bc6b2175c74c1f53d1 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Mon, 29 Apr 2024 23:07:41 +0200 Subject: [PATCH 1/2] Validate even more dictionary properties This checks primarily Arrays, but also some other properties, that we'll end up sending (sometimes indirectly) to the main-thread. --- src/core/annotation.js | 64 ++++++++++++++++++---------- src/core/catalog.js | 4 +- src/core/core_utils.js | 16 +++++++ src/core/document.js | 3 +- src/core/evaluator.js | 17 ++++---- src/core/pattern.js | 78 ++++++++++++++++++++++------------- src/display/canvas.js | 5 +-- src/display/pattern_helper.js | 2 +- 8 files changed, 125 insertions(+), 64 deletions(-) diff --git a/src/core/annotation.js b/src/core/annotation.js index 4efe7ae2f..2995965dd 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -43,6 +43,7 @@ import { getInheritableProperty, getRotationMatrix, isAscii, + isNumberArray, numberToString, stringToUTF16String, } from "./core_utils.js"; @@ -550,7 +551,7 @@ function getQuadPoints(dict, rect) { // Each quadrilateral must consist of eight coordinates. const quadPoints = dict.getArray("QuadPoints"); if ( - !Array.isArray(quadPoints) || + !isNumberArray(quadPoints, null) || quadPoints.length === 0 || quadPoints.length % 8 > 0 ) { @@ -914,10 +915,9 @@ class Annotation { * @param {Array} rectangle - The rectangle array with exactly four entries */ setRectangle(rectangle) { - this.rectangle = - Array.isArray(rectangle) && rectangle.length === 4 - ? Util.normalizeRect(rectangle) - : [0, 0, 0, 0]; + this.rectangle = isNumberArray(rectangle, 4) + ? Util.normalizeRect(rectangle) + : [0, 0, 0, 0]; } /** @@ -1150,8 +1150,14 @@ class Annotation { ["ExtGState", "ColorSpace", "Pattern", "Shading", "XObject", "Font"], appearance ); - const bbox = appearanceDict.getArray("BBox") || [0, 0, 1, 1]; - const matrix = appearanceDict.getArray("Matrix") || [1, 0, 0, 1, 0, 0]; + let bbox = appearanceDict.getArray("BBox"); + if (!isNumberArray(bbox, 4)) { + bbox = [0, 0, 1, 1]; + } + let matrix = appearanceDict.getArray("Matrix"); + if (!isNumberArray(matrix, 6)) { + matrix = [1, 0, 0, 1, 0, 0]; + } const transform = getTransformMatrix(rect, bbox, matrix); const opList = new OperatorList(); @@ -1248,10 +1254,19 @@ class Annotation { if (text.length > 1 || text[0]) { const appearanceDict = this.appearance.dict; + let bbox = appearanceDict.getArray("BBox"); + if (!isNumberArray(bbox, 4)) { + bbox = null; + } + let matrix = appearanceDict.getArray("Matrix"); + if (!isNumberArray(matrix, 6)) { + matrix = null; + } + this.data.textPosition = this._transformPoint( firstPosition, - appearanceDict.getArray("BBox"), - appearanceDict.getArray("Matrix") + bbox, + matrix ); this.data.textContent = text; } @@ -1401,7 +1416,7 @@ class AnnotationBorderStyle { setWidth(width, rect = [0, 0, 0, 0]) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { assert( - Array.isArray(rect) && rect.length === 4, + isNumberArray(rect, 4), "A valid `rect` parameter must be provided." ); } @@ -2972,7 +2987,8 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { : this.uncheckedAppearance; if (appearance) { const savedAppearance = this.appearance; - const savedMatrix = appearance.dict.getArray("Matrix") || IDENTITY_MATRIX; + const matrix = appearance.dict.getArray("Matrix"); + const savedMatrix = isNumberArray(matrix, 6) ? matrix : IDENTITY_MATRIX; if (rotation) { appearance.dict.set( @@ -3739,10 +3755,9 @@ class PopupAnnotation extends Annotation { } const parentRect = parentItem.getArray("Rect"); - this.data.parentRect = - Array.isArray(parentRect) && parentRect.length === 4 - ? Util.normalizeRect(parentRect) - : null; + this.data.parentRect = isNumberArray(parentRect, 4) + ? Util.normalizeRect(parentRect) + : null; const rt = parentItem.get("RT"); if (isName(rt, AnnotationReplyType.GROUP)) { @@ -4030,7 +4045,10 @@ class LineAnnotation extends MarkupAnnotation { this.data.hasOwnCanvas = this.data.noRotate; this.data.noHTML = false; - const lineCoordinates = dict.getArray("L"); + let lineCoordinates = dict.getArray("L"); + if (!isNumberArray(lineCoordinates, 4)) { + lineCoordinates = [0, 0, 0, 0]; + } this.data.lineCoordinates = Util.normalizeRect(lineCoordinates); if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) { @@ -4225,7 +4243,7 @@ class PolylineAnnotation extends MarkupAnnotation { // horizontal and vertical coordinates, respectively, of each vertex. // Convert this to an array of objects with x and y coordinates. const rawVertices = dict.getArray("Vertices"); - if (!Array.isArray(rawVertices)) { + if (!isNumberArray(rawVertices, null)) { return; } for (let i = 0, ii = rawVertices.length; i < ii; i += 2) { @@ -4314,11 +4332,15 @@ class InkAnnotation extends MarkupAnnotation { // of each vertex. Convert this to an array of objects with x and y // coordinates. this.data.inkLists.push([]); + if (!Array.isArray(rawInkLists[i])) { + continue; + } for (let j = 0, jj = rawInkLists[i].length; j < jj; j += 2) { - this.data.inkLists[i].push({ - x: xref.fetchIfRef(rawInkLists[i][j]), - y: xref.fetchIfRef(rawInkLists[i][j + 1]), - }); + const x = xref.fetchIfRef(rawInkLists[i][j]), + y = xref.fetchIfRef(rawInkLists[i][j + 1]); + if (typeof x === "number" && typeof y === "number") { + this.data.inkLists[i].push({ x, y }); + } } } diff --git a/src/core/catalog.js b/src/core/catalog.js index f2089ebcc..5d77972aa 100644 --- a/src/core/catalog.js +++ b/src/core/catalog.js @@ -15,6 +15,7 @@ import { collectActions, + isNumberArray, MissingDataException, PDF_VERSION_REGEXP, recoverJsURL, @@ -388,8 +389,7 @@ class Catalog { // We only need to parse the color when it's valid, and non-default. if ( - Array.isArray(color) && - color.length === 3 && + isNumberArray(color, 3) && (color[0] !== 0 || color[1] !== 0 || color[2] !== 0) ) { rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0); diff --git a/src/core/core_utils.js b/src/core/core_utils.js index c6ddb8f84..e62efc123 100644 --- a/src/core/core_utils.js +++ b/src/core/core_utils.js @@ -218,6 +218,21 @@ function isWhiteSpace(ch) { return ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a; } +/** + * Checks if something is an Array containing only boolean values, + * and (optionally) checks its length. + * @param {any} arr + * @param {number | null} len + * @returns {boolean} + */ +function isBooleanArray(arr, len) { + return ( + Array.isArray(arr) && + (len === null || arr.length === len) && + arr.every(x => typeof x === "boolean") + ); +} + /** * Checks if something is an Array containing only numbers, * and (optionally) checks its length. @@ -652,6 +667,7 @@ export { getRotationMatrix, getSizeInBytes, isAscii, + isBooleanArray, isNumberArray, isWhiteSpace, log2, diff --git a/src/core/document.js b/src/core/document.js index d80875b9f..41853455a 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -39,6 +39,7 @@ import { collectActions, getInheritableProperty, getNewAnnotationsMap, + isNumberArray, isWhiteSpace, MissingDataException, PDF_VERSION_REGEXP, @@ -162,7 +163,7 @@ class Page { } let box = this._getInheritableProperty(name, /* getArray = */ true); - if (Array.isArray(box) && box.length === 4) { + if (isNumberArray(box, 4)) { box = Util.normalizeRect(box); if (box[2] - box[0] > 0 && box[3] - box[1] > 0) { return box; diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 3366967ce..a5a9cb98e 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -460,12 +460,12 @@ class PartialEvaluator { localColorSpaceCache ) { const dict = xobj.dict; - const matrix = dict.getArray("Matrix"); + let matrix = dict.getArray("Matrix"); + if (!isNumberArray(matrix, 6)) { + matrix = null; + } let bbox = dict.getArray("BBox"); - bbox = - Array.isArray(bbox) && bbox.length === 4 - ? Util.normalizeRect(bbox) - : null; + bbox = isNumberArray(bbox, 4) ? Util.normalizeRect(bbox) : null; let optionalContent, groupOptions; if (dict.has("OC")) { @@ -1578,7 +1578,10 @@ class PartialEvaluator { localShadingPatternCache, }); if (objId) { - const matrix = dict.getArray("Matrix"); + let matrix = dict.getArray("Matrix"); + if (!isNumberArray(matrix, 6)) { + matrix = null; + } operatorList.addOp(fn, ["Shading", objId, matrix]); } return undefined; @@ -3266,7 +3269,7 @@ class PartialEvaluator { const xObjStateManager = new StateManager(currentState); const matrix = xobj.dict.getArray("Matrix"); - if (Array.isArray(matrix) && matrix.length === 6) { + if (isNumberArray(matrix, 6)) { xObjStateManager.transform(matrix); } diff --git a/src/core/pattern.js b/src/core/pattern.js index d5acbb425..5f5908cc0 100644 --- a/src/core/pattern.js +++ b/src/core/pattern.js @@ -16,14 +16,19 @@ import { assert, FormatError, + IDENTITY_MATRIX, info, unreachable, Util, warn, } from "../shared/util.js"; +import { + isBooleanArray, + isNumberArray, + MissingDataException, +} from "./core_utils.js"; import { BaseStream } from "./base_stream.js"; import { ColorSpace } from "./colorspace.js"; -import { MissingDataException } from "./core_utils.js"; const ShadingType = { FUNCTION_BASED: 1, @@ -106,8 +111,17 @@ class BaseShading { class RadialAxialShading extends BaseShading { constructor(dict, xref, resources, pdfFunctionFactory, localColorSpaceCache) { super(); - this.coordsArr = dict.getArray("Coords"); this.shadingType = dict.get("ShadingType"); + let coordsLen = 0; + if (this.shadingType === ShadingType.AXIAL) { + coordsLen = 4; + } else if (this.shadingType === ShadingType.RADIAL) { + coordsLen = 6; + } + this.coordsArr = dict.getArray("Coords"); + if (!isNumberArray(this.coordsArr, coordsLen)) { + throw new FormatError("RadialAxialShading: Invalid /Coords array."); + } const cs = ColorSpace.parse({ cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"), xref, @@ -116,25 +130,20 @@ class RadialAxialShading extends BaseShading { localColorSpaceCache, }); const bbox = dict.getArray("BBox"); - this.bbox = - Array.isArray(bbox) && bbox.length === 4 - ? Util.normalizeRect(bbox) - : null; + this.bbox = isNumberArray(bbox, 4) ? Util.normalizeRect(bbox) : null; let t0 = 0.0, t1 = 1.0; - if (dict.has("Domain")) { - const domainArr = dict.getArray("Domain"); - t0 = domainArr[0]; - t1 = domainArr[1]; + const domainArr = dict.getArray("Domain"); + if (isNumberArray(domainArr, 2)) { + [t0, t1] = domainArr; } let extendStart = false, extendEnd = false; - if (dict.has("Extend")) { - const extendArr = dict.getArray("Extend"); - extendStart = extendArr[0]; - extendEnd = extendArr[1]; + const extendArr = dict.getArray("Extend"); + if (isBooleanArray(extendArr, 2)) { + [extendStart, extendEnd] = extendArr; } if ( @@ -271,8 +280,7 @@ class RadialAxialShading extends BaseShading { } getIR() { - const coordsArr = this.coordsArr; - const shadingType = this.shadingType; + const { coordsArr, shadingType } = this; let type, p0, p1, r0, r1; if (shadingType === ShadingType.AXIAL) { p0 = [coordsArr[0], coordsArr[1]]; @@ -454,10 +462,7 @@ class MeshShading extends BaseShading { const dict = stream.dict; this.shadingType = dict.get("ShadingType"); const bbox = dict.getArray("BBox"); - this.bbox = - Array.isArray(bbox) && bbox.length === 4 - ? Util.normalizeRect(bbox) - : null; + this.bbox = isNumberArray(bbox, 4) ? Util.normalizeRect(bbox) : null; const cs = ColorSpace.parse({ cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"), xref, @@ -983,17 +988,32 @@ class DummyShading extends BaseShading { } function getTilingPatternIR(operatorList, dict, color) { - const matrix = dict.getArray("Matrix"); - const bbox = Util.normalizeRect(dict.getArray("BBox")); - const xstep = dict.get("XStep"); - const ystep = dict.get("YStep"); - const paintType = dict.get("PaintType"); - const tilingType = dict.get("TilingType"); - + let matrix = dict.getArray("Matrix"); + if (!isNumberArray(matrix, 6)) { + matrix = IDENTITY_MATRIX; + } + let bbox = dict.getArray("BBox"); + bbox = isNumberArray(bbox, 4) ? Util.normalizeRect(bbox) : null; // Ensure that the pattern has a non-zero width and height, to prevent errors // in `pattern_helper.js` (fixes issue8330.pdf). - if (bbox[2] - bbox[0] === 0 || bbox[3] - bbox[1] === 0) { - throw new FormatError(`Invalid getTilingPatternIR /BBox array: [${bbox}].`); + if (!bbox || bbox[2] - bbox[0] === 0 || bbox[3] - bbox[1] === 0) { + throw new FormatError(`Invalid getTilingPatternIR /BBox array.`); + } + const xstep = dict.get("XStep"); + if (typeof xstep !== "number") { + throw new FormatError(`Invalid getTilingPatternIR /XStep value.`); + } + const ystep = dict.get("YStep"); + if (typeof ystep !== "number") { + throw new FormatError(`Invalid getTilingPatternIR /YStep value.`); + } + const paintType = dict.get("PaintType"); + if (!Number.isInteger(paintType)) { + throw new FormatError(`Invalid getTilingPatternIR /PaintType value.`); + } + const tilingType = dict.get("TilingType"); + if (!Number.isInteger(tilingType)) { + throw new FormatError(`Invalid getTilingPatternIR /TilingType value.`); } return [ diff --git a/src/display/canvas.js b/src/display/canvas.js index eb51cef05..811e625a7 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -2465,10 +2465,9 @@ class CanvasGraphics { this.save(); this.baseTransformStack.push(this.baseTransform); - if (Array.isArray(matrix) && matrix.length === 6) { + if (matrix) { this.transform(...matrix); } - this.baseTransform = getCurrentTransform(this.ctx); if (bbox) { @@ -2652,7 +2651,7 @@ class CanvasGraphics { this.ctx.setTransform(...this.baseTransform); } - if (Array.isArray(rect) && rect.length === 4) { + if (rect) { const width = rect[2] - rect[0]; const height = rect[3] - rect[1]; diff --git a/src/display/pattern_helper.js b/src/display/pattern_helper.js index ba3e7f93d..781449fbb 100644 --- a/src/display/pattern_helper.js +++ b/src/display/pattern_helper.js @@ -455,7 +455,7 @@ class TilingPattern { constructor(IR, color, ctx, canvasGraphicsFactory, baseTransform) { this.operatorList = IR[2]; - this.matrix = IR[3] || [1, 0, 0, 1, 0, 0]; + this.matrix = IR[3]; this.bbox = IR[4]; this.xstep = IR[5]; this.ystep = IR[6]; From 9b41bfc37487cb15fcc2ff0d7d5e392816ed3054 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Tue, 30 Apr 2024 08:47:14 +0200 Subject: [PATCH 2/2] Introduce helper functions for parsing /Matrix and /BBox arrays --- src/core/annotation.js | 47 +++++++++++++++--------------------------- src/core/core_utils.js | 19 +++++++++++++++++ src/core/document.js | 10 +++++---- src/core/evaluator.js | 40 ++++++++++++++--------------------- src/core/pattern.js | 16 ++++++-------- 5 files changed, 63 insertions(+), 69 deletions(-) diff --git a/src/core/annotation.js b/src/core/annotation.js index 2995965dd..1402f32ea 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -44,6 +44,9 @@ import { getRotationMatrix, isAscii, isNumberArray, + lookupMatrix, + lookupNormalRect, + lookupRect, numberToString, stringToUTF16String, } from "./core_utils.js"; @@ -915,9 +918,7 @@ class Annotation { * @param {Array} rectangle - The rectangle array with exactly four entries */ setRectangle(rectangle) { - this.rectangle = isNumberArray(rectangle, 4) - ? Util.normalizeRect(rectangle) - : [0, 0, 0, 0]; + this.rectangle = lookupNormalRect(rectangle, [0, 0, 0, 0]); } /** @@ -1150,14 +1151,11 @@ class Annotation { ["ExtGState", "ColorSpace", "Pattern", "Shading", "XObject", "Font"], appearance ); - let bbox = appearanceDict.getArray("BBox"); - if (!isNumberArray(bbox, 4)) { - bbox = [0, 0, 1, 1]; - } - let matrix = appearanceDict.getArray("Matrix"); - if (!isNumberArray(matrix, 6)) { - matrix = [1, 0, 0, 1, 0, 0]; - } + const bbox = lookupRect(appearanceDict.getArray("BBox"), [0, 0, 1, 1]); + const matrix = lookupMatrix( + appearanceDict.getArray("Matrix"), + IDENTITY_MATRIX + ); const transform = getTransformMatrix(rect, bbox, matrix); const opList = new OperatorList(); @@ -1254,14 +1252,8 @@ class Annotation { if (text.length > 1 || text[0]) { const appearanceDict = this.appearance.dict; - let bbox = appearanceDict.getArray("BBox"); - if (!isNumberArray(bbox, 4)) { - bbox = null; - } - let matrix = appearanceDict.getArray("Matrix"); - if (!isNumberArray(matrix, 6)) { - matrix = null; - } + const bbox = lookupRect(appearanceDict.getArray("BBox"), null); + const matrix = lookupMatrix(appearanceDict.getArray("Matrix"), null); this.data.textPosition = this._transformPoint( firstPosition, @@ -2987,8 +2979,10 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { : this.uncheckedAppearance; if (appearance) { const savedAppearance = this.appearance; - const matrix = appearance.dict.getArray("Matrix"); - const savedMatrix = isNumberArray(matrix, 6) ? matrix : IDENTITY_MATRIX; + const savedMatrix = lookupMatrix( + appearance.dict.getArray("Matrix"), + IDENTITY_MATRIX + ); if (rotation) { appearance.dict.set( @@ -3753,11 +3747,7 @@ class PopupAnnotation extends Annotation { warn("Popup annotation has a missing or invalid parent annotation."); return; } - - const parentRect = parentItem.getArray("Rect"); - this.data.parentRect = isNumberArray(parentRect, 4) - ? Util.normalizeRect(parentRect) - : null; + this.data.parentRect = lookupNormalRect(parentItem.getArray("Rect"), null); const rt = parentItem.get("RT"); if (isName(rt, AnnotationReplyType.GROUP)) { @@ -4045,10 +4035,7 @@ class LineAnnotation extends MarkupAnnotation { this.data.hasOwnCanvas = this.data.noRotate; this.data.noHTML = false; - let lineCoordinates = dict.getArray("L"); - if (!isNumberArray(lineCoordinates, 4)) { - lineCoordinates = [0, 0, 0, 0]; - } + const lineCoordinates = lookupRect(dict.getArray("L"), [0, 0, 0, 0]); this.data.lineCoordinates = Util.normalizeRect(lineCoordinates); if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) { diff --git a/src/core/core_utils.js b/src/core/core_utils.js index e62efc123..a84373d16 100644 --- a/src/core/core_utils.js +++ b/src/core/core_utils.js @@ -19,6 +19,7 @@ import { BaseException, objectSize, stringToPDFString, + Util, warn, } from "../shared/util.js"; import { Dict, isName, Ref, RefSet } from "./primitives.js"; @@ -248,6 +249,21 @@ function isNumberArray(arr, len) { ); } +// Returns the matrix, or the fallback value if it's invalid. +function lookupMatrix(arr, fallback) { + return isNumberArray(arr, 6) ? arr : fallback; +} + +// Returns the rectangle, or the fallback value if it's invalid. +function lookupRect(arr, fallback) { + return isNumberArray(arr, 4) ? arr : fallback; +} + +// Returns the normalized rectangle, or the fallback value if it's invalid. +function lookupNormalRect(arr, fallback) { + return isNumberArray(arr, 4) ? Util.normalizeRect(arr) : fallback; +} + /** * AcroForm field names use an array like notation to refer to * repeated XFA elements e.g. foo.bar[nnn]. @@ -671,6 +687,9 @@ export { isNumberArray, isWhiteSpace, log2, + lookupMatrix, + lookupNormalRect, + lookupRect, MissingDataException, numberToString, ParserEOFException, diff --git a/src/core/document.js b/src/core/document.js index 41853455a..8e6c2243b 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -39,8 +39,8 @@ import { collectActions, getInheritableProperty, getNewAnnotationsMap, - isNumberArray, isWhiteSpace, + lookupNormalRect, MissingDataException, PDF_VERSION_REGEXP, validateCSSFont, @@ -161,10 +161,12 @@ class Page { if (this.xfaData) { return this.xfaData.bbox; } - let box = this._getInheritableProperty(name, /* getArray = */ true); + const box = lookupNormalRect( + this._getInheritableProperty(name, /* getArray = */ true), + null + ); - if (isNumberArray(box, 4)) { - box = Util.normalizeRect(box); + if (box) { if (box[2] - box[0] > 0 && box[3] - box[1] > 0) { return box; } diff --git a/src/core/evaluator.js b/src/core/evaluator.js index a5a9cb98e..f78d2d2e9 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -53,6 +53,7 @@ import { import { getTilingPatternIR, Pattern } from "./pattern.js"; import { getXfaFontDict, getXfaFontName } from "./xfa_fonts.js"; import { IdentityToUnicodeMap, ToUnicodeMap } from "./to_unicode_map.js"; +import { isNumberArray, lookupMatrix, lookupNormalRect } from "./core_utils.js"; import { isPDFFunction, PDFFunctionFactory } from "./function.js"; import { Lexer, Parser } from "./parser.js"; import { @@ -73,7 +74,6 @@ 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"; @@ -460,12 +460,8 @@ class PartialEvaluator { localColorSpaceCache ) { const dict = xobj.dict; - let matrix = dict.getArray("Matrix"); - if (!isNumberArray(matrix, 6)) { - matrix = null; - } - let bbox = dict.getArray("BBox"); - bbox = isNumberArray(bbox, 4) ? Util.normalizeRect(bbox) : null; + const matrix = lookupMatrix(dict.getArray("Matrix"), null); + const bbox = lookupNormalRect(dict.getArray("BBox"), null); let optionalContent, groupOptions; if (dict.has("OC")) { @@ -1578,10 +1574,7 @@ class PartialEvaluator { localShadingPatternCache, }); if (objId) { - let matrix = dict.getArray("Matrix"); - if (!isNumberArray(matrix, 6)) { - matrix = null; - } + const matrix = lookupMatrix(dict.getArray("Matrix"), null); operatorList.addOp(fn, ["Shading", objId, matrix]); } return undefined; @@ -3268,8 +3261,8 @@ class PartialEvaluator { const currentState = stateManager.state.clone(); const xObjStateManager = new StateManager(currentState); - const matrix = xobj.dict.getArray("Matrix"); - if (isNumberArray(matrix, 6)) { + const matrix = lookupMatrix(xobj.dict.getArray("Matrix"), null); + if (matrix) { xObjStateManager.transform(matrix); } @@ -4247,10 +4240,7 @@ class PartialEvaluator { if (!descriptor) { if (isType3Font) { - let bbox = dict.getArray("FontBBox"); - if (!isNumberArray(bbox, 4)) { - bbox = [0, 0, 0, 0]; - } + const bbox = lookupNormalRect(dict.getArray("FontBBox"), [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); @@ -4440,14 +4430,14 @@ class PartialEvaluator { } } - let fontMatrix = dict.getArray("FontMatrix"); - if (!isNumberArray(fontMatrix, 6)) { - fontMatrix = FONT_IDENTITY_MATRIX; - } - let bbox = descriptor.getArray("FontBBox") || dict.getArray("FontBBox"); - if (!isNumberArray(bbox, 4)) { - bbox = undefined; - } + const fontMatrix = lookupMatrix( + dict.getArray("FontMatrix"), + FONT_IDENTITY_MATRIX + ); + const bbox = lookupNormalRect( + descriptor.getArray("FontBBox") || dict.getArray("FontBBox"), + undefined + ); let ascent = descriptor.get("Ascent"); if (typeof ascent !== "number") { ascent = undefined; diff --git a/src/core/pattern.js b/src/core/pattern.js index 5f5908cc0..37fdc5257 100644 --- a/src/core/pattern.js +++ b/src/core/pattern.js @@ -25,6 +25,8 @@ import { import { isBooleanArray, isNumberArray, + lookupMatrix, + lookupNormalRect, MissingDataException, } from "./core_utils.js"; import { BaseStream } from "./base_stream.js"; @@ -129,8 +131,7 @@ class RadialAxialShading extends BaseShading { pdfFunctionFactory, localColorSpaceCache, }); - const bbox = dict.getArray("BBox"); - this.bbox = isNumberArray(bbox, 4) ? Util.normalizeRect(bbox) : null; + this.bbox = lookupNormalRect(dict.getArray("BBox"), null); let t0 = 0.0, t1 = 1.0; @@ -461,8 +462,7 @@ class MeshShading extends BaseShading { } const dict = stream.dict; this.shadingType = dict.get("ShadingType"); - const bbox = dict.getArray("BBox"); - this.bbox = isNumberArray(bbox, 4) ? Util.normalizeRect(bbox) : null; + this.bbox = lookupNormalRect(dict.getArray("BBox"), null); const cs = ColorSpace.parse({ cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"), xref, @@ -988,12 +988,8 @@ class DummyShading extends BaseShading { } function getTilingPatternIR(operatorList, dict, color) { - let matrix = dict.getArray("Matrix"); - if (!isNumberArray(matrix, 6)) { - matrix = IDENTITY_MATRIX; - } - let bbox = dict.getArray("BBox"); - bbox = isNumberArray(bbox, 4) ? Util.normalizeRect(bbox) : null; + const matrix = lookupMatrix(dict.getArray("Matrix"), IDENTITY_MATRIX); + const bbox = lookupNormalRect(dict.getArray("BBox"), null); // Ensure that the pattern has a non-zero width and height, to prevent errors // in `pattern_helper.js` (fixes issue8330.pdf). if (!bbox || bbox[2] - bbox[0] === 0 || bbox[3] - bbox[1] === 0) {