pdfjs-dist
Version:
Generic build of Mozilla's PDF.js library.
1,431 lines (1,430 loc) • 132 kB
JavaScript
/**
* @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