UNPKG

pdfjs-dist

Version:

Generic build of Mozilla's PDF.js library.

1,431 lines (1,430 loc) • 132 kB
/** * @licstart The following is the entire license notice for the * JavaScript code in this page * * Copyright 2022 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. * * @licend The above is the entire license notice for the * JavaScript code in this page */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PartialEvaluator = exports.EvaluatorPreprocessor = void 0; var _util = require("../shared/util.js"); var _cmap = require("./cmap.js"); var _primitives = require("./primitives.js"); var _fonts = require("./fonts.js"); var _fonts_utils = require("./fonts_utils.js"); var _encodings = require("./encodings.js"); var _standard_fonts = require("./standard_fonts.js"); var _pattern = require("./pattern.js"); var _xfa_fonts = require("./xfa_fonts.js"); var _to_unicode_map = require("./to_unicode_map.js"); var _function = require("./function.js"); var _parser = require("./parser.js"); var _image_utils = require("./image_utils.js"); var _stream = require("./stream.js"); var _base_stream = require("./base_stream.js"); var _bidi = require("./bidi.js"); var _colorspace = require("./colorspace.js"); var _decode_stream = require("./decode_stream.js"); var _glyphlist = require("./glyphlist.js"); var _core_utils = require("./core_utils.js"); var _metrics = require("./metrics.js"); var _unicode = require("./unicode.js"); var _murmurhash = require("../shared/murmurhash3.js"); var _operator_list = require("./operator_list.js"); var _image = require("./image.js"); const DefaultPartialEvaluatorOptions = Object.freeze({ maxImageSize: -1, disableFontFace: false, ignoreErrors: false, isEvalSupported: true, isOffscreenCanvasSupported: true, fontExtraProperties: false, useSystemFonts: true, cMapUrl: null, standardFontDataUrl: null }); const PatternType = { TILING: 1, SHADING: 2 }; const TEXT_CHUNK_BATCH_SIZE = 10; const deferred = Promise.resolve(); function normalizeBlendMode(value, parsingArray = false) { if (Array.isArray(value)) { for (const val of value) { const maybeBM = normalizeBlendMode(val, true); if (maybeBM) { return maybeBM; } } (0, _util.warn)(`Unsupported blend mode Array: ${value}`); return "source-over"; } if (!(value instanceof _primitives.Name)) { if (parsingArray) { return null; } return "source-over"; } switch (value.name) { case "Normal": case "Compatible": return "source-over"; case "Multiply": return "multiply"; case "Screen": return "screen"; case "Overlay": return "overlay"; case "Darken": return "darken"; case "Lighten": return "lighten"; case "ColorDodge": return "color-dodge"; case "ColorBurn": return "color-burn"; case "HardLight": return "hard-light"; case "SoftLight": return "soft-light"; case "Difference": return "difference"; case "Exclusion": return "exclusion"; case "Hue": return "hue"; case "Saturation": return "saturation"; case "Color": return "color"; case "Luminosity": return "luminosity"; } if (parsingArray) { return null; } (0, _util.warn)(`Unsupported blend mode: ${value.name}`); return "source-over"; } function incrementCachedImageMaskCount(data) { if (data.fn === _util.OPS.paintImageMaskXObject && data.args[0] && data.args[0].count > 0) { data.args[0].count++; } } class TimeSlotManager { static get TIME_SLOT_DURATION_MS() { return (0, _util.shadow)(this, "TIME_SLOT_DURATION_MS", 20); } static get CHECK_TIME_EVERY() { return (0, _util.shadow)(this, "CHECK_TIME_EVERY", 100); } constructor() { this.reset(); } check() { if (++this.checked < TimeSlotManager.CHECK_TIME_EVERY) { return false; } this.checked = 0; return this.endTime <= Date.now(); } reset() { this.endTime = Date.now() + TimeSlotManager.TIME_SLOT_DURATION_MS; this.checked = 0; } } class PartialEvaluator { constructor({ xref, handler, pageIndex, idFactory, fontCache, builtInCMapCache, standardFontDataCache, globalImageCache, options = null }) { this.xref = xref; this.handler = handler; this.pageIndex = pageIndex; this.idFactory = idFactory; this.fontCache = fontCache; this.builtInCMapCache = builtInCMapCache; this.standardFontDataCache = standardFontDataCache; this.globalImageCache = globalImageCache; this.options = options || DefaultPartialEvaluatorOptions; this.parsingType3Font = false; this._fetchBuiltInCMapBound = this.fetchBuiltInCMap.bind(this); } get _pdfFunctionFactory() { const pdfFunctionFactory = new _function.PDFFunctionFactory({ xref: this.xref, isEvalSupported: this.options.isEvalSupported }); return (0, _util.shadow)(this, "_pdfFunctionFactory", pdfFunctionFactory); } clone(newOptions = null) { const newEvaluator = Object.create(this); newEvaluator.options = Object.assign(Object.create(null), this.options, newOptions); return newEvaluator; } hasBlendModes(resources, nonBlendModesSet) { if (!(resources instanceof _primitives.Dict)) { return false; } if (resources.objId && nonBlendModesSet.has(resources.objId)) { return false; } const processed = new _primitives.RefSet(nonBlendModesSet); if (resources.objId) { processed.put(resources.objId); } const nodes = [resources], xref = this.xref; while (nodes.length) { const node = nodes.shift(); const graphicStates = node.get("ExtGState"); if (graphicStates instanceof _primitives.Dict) { for (let graphicState of graphicStates.getRawValues()) { if (graphicState instanceof _primitives.Ref) { if (processed.has(graphicState)) { continue; } try { graphicState = xref.fetch(graphicState); } catch (ex) { processed.put(graphicState); (0, _util.info)(`hasBlendModes - ignoring ExtGState: "${ex}".`); continue; } } if (!(graphicState instanceof _primitives.Dict)) { continue; } if (graphicState.objId) { processed.put(graphicState.objId); } const bm = graphicState.get("BM"); if (bm instanceof _primitives.Name) { if (bm.name !== "Normal") { return true; } continue; } if (bm !== undefined && Array.isArray(bm)) { for (const element of bm) { if (element instanceof _primitives.Name && element.name !== "Normal") { return true; } } } } } const xObjects = node.get("XObject"); if (!(xObjects instanceof _primitives.Dict)) { continue; } for (let xObject of xObjects.getRawValues()) { if (xObject instanceof _primitives.Ref) { if (processed.has(xObject)) { continue; } try { xObject = xref.fetch(xObject); } catch (ex) { processed.put(xObject); (0, _util.info)(`hasBlendModes - ignoring XObject: "${ex}".`); continue; } } if (!(xObject instanceof _base_stream.BaseStream)) { continue; } if (xObject.dict.objId) { processed.put(xObject.dict.objId); } const xResources = xObject.dict.get("Resources"); if (!(xResources instanceof _primitives.Dict)) { continue; } if (xResources.objId && processed.has(xResources.objId)) { continue; } nodes.push(xResources); if (xResources.objId) { processed.put(xResources.objId); } } } for (const ref of processed) { nonBlendModesSet.put(ref); } return false; } async fetchBuiltInCMap(name) { const cachedData = this.builtInCMapCache.get(name); if (cachedData) { return cachedData; } let data; if (this.options.cMapUrl !== null) { const url = `${this.options.cMapUrl}${name}.bcmap`; const response = await fetch(url); if (!response.ok) { throw new Error(`fetchBuiltInCMap: failed to fetch file "${url}" with "${response.statusText}".`); } data = { cMapData: new Uint8Array(await response.arrayBuffer()), compressionType: _util.CMapCompressionType.BINARY }; } else { data = await this.handler.sendWithPromise("FetchBuiltInCMap", { name }); } if (data.compressionType !== _util.CMapCompressionType.NONE) { this.builtInCMapCache.set(name, data); } return data; } async fetchStandardFontData(name) { const cachedData = this.standardFontDataCache.get(name); if (cachedData) { return new _stream.Stream(cachedData); } if (this.options.useSystemFonts && name !== "Symbol" && name !== "ZapfDingbats") { return null; } const standardFontNameToFileName = (0, _standard_fonts.getFontNameToFileMap)(), filename = standardFontNameToFileName[name]; let data; if (this.options.standardFontDataUrl !== null) { const url = `${this.options.standardFontDataUrl}${filename}`; const response = await fetch(url); if (!response.ok) { (0, _util.warn)(`fetchStandardFontData: failed to fetch file "${url}" with "${response.statusText}".`); } else { data = await response.arrayBuffer(); } } else { try { data = await this.handler.sendWithPromise("FetchStandardFontData", { filename }); } catch (e) { (0, _util.warn)(`fetchStandardFontData: failed to fetch file "${filename}" with "${e}".`); } } if (!data) { return null; } this.standardFontDataCache.set(name, data); return new _stream.Stream(data); } async buildFormXObject(resources, xobj, smask, operatorList, task, initialState, localColorSpaceCache) { const dict = xobj.dict; const matrix = dict.getArray("Matrix"); let bbox = dict.getArray("BBox"); if (Array.isArray(bbox) && bbox.length === 4) { bbox = _util.Util.normalizeRect(bbox); } else { bbox = null; } let optionalContent, groupOptions; if (dict.has("OC")) { optionalContent = await this.parseMarkedContentProps(dict.get("OC"), resources); } if (optionalContent !== undefined) { operatorList.addOp(_util.OPS.beginMarkedContentProps, ["OC", optionalContent]); } const group = dict.get("Group"); if (group) { groupOptions = { matrix, bbox, smask, isolated: false, knockout: false }; const groupSubtype = group.get("S"); let colorSpace = null; if ((0, _primitives.isName)(groupSubtype, "Transparency")) { groupOptions.isolated = group.get("I") || false; groupOptions.knockout = group.get("K") || false; if (group.has("CS")) { const cs = group.getRaw("CS"); const cachedColorSpace = _colorspace.ColorSpace.getCached(cs, this.xref, localColorSpaceCache); if (cachedColorSpace) { colorSpace = cachedColorSpace; } else { colorSpace = await this.parseColorSpace({ cs, resources, localColorSpaceCache }); } } } if (smask && smask.backdrop) { colorSpace = colorSpace || _colorspace.ColorSpace.singletons.rgb; smask.backdrop = colorSpace.getRgb(smask.backdrop, 0); } operatorList.addOp(_util.OPS.beginGroup, [groupOptions]); } const args = group ? [matrix, null] : [matrix, bbox]; operatorList.addOp(_util.OPS.paintFormXObjectBegin, args); return this.getOperatorList({ stream: xobj, task, resources: dict.get("Resources") || resources, operatorList, initialState }).then(function () { operatorList.addOp(_util.OPS.paintFormXObjectEnd, []); if (group) { operatorList.addOp(_util.OPS.endGroup, [groupOptions]); } if (optionalContent !== undefined) { operatorList.addOp(_util.OPS.endMarkedContent, []); } }); } _sendImgData(objId, imgData, cacheGlobally = false) { const transfers = imgData ? [imgData.bitmap || imgData.data.buffer] : null; if (this.parsingType3Font || cacheGlobally) { return this.handler.send("commonobj", [objId, "Image", imgData], transfers); } return this.handler.send("obj", [objId, this.pageIndex, "Image", imgData], transfers); } async buildPaintImageXObject({ resources, image, isInline = false, operatorList, cacheKey, localImageCache, localColorSpaceCache }) { const dict = image.dict; const imageRef = dict.objId; const w = dict.get("W", "Width"); const h = dict.get("H", "Height"); if (!(w && typeof w === "number") || !(h && typeof h === "number")) { (0, _util.warn)("Image dimensions are missing, or not numbers."); return; } const maxImageSize = this.options.maxImageSize; if (maxImageSize !== -1 && w * h > maxImageSize) { const msg = "Image exceeded maximum allowed size and was removed."; if (this.options.ignoreErrors) { (0, _util.warn)(msg); return; } throw new Error(msg); } let optionalContent; if (dict.has("OC")) { optionalContent = await this.parseMarkedContentProps(dict.get("OC"), resources); } const imageMask = dict.get("IM", "ImageMask") || false; let imgData, args; if (imageMask) { const interpolate = dict.get("I", "Interpolate"); const bitStrideLength = w + 7 >> 3; const imgArray = image.getBytes(bitStrideLength * h); const decode = dict.getArray("D", "Decode"); if (this.parsingType3Font) { imgData = _image.PDFImage.createRawMask({ imgArray, width: w, height: h, imageIsFromDecodeStream: image instanceof _decode_stream.DecodeStream, inverseDecode: !!decode && decode[0] > 0, interpolate }); imgData.cached = !!cacheKey; args = [imgData]; operatorList.addImageOps(_util.OPS.paintImageMaskXObject, args, optionalContent); if (cacheKey) { localImageCache.set(cacheKey, imageRef, { fn: _util.OPS.paintImageMaskXObject, args, optionalContent }); } return; } imgData = _image.PDFImage.createMask({ imgArray, width: w, height: h, imageIsFromDecodeStream: image instanceof _decode_stream.DecodeStream, inverseDecode: !!decode && decode[0] > 0, interpolate, isOffscreenCanvasSupported: this.options.isOffscreenCanvasSupported }); if (imgData.isSingleOpaquePixel) { operatorList.addImageOps(_util.OPS.paintSolidColorImageMask, [], optionalContent); if (cacheKey) { localImageCache.set(cacheKey, imageRef, { fn: _util.OPS.paintSolidColorImageMask, args: [], optionalContent }); } return; } const objId = `mask_${this.idFactory.createObjId()}`; operatorList.addDependency(objId); this._sendImgData(objId, imgData); args = [{ data: objId, width: imgData.width, height: imgData.height, interpolate: imgData.interpolate, count: 1 }]; operatorList.addImageOps(_util.OPS.paintImageMaskXObject, args, optionalContent); if (cacheKey) { localImageCache.set(cacheKey, imageRef, { fn: _util.OPS.paintImageMaskXObject, args, optionalContent }); } return; } const softMask = dict.get("SM", "SMask") || false; const mask = dict.get("Mask") || false; const SMALL_IMAGE_DIMENSIONS = 200; if (isInline && !softMask && !mask && w + h < SMALL_IMAGE_DIMENSIONS) { const imageObj = new _image.PDFImage({ xref: this.xref, res: resources, image, isInline, pdfFunctionFactory: this._pdfFunctionFactory, localColorSpaceCache }); imgData = imageObj.createImageData(true); operatorList.addImageOps(_util.OPS.paintInlineImageXObject, [imgData], optionalContent); return; } let objId = `img_${this.idFactory.createObjId()}`, cacheGlobally = false; if (this.parsingType3Font) { objId = `${this.idFactory.getDocId()}_type3_${objId}`; } else if (imageRef) { cacheGlobally = this.globalImageCache.shouldCache(imageRef, this.pageIndex); if (cacheGlobally) { objId = `${this.idFactory.getDocId()}_${objId}`; } } operatorList.addDependency(objId); args = [objId, w, h]; _image.PDFImage.buildImage({ xref: this.xref, res: resources, image, isInline, pdfFunctionFactory: this._pdfFunctionFactory, localColorSpaceCache }).then(imageObj => { imgData = imageObj.createImageData(false); if (cacheKey && imageRef && cacheGlobally) { this.globalImageCache.addByteSize(imageRef, imgData.data.length); } return this._sendImgData(objId, imgData, cacheGlobally); }).catch(reason => { (0, _util.warn)(`Unable to decode image "${objId}": "${reason}".`); return this._sendImgData(objId, null, cacheGlobally); }); operatorList.addImageOps(_util.OPS.paintImageXObject, args, optionalContent); if (cacheKey) { localImageCache.set(cacheKey, imageRef, { fn: _util.OPS.paintImageXObject, args, optionalContent }); if (imageRef) { (0, _util.assert)(!isInline, "Cannot cache an inline image globally."); this.globalImageCache.addPageIndex(imageRef, this.pageIndex); if (cacheGlobally) { this.globalImageCache.setData(imageRef, { objId, fn: _util.OPS.paintImageXObject, args, optionalContent, byteSize: 0 }); } } } } handleSMask(smask, resources, operatorList, task, stateManager, localColorSpaceCache) { const smaskContent = smask.get("G"); const smaskOptions = { subtype: smask.get("S").name, backdrop: smask.get("BC") }; const transferObj = smask.get("TR"); if ((0, _function.isPDFFunction)(transferObj)) { const transferFn = this._pdfFunctionFactory.create(transferObj); const transferMap = new Uint8Array(256); const tmp = new Float32Array(1); for (let i = 0; i < 256; i++) { tmp[0] = i / 255; transferFn(tmp, 0, tmp, 0); transferMap[i] = tmp[0] * 255 | 0; } smaskOptions.transferMap = transferMap; } return this.buildFormXObject(resources, smaskContent, smaskOptions, operatorList, task, stateManager.state.clone(), localColorSpaceCache); } handleTransferFunction(tr) { let transferArray; if (Array.isArray(tr)) { transferArray = tr; } else if ((0, _function.isPDFFunction)(tr)) { transferArray = [tr]; } else { return null; } const transferMaps = []; let numFns = 0, numEffectfulFns = 0; for (const entry of transferArray) { const transferObj = this.xref.fetchIfRef(entry); numFns++; if ((0, _primitives.isName)(transferObj, "Identity")) { transferMaps.push(null); continue; } else if (!(0, _function.isPDFFunction)(transferObj)) { return null; } const transferFn = this._pdfFunctionFactory.create(transferObj); const transferMap = new Uint8Array(256), tmp = new Float32Array(1); for (let j = 0; j < 256; j++) { tmp[0] = j / 255; transferFn(tmp, 0, tmp, 0); transferMap[j] = tmp[0] * 255 | 0; } transferMaps.push(transferMap); numEffectfulFns++; } if (!(numFns === 1 || numFns === 4)) { return null; } if (numEffectfulFns === 0) { return null; } return transferMaps; } handleTilingType(fn, color, resources, pattern, patternDict, operatorList, task, localTilingPatternCache) { const tilingOpList = new _operator_list.OperatorList(); const patternResources = _primitives.Dict.merge({ xref: this.xref, dictArray: [patternDict.get("Resources"), resources] }); return this.getOperatorList({ stream: pattern, task, resources: patternResources, operatorList: tilingOpList }).then(function () { const operatorListIR = tilingOpList.getIR(); const tilingPatternIR = (0, _pattern.getTilingPatternIR)(operatorListIR, patternDict, color); operatorList.addDependencies(tilingOpList.dependencies); operatorList.addOp(fn, tilingPatternIR); if (patternDict.objId) { localTilingPatternCache.set(null, patternDict.objId, { operatorListIR, dict: patternDict }); } }).catch(reason => { if (reason instanceof _util.AbortException) { return; } if (this.options.ignoreErrors) { this.handler.send("UnsupportedFeature", { featureId: _util.UNSUPPORTED_FEATURES.errorTilingPattern }); (0, _util.warn)(`handleTilingType - ignoring pattern: "${reason}".`); return; } throw reason; }); } handleSetFont(resources, fontArgs, fontRef, operatorList, task, state, fallbackFontDict = null, cssFontInfo = null) { const fontName = fontArgs && fontArgs[0] instanceof _primitives.Name ? fontArgs[0].name : null; return this.loadFont(fontName, fontRef, resources, fallbackFontDict, cssFontInfo).then(translated => { if (!translated.font.isType3Font) { return translated; } return translated.loadType3Data(this, resources, task).then(function () { operatorList.addDependencies(translated.type3Dependencies); return translated; }).catch(reason => { this.handler.send("UnsupportedFeature", { featureId: _util.UNSUPPORTED_FEATURES.errorFontLoadType3 }); return new TranslatedFont({ loadedName: "g_font_error", font: new _fonts.ErrorFont(`Type3 font load error: ${reason}`), dict: translated.font, evaluatorOptions: this.options }); }); }).then(translated => { state.font = translated.font; translated.send(this.handler); return translated.loadedName; }); } handleText(chars, state) { const font = state.font; const glyphs = font.charsToGlyphs(chars); if (font.data) { const isAddToPathSet = !!(state.textRenderingMode & _util.TextRenderingMode.ADD_TO_PATH_FLAG); if (isAddToPathSet || state.fillColorSpace.name === "Pattern" || font.disableFontFace || this.options.disableFontFace) { PartialEvaluator.buildFontPaths(font, glyphs, this.handler, this.options); } } return glyphs; } ensureStateFont(state) { if (state.font) { return; } const reason = new _util.FormatError("Missing setFont (Tf) operator before text rendering operator."); if (this.options.ignoreErrors) { this.handler.send("UnsupportedFeature", { featureId: _util.UNSUPPORTED_FEATURES.errorFontState }); (0, _util.warn)(`ensureStateFont: "${reason}".`); return; } throw reason; } async setGState({ resources, gState, operatorList, cacheKey, task, stateManager, localGStateCache, localColorSpaceCache }) { const gStateRef = gState.objId; let isSimpleGState = true; const gStateObj = []; let promise = Promise.resolve(); for (const key of gState.getKeys()) { const value = gState.get(key); switch (key) { case "Type": break; case "LW": case "LC": case "LJ": case "ML": case "D": case "RI": case "FL": case "CA": case "ca": gStateObj.push([key, value]); break; case "Font": isSimpleGState = false; promise = promise.then(() => { return this.handleSetFont(resources, null, value[0], operatorList, task, stateManager.state).then(function (loadedName) { operatorList.addDependency(loadedName); gStateObj.push([key, [loadedName, value[1]]]); }); }); break; case "BM": gStateObj.push([key, normalizeBlendMode(value)]); break; case "SMask": if ((0, _primitives.isName)(value, "None")) { gStateObj.push([key, false]); break; } if (value instanceof _primitives.Dict) { isSimpleGState = false; promise = promise.then(() => { return this.handleSMask(value, resources, operatorList, task, stateManager, localColorSpaceCache); }); gStateObj.push([key, true]); } else { (0, _util.warn)("Unsupported SMask type"); } break; case "TR": const transferMaps = this.handleTransferFunction(value); gStateObj.push([key, transferMaps]); break; case "OP": case "op": case "OPM": case "BG": case "BG2": case "UCR": case "UCR2": case "TR2": case "HT": case "SM": case "SA": case "AIS": case "TK": (0, _util.info)("graphic state operator " + key); break; default: (0, _util.info)("Unknown graphic state operator " + key); break; } } return promise.then(function () { if (gStateObj.length > 0) { operatorList.addOp(_util.OPS.setGState, [gStateObj]); } if (isSimpleGState) { localGStateCache.set(cacheKey, gStateRef, gStateObj); } }); } loadFont(fontName, font, resources, fallbackFontDict = null, cssFontInfo = null) { const errorFont = async () => { return new TranslatedFont({ loadedName: "g_font_error", font: new _fonts.ErrorFont(`Font "${fontName}" is not available.`), dict: font, evaluatorOptions: this.options }); }; const xref = this.xref; let fontRef; if (font) { if (font instanceof _primitives.Ref) { fontRef = font; } } else { const fontRes = resources.get("Font"); if (fontRes) { fontRef = fontRes.getRaw(fontName); } } if (!fontRef) { const partialMsg = `Font "${fontName || font && font.toString()}" is not available`; if (!this.options.ignoreErrors && !this.parsingType3Font) { (0, _util.warn)(`${partialMsg}.`); return errorFont(); } this.handler.send("UnsupportedFeature", { featureId: _util.UNSUPPORTED_FEATURES.errorFontMissing }); (0, _util.warn)(`${partialMsg} -- attempting to fallback to a default font.`); if (fallbackFontDict) { fontRef = fallbackFontDict; } else { fontRef = PartialEvaluator.fallbackFontDict; } } if (this.parsingType3Font && this.type3FontRefs.has(fontRef)) { return errorFont(); } if (this.fontCache.has(fontRef)) { return this.fontCache.get(fontRef); } font = xref.fetchIfRef(fontRef); if (!(font instanceof _primitives.Dict)) { return errorFont(); } if (font.cacheKey && this.fontCache.has(font.cacheKey)) { return this.fontCache.get(font.cacheKey); } const fontCapability = (0, _util.createPromiseCapability)(); let preEvaluatedFont; try { preEvaluatedFont = this.preEvaluateFont(font); preEvaluatedFont.cssFontInfo = cssFontInfo; } catch (reason) { (0, _util.warn)(`loadFont - preEvaluateFont failed: "${reason}".`); return errorFont(); } const { descriptor, hash } = preEvaluatedFont; const fontRefIsRef = fontRef instanceof _primitives.Ref; let fontID; if (fontRefIsRef) { fontID = `f${fontRef.toString()}`; } if (hash && descriptor instanceof _primitives.Dict) { if (!descriptor.fontAliases) { descriptor.fontAliases = Object.create(null); } const fontAliases = descriptor.fontAliases; if (fontAliases[hash]) { const aliasFontRef = fontAliases[hash].aliasRef; if (fontRefIsRef && aliasFontRef && this.fontCache.has(aliasFontRef)) { this.fontCache.putAlias(fontRef, aliasFontRef); return this.fontCache.get(fontRef); } } else { fontAliases[hash] = { fontID: this.idFactory.createFontId() }; } if (fontRefIsRef) { fontAliases[hash].aliasRef = fontRef; } fontID = fontAliases[hash].fontID; } if (fontRefIsRef) { this.fontCache.put(fontRef, fontCapability.promise); } else { if (!fontID) { fontID = this.idFactory.createFontId(); } font.cacheKey = `cacheKey_${fontID}`; this.fontCache.put(font.cacheKey, fontCapability.promise); } (0, _util.assert)(fontID && fontID.startsWith("f"), 'The "fontID" must be (correctly) defined.'); font.loadedName = `${this.idFactory.getDocId()}_${fontID}`; this.translateFont(preEvaluatedFont).then(translatedFont => { if (translatedFont.fontType !== undefined) { xref.stats.addFontType(translatedFont.fontType); } fontCapability.resolve(new TranslatedFont({ loadedName: font.loadedName, font: translatedFont, dict: font, evaluatorOptions: this.options })); }).catch(reason => { this.handler.send("UnsupportedFeature", { featureId: _util.UNSUPPORTED_FEATURES.errorFontTranslate }); (0, _util.warn)(`loadFont - translateFont failed: "${reason}".`); try { const fontFile3 = descriptor && descriptor.get("FontFile3"); const subtype = fontFile3 && fontFile3.get("Subtype"); const fontType = (0, _fonts_utils.getFontType)(preEvaluatedFont.type, subtype && subtype.name); if (fontType !== undefined) { xref.stats.addFontType(fontType); } } catch (ex) {} fontCapability.resolve(new TranslatedFont({ loadedName: font.loadedName, font: new _fonts.ErrorFont(reason instanceof Error ? reason.message : reason), dict: font, evaluatorOptions: this.options })); }); return fontCapability.promise; } buildPath(operatorList, fn, args, parsingText = false) { const lastIndex = operatorList.length - 1; if (!args) { args = []; } if (lastIndex < 0 || operatorList.fnArray[lastIndex] !== _util.OPS.constructPath) { if (parsingText) { (0, _util.warn)(`Encountered path operator "${fn}" inside of a text object.`); operatorList.addOp(_util.OPS.save, null); } let minMax; switch (fn) { case _util.OPS.rectangle: const x = args[0] + args[2]; const y = args[1] + args[3]; minMax = [Math.min(args[0], x), Math.max(args[0], x), Math.min(args[1], y), Math.max(args[1], y)]; break; case _util.OPS.moveTo: case _util.OPS.lineTo: minMax = [args[0], args[0], args[1], args[1]]; break; default: minMax = [Infinity, -Infinity, Infinity, -Infinity]; break; } operatorList.addOp(_util.OPS.constructPath, [[fn], args, minMax]); if (parsingText) { operatorList.addOp(_util.OPS.restore, null); } } else { const opArgs = operatorList.argsArray[lastIndex]; opArgs[0].push(fn); opArgs[1].push(...args); const minMax = opArgs[2]; switch (fn) { case _util.OPS.rectangle: const x = args[0] + args[2]; const y = args[1] + args[3]; minMax[0] = Math.min(minMax[0], args[0], x); minMax[1] = Math.max(minMax[1], args[0], x); minMax[2] = Math.min(minMax[2], args[1], y); minMax[3] = Math.max(minMax[3], args[1], y); break; case _util.OPS.moveTo: case _util.OPS.lineTo: minMax[0] = Math.min(minMax[0], args[0]); minMax[1] = Math.max(minMax[1], args[0]); minMax[2] = Math.min(minMax[2], args[1]); minMax[3] = Math.max(minMax[3], args[1]); break; } } } parseColorSpace({ cs, resources, localColorSpaceCache }) { return _colorspace.ColorSpace.parseAsync({ cs, xref: this.xref, resources, pdfFunctionFactory: this._pdfFunctionFactory, localColorSpaceCache }).catch(reason => { if (reason instanceof _util.AbortException) { return null; } if (this.options.ignoreErrors) { this.handler.send("UnsupportedFeature", { featureId: _util.UNSUPPORTED_FEATURES.errorColorSpace }); (0, _util.warn)(`parseColorSpace - ignoring ColorSpace: "${reason}".`); return null; } throw reason; }); } parseShading({ shading, resources, localColorSpaceCache, localShadingPatternCache }) { let id = localShadingPatternCache.get(shading); if (!id) { var shadingFill = _pattern.Pattern.parseShading(shading, this.xref, resources, this.handler, this._pdfFunctionFactory, localColorSpaceCache); const patternIR = shadingFill.getIR(); id = `pattern_${this.idFactory.createObjId()}`; localShadingPatternCache.set(shading, id); this.handler.send("obj", [id, this.pageIndex, "Pattern", patternIR]); } return id; } handleColorN(operatorList, fn, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache, localShadingPatternCache) { const patternName = args.pop(); if (patternName instanceof _primitives.Name) { const rawPattern = patterns.getRaw(patternName.name); const localTilingPattern = rawPattern instanceof _primitives.Ref && localTilingPatternCache.getByRef(rawPattern); if (localTilingPattern) { try { const color = cs.base ? cs.base.getRgb(args, 0) : null; const tilingPatternIR = (0, _pattern.getTilingPatternIR)(localTilingPattern.operatorListIR, localTilingPattern.dict, color); operatorList.addOp(fn, tilingPatternIR); return undefined; } catch (ex) {} } const pattern = this.xref.fetchIfRef(rawPattern); if (pattern) { const dict = pattern instanceof _base_stream.BaseStream ? pattern.dict : pattern; const typeNum = dict.get("PatternType"); if (typeNum === PatternType.TILING) { const color = cs.base ? cs.base.getRgb(args, 0) : null; return this.handleTilingType(fn, color, resources, pattern, dict, operatorList, task, localTilingPatternCache); } else if (typeNum === PatternType.SHADING) { const shading = dict.get("Shading"); const matrix = dict.getArray("Matrix"); const objId = this.parseShading({ shading, resources, localColorSpaceCache, localShadingPatternCache }); operatorList.addOp(fn, ["Shading", objId, matrix]); return undefined; } throw new _util.FormatError(`Unknown PatternType: ${typeNum}`); } } throw new _util.FormatError(`Unknown PatternName: ${patternName}`); } _parseVisibilityExpression(array, nestingCounter, currentResult) { const MAX_NESTING = 10; if (++nestingCounter > MAX_NESTING) { (0, _util.warn)("Visibility expression is too deeply nested"); return; } const length = array.length; const operator = this.xref.fetchIfRef(array[0]); if (length < 2 || !(operator instanceof _primitives.Name)) { (0, _util.warn)("Invalid visibility expression"); return; } switch (operator.name) { case "And": case "Or": case "Not": currentResult.push(operator.name); break; default: (0, _util.warn)(`Invalid operator ${operator.name} in visibility expression`); return; } for (let i = 1; i < length; i++) { const raw = array[i]; const object = this.xref.fetchIfRef(raw); if (Array.isArray(object)) { const nestedResult = []; currentResult.push(nestedResult); this._parseVisibilityExpression(object, nestingCounter, nestedResult); } else if (raw instanceof _primitives.Ref) { currentResult.push(raw.toString()); } } } async parseMarkedContentProps(contentProperties, resources) { let optionalContent; if (contentProperties instanceof _primitives.Name) { const properties = resources.get("Properties"); optionalContent = properties.get(contentProperties.name); } else if (contentProperties instanceof _primitives.Dict) { optionalContent = contentProperties; } else { throw new _util.FormatError("Optional content properties malformed."); } const optionalContentType = optionalContent.get("Type").name; if (optionalContentType === "OCG") { return { type: optionalContentType, id: optionalContent.objId }; } else if (optionalContentType === "OCMD") { const expression = optionalContent.get("VE"); if (Array.isArray(expression)) { const result = []; this._parseVisibilityExpression(expression, 0, result); if (result.length > 0) { return { type: "OCMD", expression: result }; } } const optionalContentGroups = optionalContent.get("OCGs"); if (Array.isArray(optionalContentGroups) || optionalContentGroups instanceof _primitives.Dict) { const groupIds = []; if (Array.isArray(optionalContentGroups)) { for (const ocg of optionalContentGroups) { groupIds.push(ocg.toString()); } } else { groupIds.push(optionalContentGroups.objId); } return { type: optionalContentType, ids: groupIds, policy: optionalContent.get("P") instanceof _primitives.Name ? optionalContent.get("P").name : null, expression: null }; } else if (optionalContentGroups instanceof _primitives.Ref) { return { type: optionalContentType, id: optionalContentGroups.toString() }; } } return null; } getOperatorList({ stream, task, resources, operatorList, initialState = null, fallbackFontDict = null }) { resources = resources || _primitives.Dict.empty; initialState = initialState || new EvalState(); if (!operatorList) { throw new Error('getOperatorList: missing "operatorList" parameter'); } const self = this; const xref = this.xref; let parsingText = false; const localImageCache = new _image_utils.LocalImageCache(); const localColorSpaceCache = new _image_utils.LocalColorSpaceCache(); const localGStateCache = new _image_utils.LocalGStateCache(); const localTilingPatternCache = new _image_utils.LocalTilingPatternCache(); const localShadingPatternCache = new Map(); const xobjs = resources.get("XObject") || _primitives.Dict.empty; const patterns = resources.get("Pattern") || _primitives.Dict.empty; const stateManager = new StateManager(initialState); const preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager); const timeSlotManager = new TimeSlotManager(); function closePendingRestoreOPS(argument) { for (let i = 0, ii = preprocessor.savedStatesDepth; i < ii; i++) { operatorList.addOp(_util.OPS.restore, []); } } return new Promise(function promiseBody(resolve, reject) { const next = function (promise) { Promise.all([promise, operatorList.ready]).then(function () { try { promiseBody(resolve, reject); } catch (ex) { reject(ex); } }, reject); }; task.ensureNotTerminated(); timeSlotManager.reset(); const operation = {}; let stop, i, ii, cs, name, isValidName; while (!(stop = timeSlotManager.check())) { operation.args = null; if (!preprocessor.read(operation)) { break; } let args = operation.args; let fn = operation.fn; switch (fn | 0) { case _util.OPS.paintXObject: isValidName = args[0] instanceof _primitives.Name; name = args[0].name; if (isValidName) { const localImage = localImageCache.getByName(name); if (localImage) { operatorList.addImageOps(localImage.fn, localImage.args, localImage.optionalContent); incrementCachedImageMaskCount(localImage); args = null; continue; } } next(new Promise(function (resolveXObject, rejectXObject) { if (!isValidName) { throw new _util.FormatError("XObject must be referred to by name."); } let xobj = xobjs.getRaw(name); if (xobj instanceof _primitives.Ref) { const localImage = localImageCache.getByRef(xobj); if (localImage) { operatorList.addImageOps(localImage.fn, localImage.args, localImage.optionalContent); incrementCachedImageMaskCount(localImage); resolveXObject(); return; } const globalImage = self.globalImageCache.getData(xobj, self.pageIndex); if (globalImage) { operatorList.addDependency(globalImage.objId); operatorList.addImageOps(globalImage.fn, globalImage.args, globalImage.optionalContent); resolveXObject(); return; } xobj = xref.fetch(xobj); } if (!(xobj instanceof _base_stream.BaseStream)) { throw new _util.FormatError("XObject should be a stream"); } const type = xobj.dict.get("Subtype"); if (!(type instanceof _primitives.Name)) { throw new _util.FormatError("XObject should have a Name subtype"); } if (type.name === "Form") { stateManager.save(); self.buildFormXObject(resources, xobj, null, operatorList, task, stateManager.state.clone(), localColorSpaceCache).then(function () { stateManager.restore(); resolveXObject(); }, rejectXObject); return; } else if (type.name === "Image") { self.buildPaintImageXObject({ resources, image: xobj, operatorList, cacheKey: name, localImageCache, localColorSpaceCache }).then(resolveXObject, rejectXObject); return; } else if (type.name === "PS") { (0, _util.info)("Ignored XObject subtype PS"); } else { throw new _util.FormatError(`Unhandled XObject subtype ${type.name}`); } resolveXObject(); }).catch(function (reason) { if (reason instanceof _util.AbortException) { return; } if (self.options.ignoreErrors) { self.handler.send("UnsupportedFeature", { featureId: _util.UNSUPPORTED_FEATURES.errorXObject }); (0, _util.warn)(`getOperatorList - ignoring XObject: "${reason}".`); return; } throw reason; })); return; case _util.OPS.setFont: var fontSize = args[1]; next(self.handleSetFont(resources, args, null, operatorList, task, stateManager.state, fallbackFontDict).then(function (loadedName) { operatorList.addDependency(loadedName); operatorList.addOp(_util.OPS.setFont, [loadedName, fontSize]); })); return; case _util.OPS.beginText: parsingText = true; break; case _util.OPS.endText: parsingText = false; break; case _util.OPS.endInlineImage: var cacheKey = args[0].cacheKey; if (cacheKey) { const localImage = localImageCache.getByName(cacheKey); if (localImage) { operatorList.addImageOps(localImage.fn, localImage.args, localImage.optionalContent); incrementCachedImageMaskCount(localImage); args = null; continue; } } next(self.buildPaintImageXObject({ resources, image: args[0], isInline: true, operatorList, cacheKey, localImageCache, localColorSpaceCache })); return; case _util.OPS.showText: if (!stateManager.state.font) { self.ensureStateFont(stateManager.state); continue; } args[0] = self.handleText(args[0], stateManager.state); break; case _util.OPS.showSpacedText: if (!stateManager.state.font) { self.ensureStateFont(stateManager.state); continue; } var combinedGlyphs = []; var state = stateManager.state; for (const arrItem of args[0]) { if (typeof arrItem === "string") { combinedGlyphs.push(...self.handleText(arrItem, state)); } else if (typeof arrItem === "number") { combinedGlyphs.push(arrItem); } } args[0] = combinedGlyphs; fn = _util.OPS.showText; break; case _util.OPS.nextLineShowText: if (!stateManager.state.font) { self.ensureStateFont(stateManager.state); continue; } operatorList.addOp(_util.OPS.nextLine); args[0] = self.handleText(args[0], stateManager.state); fn = _util.OPS.showText; break; case _util.OPS.nextLineSetSpacingShowText: if (!stateManager.state.font) { self.ensureStateFont(stateManager.state); continue; } operatorList.addOp(_util.OPS.nextLine); operatorList.addOp(_util.OPS.setWordSpacing, [args.shift()]); operatorList.addOp(_util.OPS.setCharSpacing, [args.shift()]); args[0] = self.handleText(args[0], stateManager.state); fn = _util.OPS.showText; break; case _util.OPS.setTextRenderingMode: stateManager.state.textRenderingMode = args[0]; break; case _util.OPS.setFillColorSpace: { const cachedColorSpace = _colorspace.ColorSpace.getCached(args[0], xref, localColorSpaceCache); if (cachedColorSpace) { stateManager.state.fillColorSpace = cachedColorSpace; continue; } next(self.parseColorSpace({ cs: args[0], resources, localColorSpaceCache }).then(function (colorSpace) { if (colorSpace) { stateManager.state.fillColorSpace = colorSpace; } })); return; } case _util.OPS.setStrokeColorSpace: { const cachedColorSpace = _colorspace.ColorSpace.getCached(args[0], xref, localColorSpaceCache); if (cachedColorSpace) { stateManager.state.strokeColorSpace = cachedColorSpace; continue; } next(self.parseColorSpace({ cs: args[0], resources, localColorSpaceCache }).then(function (colorSpace) { if (colorSpace) { stateManager.state.strokeColorSpace = colorSpace; } })); return; } case _util.OPS.setFillColor: cs = stateManager.state.fillColorSpace; args = cs.getRgb(args, 0); fn = _util.OPS.setFillRGBColor; break; case _util.OPS.setStrokeColor: cs = stateManager.state.strokeColorSpace; args = cs.getRgb(args, 0); fn = _util.OPS.setStrokeRGBColor; break; case _util.OPS.setFillGray: stateManager.state.fillColorSpace = _colorspace.ColorSpace.singletons.gray; args = _colorspace.ColorSpace.singletons.g