| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351 | /** * @licstart The following is the entire license notice for the * Javascript code in this page * * Copyright 2021 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.isPDFFunction = isPDFFunction;exports.PostScriptEvaluator = exports.PostScriptCompiler = exports.PDFFunctionFactory = void 0;var _primitives = require("./primitives.js");var _util = require("../shared/util.js");var _ps_parser = require("./ps_parser.js");var _image_utils = require("./image_utils.js");class PDFFunctionFactory {  constructor({    xref,    isEvalSupported = true  }) {    this.xref = xref;    this.isEvalSupported = isEvalSupported !== false;  }  create(fn) {    const cachedFunction = this.getCached(fn);    if (cachedFunction) {      return cachedFunction;    }    const parsedFunction = PDFFunction.parse({      xref: this.xref,      isEvalSupported: this.isEvalSupported,      fn: fn instanceof _primitives.Ref ? this.xref.fetch(fn) : fn    });    this._cache(fn, parsedFunction);    return parsedFunction;  }  createFromArray(fnObj) {    const cachedFunction = this.getCached(fnObj);    if (cachedFunction) {      return cachedFunction;    }    const parsedFunction = PDFFunction.parseArray({      xref: this.xref,      isEvalSupported: this.isEvalSupported,      fnObj: fnObj instanceof _primitives.Ref ? this.xref.fetch(fnObj) : fnObj    });    this._cache(fnObj, parsedFunction);    return parsedFunction;  }  getCached(cacheKey) {    let fnRef;    if (cacheKey instanceof _primitives.Ref) {      fnRef = cacheKey;    } else if (cacheKey instanceof _primitives.Dict) {      fnRef = cacheKey.objId;    } else if ((0, _primitives.isStream)(cacheKey)) {      fnRef = cacheKey.dict && cacheKey.dict.objId;    }    if (fnRef) {      const localFunction = this._localFunctionCache.getByRef(fnRef);      if (localFunction) {        return localFunction;      }    }    return null;  }  _cache(cacheKey, parsedFunction) {    if (!parsedFunction) {      throw new Error('PDFFunctionFactory._cache - expected "parsedFunction" argument.');    }    let fnRef;    if (cacheKey instanceof _primitives.Ref) {      fnRef = cacheKey;    } else if (cacheKey instanceof _primitives.Dict) {      fnRef = cacheKey.objId;    } else if ((0, _primitives.isStream)(cacheKey)) {      fnRef = cacheKey.dict && cacheKey.dict.objId;    }    if (fnRef) {      this._localFunctionCache.set(null, fnRef, parsedFunction);    }  }  get _localFunctionCache() {    return (0, _util.shadow)(this, "_localFunctionCache", new _image_utils.LocalFunctionCache());  }}exports.PDFFunctionFactory = PDFFunctionFactory;function toNumberArray(arr) {  if (!Array.isArray(arr)) {    return null;  }  const length = arr.length;  for (let i = 0; i < length; i++) {    if (typeof arr[i] !== "number") {      const result = new Array(length);      for (let j = 0; j < length; j++) {        result[j] = +arr[j];      }      return result;    }  }  return arr;}class PDFFunction {  static getSampleArray(size, outputSize, bps, stream) {    let i, ii;    let length = 1;    for (i = 0, ii = size.length; i < ii; i++) {      length *= size[i];    }    length *= outputSize;    const array = new Array(length);    let codeSize = 0;    let codeBuf = 0;    const sampleMul = 1.0 / (2.0 ** bps - 1);    const strBytes = stream.getBytes((length * bps + 7) / 8);    let strIdx = 0;    for (i = 0; i < length; i++) {      while (codeSize < bps) {        codeBuf <<= 8;        codeBuf |= strBytes[strIdx++];        codeSize += 8;      }      codeSize -= bps;      array[i] = (codeBuf >> codeSize) * sampleMul;      codeBuf &= (1 << codeSize) - 1;    }    return array;  }  static parse({    xref,    isEvalSupported,    fn  }) {    const dict = fn.dict || fn;    const typeNum = dict.get("FunctionType");    switch (typeNum) {      case 0:        return this.constructSampled({          xref,          isEvalSupported,          fn,          dict        });      case 1:        break;      case 2:        return this.constructInterpolated({          xref,          isEvalSupported,          dict        });      case 3:        return this.constructStiched({          xref,          isEvalSupported,          dict        });      case 4:        return this.constructPostScript({          xref,          isEvalSupported,          fn,          dict        });    }    throw new _util.FormatError("Unknown type of function");  }  static parseArray({    xref,    isEvalSupported,    fnObj  }) {    if (!Array.isArray(fnObj)) {      return this.parse({        xref,        isEvalSupported,        fn: fnObj      });    }    const fnArray = [];    for (let j = 0, jj = fnObj.length; j < jj; j++) {      fnArray.push(this.parse({        xref,        isEvalSupported,        fn: xref.fetchIfRef(fnObj[j])      }));    }    return function (src, srcOffset, dest, destOffset) {      for (let i = 0, ii = fnArray.length; i < ii; i++) {        fnArray[i](src, srcOffset, dest, destOffset + i);      }    };  }  static constructSampled({    xref,    isEvalSupported,    fn,    dict  }) {    function toMultiArray(arr) {      const inputLength = arr.length;      const out = [];      let index = 0;      for (let i = 0; i < inputLength; i += 2) {        out[index++] = [arr[i], arr[i + 1]];      }      return out;    }    function interpolate(x, xmin, xmax, ymin, ymax) {      return ymin + (x - xmin) * ((ymax - ymin) / (xmax - xmin));    }    let domain = toNumberArray(dict.getArray("Domain"));    let range = toNumberArray(dict.getArray("Range"));    if (!domain || !range) {      throw new _util.FormatError("No domain or range");    }    const inputSize = domain.length / 2;    const outputSize = range.length / 2;    domain = toMultiArray(domain);    range = toMultiArray(range);    const size = toNumberArray(dict.getArray("Size"));    const bps = dict.get("BitsPerSample");    const order = dict.get("Order") || 1;    if (order !== 1) {      (0, _util.info)("No support for cubic spline interpolation: " + order);    }    let encode = toNumberArray(dict.getArray("Encode"));    if (!encode) {      encode = [];      for (let i = 0; i < inputSize; ++i) {        encode.push([0, size[i] - 1]);      }    } else {      encode = toMultiArray(encode);    }    let decode = toNumberArray(dict.getArray("Decode"));    if (!decode) {      decode = range;    } else {      decode = toMultiArray(decode);    }    const samples = this.getSampleArray(size, outputSize, bps, fn);    return function constructSampledFn(src, srcOffset, dest, destOffset) {      const cubeVertices = 1 << inputSize;      const cubeN = new Float64Array(cubeVertices);      const cubeVertex = new Uint32Array(cubeVertices);      let i, j;      for (j = 0; j < cubeVertices; j++) {        cubeN[j] = 1;      }      let k = outputSize,          pos = 1;      for (i = 0; i < inputSize; ++i) {        const domain_2i = domain[i][0];        const domain_2i_1 = domain[i][1];        const xi = Math.min(Math.max(src[srcOffset + i], domain_2i), domain_2i_1);        let e = interpolate(xi, domain_2i, domain_2i_1, encode[i][0], encode[i][1]);        const size_i = size[i];        e = Math.min(Math.max(e, 0), size_i - 1);        const e0 = e < size_i - 1 ? Math.floor(e) : e - 1;        const n0 = e0 + 1 - e;        const n1 = e - e0;        const offset0 = e0 * k;        const offset1 = offset0 + k;        for (j = 0; j < cubeVertices; j++) {          if (j & pos) {            cubeN[j] *= n1;            cubeVertex[j] += offset1;          } else {            cubeN[j] *= n0;            cubeVertex[j] += offset0;          }        }        k *= size_i;        pos <<= 1;      }      for (j = 0; j < outputSize; ++j) {        let rj = 0;        for (i = 0; i < cubeVertices; i++) {          rj += samples[cubeVertex[i] + j] * cubeN[i];        }        rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);        dest[destOffset + j] = Math.min(Math.max(rj, range[j][0]), range[j][1]);      }    };  }  static constructInterpolated({    xref,    isEvalSupported,    dict  }) {    const c0 = toNumberArray(dict.getArray("C0")) || [0];    const c1 = toNumberArray(dict.getArray("C1")) || [1];    const n = dict.get("N");    const diff = [];    for (let i = 0, ii = c0.length; i < ii; ++i) {      diff.push(c1[i] - c0[i]);    }    const length = diff.length;    return function constructInterpolatedFn(src, srcOffset, dest, destOffset) {      const x = n === 1 ? src[srcOffset] : src[srcOffset] ** n;      for (let j = 0; j < length; ++j) {        dest[destOffset + j] = c0[j] + x * diff[j];      }    };  }  static constructStiched({    xref,    isEvalSupported,    dict  }) {    const domain = toNumberArray(dict.getArray("Domain"));    if (!domain) {      throw new _util.FormatError("No domain");    }    const inputSize = domain.length / 2;    if (inputSize !== 1) {      throw new _util.FormatError("Bad domain for stiched function");    }    const fnRefs = dict.get("Functions");    const fns = [];    for (let i = 0, ii = fnRefs.length; i < ii; ++i) {      fns.push(this.parse({        xref,        isEvalSupported,        fn: xref.fetchIfRef(fnRefs[i])      }));    }    const bounds = toNumberArray(dict.getArray("Bounds"));    const encode = toNumberArray(dict.getArray("Encode"));    const tmpBuf = new Float32Array(1);    return function constructStichedFn(src, srcOffset, dest, destOffset) {      const clip = function constructStichedFromIRClip(v, min, max) {        if (v > max) {          v = max;        } else if (v < min) {          v = min;        }        return v;      };      const v = clip(src[srcOffset], domain[0], domain[1]);      const length = bounds.length;      let i;      for (i = 0; i < length; ++i) {        if (v < bounds[i]) {          break;        }      }      let dmin = domain[0];      if (i > 0) {        dmin = bounds[i - 1];      }      let dmax = domain[1];      if (i < bounds.length) {        dmax = bounds[i];      }      const rmin = encode[2 * i];      const rmax = encode[2 * i + 1];      tmpBuf[0] = dmin === dmax ? rmin : rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);      fns[i](tmpBuf, 0, dest, destOffset);    };  }  static constructPostScript({    xref,    isEvalSupported,    fn,    dict  }) {    const domain = toNumberArray(dict.getArray("Domain"));    const range = toNumberArray(dict.getArray("Range"));    if (!domain) {      throw new _util.FormatError("No domain.");    }    if (!range) {      throw new _util.FormatError("No range.");    }    const lexer = new _ps_parser.PostScriptLexer(fn);    const parser = new _ps_parser.PostScriptParser(lexer);    const code = parser.parse();    if (isEvalSupported && _util.IsEvalSupportedCached.value) {      const compiled = new PostScriptCompiler().compile(code, domain, range);      if (compiled) {        return new Function("src", "srcOffset", "dest", "destOffset", compiled);      }    }    (0, _util.info)("Unable to compile PS function");    const numOutputs = range.length >> 1;    const numInputs = domain.length >> 1;    const evaluator = new PostScriptEvaluator(code);    const cache = Object.create(null);    const MAX_CACHE_SIZE = 2048 * 4;    let cache_available = MAX_CACHE_SIZE;    const tmpBuf = new Float32Array(numInputs);    return function constructPostScriptFn(src, srcOffset, dest, destOffset) {      let i, value;      let key = "";      const input = tmpBuf;      for (i = 0; i < numInputs; i++) {        value = src[srcOffset + i];        input[i] = value;        key += value + "_";      }      const cachedValue = cache[key];      if (cachedValue !== undefined) {        dest.set(cachedValue, destOffset);        return;      }      const output = new Float32Array(numOutputs);      const stack = evaluator.execute(input);      const stackIndex = stack.length - numOutputs;      for (i = 0; i < numOutputs; i++) {        value = stack[stackIndex + i];        let bound = range[i * 2];        if (value < bound) {          value = bound;        } else {          bound = range[i * 2 + 1];          if (value > bound) {            value = bound;          }        }        output[i] = value;      }      if (cache_available > 0) {        cache_available--;        cache[key] = output;      }      dest.set(output, destOffset);    };  }}function isPDFFunction(v) {  let fnDict;  if (typeof v !== "object") {    return false;  } else if ((0, _primitives.isDict)(v)) {    fnDict = v;  } else if ((0, _primitives.isStream)(v)) {    fnDict = v.dict;  } else {    return false;  }  return fnDict.has("FunctionType");}class PostScriptStack {  static get MAX_STACK_SIZE() {    return (0, _util.shadow)(this, "MAX_STACK_SIZE", 100);  }  constructor(initialStack) {    this.stack = !initialStack ? [] : Array.prototype.slice.call(initialStack, 0);  }  push(value) {    if (this.stack.length >= PostScriptStack.MAX_STACK_SIZE) {      throw new Error("PostScript function stack overflow.");    }    this.stack.push(value);  }  pop() {    if (this.stack.length <= 0) {      throw new Error("PostScript function stack underflow.");    }    return this.stack.pop();  }  copy(n) {    if (this.stack.length + n >= PostScriptStack.MAX_STACK_SIZE) {      throw new Error("PostScript function stack overflow.");    }    const stack = this.stack;    for (let i = stack.length - n, j = n - 1; j >= 0; j--, i++) {      stack.push(stack[i]);    }  }  index(n) {    this.push(this.stack[this.stack.length - n - 1]);  }  roll(n, p) {    const stack = this.stack;    const l = stack.length - n;    const r = stack.length - 1;    const c = l + (p - Math.floor(p / n) * n);    for (let i = l, j = r; i < j; i++, j--) {      const t = stack[i];      stack[i] = stack[j];      stack[j] = t;    }    for (let i = l, j = c - 1; i < j; i++, j--) {      const t = stack[i];      stack[i] = stack[j];      stack[j] = t;    }    for (let i = c, j = r; i < j; i++, j--) {      const t = stack[i];      stack[i] = stack[j];      stack[j] = t;    }  }}class PostScriptEvaluator {  constructor(operators) {    this.operators = operators;  }  execute(initialStack) {    const stack = new PostScriptStack(initialStack);    let counter = 0;    const operators = this.operators;    const length = operators.length;    let operator, a, b;    while (counter < length) {      operator = operators[counter++];      if (typeof operator === "number") {        stack.push(operator);        continue;      }      switch (operator) {        case "jz":          b = stack.pop();          a = stack.pop();          if (!a) {            counter = b;          }          break;        case "j":          a = stack.pop();          counter = a;          break;        case "abs":          a = stack.pop();          stack.push(Math.abs(a));          break;        case "add":          b = stack.pop();          a = stack.pop();          stack.push(a + b);          break;        case "and":          b = stack.pop();          a = stack.pop();          if ((0, _util.isBool)(a) && (0, _util.isBool)(b)) {            stack.push(a && b);          } else {            stack.push(a & b);          }          break;        case "atan":          a = stack.pop();          stack.push(Math.atan(a));          break;        case "bitshift":          b = stack.pop();          a = stack.pop();          if (a > 0) {            stack.push(a << b);          } else {            stack.push(a >> b);          }          break;        case "ceiling":          a = stack.pop();          stack.push(Math.ceil(a));          break;        case "copy":          a = stack.pop();          stack.copy(a);          break;        case "cos":          a = stack.pop();          stack.push(Math.cos(a));          break;        case "cvi":          a = stack.pop() | 0;          stack.push(a);          break;        case "cvr":          break;        case "div":          b = stack.pop();          a = stack.pop();          stack.push(a / b);          break;        case "dup":          stack.copy(1);          break;        case "eq":          b = stack.pop();          a = stack.pop();          stack.push(a === b);          break;        case "exch":          stack.roll(2, 1);          break;        case "exp":          b = stack.pop();          a = stack.pop();          stack.push(a ** b);          break;        case "false":          stack.push(false);          break;        case "floor":          a = stack.pop();          stack.push(Math.floor(a));          break;        case "ge":          b = stack.pop();          a = stack.pop();          stack.push(a >= b);          break;        case "gt":          b = stack.pop();          a = stack.pop();          stack.push(a > b);          break;        case "idiv":          b = stack.pop();          a = stack.pop();          stack.push(a / b | 0);          break;        case "index":          a = stack.pop();          stack.index(a);          break;        case "le":          b = stack.pop();          a = stack.pop();          stack.push(a <= b);          break;        case "ln":          a = stack.pop();          stack.push(Math.log(a));          break;        case "log":          a = stack.pop();          stack.push(Math.log(a) / Math.LN10);          break;        case "lt":          b = stack.pop();          a = stack.pop();          stack.push(a < b);          break;        case "mod":          b = stack.pop();          a = stack.pop();          stack.push(a % b);          break;        case "mul":          b = stack.pop();          a = stack.pop();          stack.push(a * b);          break;        case "ne":          b = stack.pop();          a = stack.pop();          stack.push(a !== b);          break;        case "neg":          a = stack.pop();          stack.push(-a);          break;        case "not":          a = stack.pop();          if ((0, _util.isBool)(a)) {            stack.push(!a);          } else {            stack.push(~a);          }          break;        case "or":          b = stack.pop();          a = stack.pop();          if ((0, _util.isBool)(a) && (0, _util.isBool)(b)) {            stack.push(a || b);          } else {            stack.push(a | b);          }          break;        case "pop":          stack.pop();          break;        case "roll":          b = stack.pop();          a = stack.pop();          stack.roll(a, b);          break;        case "round":          a = stack.pop();          stack.push(Math.round(a));          break;        case "sin":          a = stack.pop();          stack.push(Math.sin(a));          break;        case "sqrt":          a = stack.pop();          stack.push(Math.sqrt(a));          break;        case "sub":          b = stack.pop();          a = stack.pop();          stack.push(a - b);          break;        case "true":          stack.push(true);          break;        case "truncate":          a = stack.pop();          a = a < 0 ? Math.ceil(a) : Math.floor(a);          stack.push(a);          break;        case "xor":          b = stack.pop();          a = stack.pop();          if ((0, _util.isBool)(a) && (0, _util.isBool)(b)) {            stack.push(a !== b);          } else {            stack.push(a ^ b);          }          break;        default:          throw new _util.FormatError(`Unknown operator ${operator}`);      }    }    return stack.stack;  }}exports.PostScriptEvaluator = PostScriptEvaluator;const PostScriptCompiler = function PostScriptCompilerClosure() {  class AstNode {    constructor(type) {      this.type = type;    }    visit(visitor) {      (0, _util.unreachable)("abstract method");    }  }  class AstArgument extends AstNode {    constructor(index, min, max) {      super("args");      this.index = index;      this.min = min;      this.max = max;    }    visit(visitor) {      visitor.visitArgument(this);    }  }  class AstLiteral extends AstNode {    constructor(number) {      super("literal");      this.number = number;      this.min = number;      this.max = number;    }    visit(visitor) {      visitor.visitLiteral(this);    }  }  class AstBinaryOperation extends AstNode {    constructor(op, arg1, arg2, min, max) {      super("binary");      this.op = op;      this.arg1 = arg1;      this.arg2 = arg2;      this.min = min;      this.max = max;    }    visit(visitor) {      visitor.visitBinaryOperation(this);    }  }  class AstMin extends AstNode {    constructor(arg, max) {      super("max");      this.arg = arg;      this.min = arg.min;      this.max = max;    }    visit(visitor) {      visitor.visitMin(this);    }  }  class AstVariable extends AstNode {    constructor(index, min, max) {      super("var");      this.index = index;      this.min = min;      this.max = max;    }    visit(visitor) {      visitor.visitVariable(this);    }  }  class AstVariableDefinition extends AstNode {    constructor(variable, arg) {      super("definition");      this.variable = variable;      this.arg = arg;    }    visit(visitor) {      visitor.visitVariableDefinition(this);    }  }  class ExpressionBuilderVisitor {    constructor() {      this.parts = [];    }    visitArgument(arg) {      this.parts.push("Math.max(", arg.min, ", Math.min(", arg.max, ", src[srcOffset + ", arg.index, "]))");    }    visitVariable(variable) {      this.parts.push("v", variable.index);    }    visitLiteral(literal) {      this.parts.push(literal.number);    }    visitBinaryOperation(operation) {      this.parts.push("(");      operation.arg1.visit(this);      this.parts.push(" ", operation.op, " ");      operation.arg2.visit(this);      this.parts.push(")");    }    visitVariableDefinition(definition) {      this.parts.push("var ");      definition.variable.visit(this);      this.parts.push(" = ");      definition.arg.visit(this);      this.parts.push(";");    }    visitMin(max) {      this.parts.push("Math.min(");      max.arg.visit(this);      this.parts.push(", ", max.max, ")");    }    toString() {      return this.parts.join("");    }  }  function buildAddOperation(num1, num2) {    if (num2.type === "literal" && num2.number === 0) {      return num1;    }    if (num1.type === "literal" && num1.number === 0) {      return num2;    }    if (num2.type === "literal" && num1.type === "literal") {      return new AstLiteral(num1.number + num2.number);    }    return new AstBinaryOperation("+", num1, num2, num1.min + num2.min, num1.max + num2.max);  }  function buildMulOperation(num1, num2) {    if (num2.type === "literal") {      if (num2.number === 0) {        return new AstLiteral(0);      } else if (num2.number === 1) {        return num1;      } else if (num1.type === "literal") {        return new AstLiteral(num1.number * num2.number);      }    }    if (num1.type === "literal") {      if (num1.number === 0) {        return new AstLiteral(0);      } else if (num1.number === 1) {        return num2;      }    }    const min = Math.min(num1.min * num2.min, num1.min * num2.max, num1.max * num2.min, num1.max * num2.max);    const max = Math.max(num1.min * num2.min, num1.min * num2.max, num1.max * num2.min, num1.max * num2.max);    return new AstBinaryOperation("*", num1, num2, min, max);  }  function buildSubOperation(num1, num2) {    if (num2.type === "literal") {      if (num2.number === 0) {        return num1;      } else if (num1.type === "literal") {        return new AstLiteral(num1.number - num2.number);      }    }    if (num2.type === "binary" && num2.op === "-" && num1.type === "literal" && num1.number === 1 && num2.arg1.type === "literal" && num2.arg1.number === 1) {      return num2.arg2;    }    return new AstBinaryOperation("-", num1, num2, num1.min - num2.max, num1.max - num2.min);  }  function buildMinOperation(num1, max) {    if (num1.min >= max) {      return new AstLiteral(max);    } else if (num1.max <= max) {      return num1;    }    return new AstMin(num1, max);  }  class PostScriptCompiler {    compile(code, domain, range) {      const stack = [];      const instructions = [];      const inputSize = domain.length >> 1,            outputSize = range.length >> 1;      let lastRegister = 0;      let n, j;      let num1, num2, ast1, ast2, tmpVar, item;      for (let i = 0; i < inputSize; i++) {        stack.push(new AstArgument(i, domain[i * 2], domain[i * 2 + 1]));      }      for (let i = 0, ii = code.length; i < ii; i++) {        item = code[i];        if (typeof item === "number") {          stack.push(new AstLiteral(item));          continue;        }        switch (item) {          case "add":            if (stack.length < 2) {              return null;            }            num2 = stack.pop();            num1 = stack.pop();            stack.push(buildAddOperation(num1, num2));            break;          case "cvr":            if (stack.length < 1) {              return null;            }            break;          case "mul":            if (stack.length < 2) {              return null;            }            num2 = stack.pop();            num1 = stack.pop();            stack.push(buildMulOperation(num1, num2));            break;          case "sub":            if (stack.length < 2) {              return null;            }            num2 = stack.pop();            num1 = stack.pop();            stack.push(buildSubOperation(num1, num2));            break;          case "exch":            if (stack.length < 2) {              return null;            }            ast1 = stack.pop();            ast2 = stack.pop();            stack.push(ast1, ast2);            break;          case "pop":            if (stack.length < 1) {              return null;            }            stack.pop();            break;          case "index":            if (stack.length < 1) {              return null;            }            num1 = stack.pop();            if (num1.type !== "literal") {              return null;            }            n = num1.number;            if (n < 0 || !Number.isInteger(n) || stack.length < n) {              return null;            }            ast1 = stack[stack.length - n - 1];            if (ast1.type === "literal" || ast1.type === "var") {              stack.push(ast1);              break;            }            tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);            stack[stack.length - n - 1] = tmpVar;            stack.push(tmpVar);            instructions.push(new AstVariableDefinition(tmpVar, ast1));            break;          case "dup":            if (stack.length < 1) {              return null;            }            if (typeof code[i + 1] === "number" && code[i + 2] === "gt" && code[i + 3] === i + 7 && code[i + 4] === "jz" && code[i + 5] === "pop" && code[i + 6] === code[i + 1]) {              num1 = stack.pop();              stack.push(buildMinOperation(num1, code[i + 1]));              i += 6;              break;            }            ast1 = stack[stack.length - 1];            if (ast1.type === "literal" || ast1.type === "var") {              stack.push(ast1);              break;            }            tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);            stack[stack.length - 1] = tmpVar;            stack.push(tmpVar);            instructions.push(new AstVariableDefinition(tmpVar, ast1));            break;          case "roll":            if (stack.length < 2) {              return null;            }            num2 = stack.pop();            num1 = stack.pop();            if (num2.type !== "literal" || num1.type !== "literal") {              return null;            }            j = num2.number;            n = num1.number;            if (n <= 0 || !Number.isInteger(n) || !Number.isInteger(j) || stack.length < n) {              return null;            }            j = (j % n + n) % n;            if (j === 0) {              break;            }            Array.prototype.push.apply(stack, stack.splice(stack.length - n, n - j));            break;          default:            return null;        }      }      if (stack.length !== outputSize) {        return null;      }      const result = [];      for (const instruction of instructions) {        const statementBuilder = new ExpressionBuilderVisitor();        instruction.visit(statementBuilder);        result.push(statementBuilder.toString());      }      for (let i = 0, ii = stack.length; i < ii; i++) {        const expr = stack[i],              statementBuilder = new ExpressionBuilderVisitor();        expr.visit(statementBuilder);        const min = range[i * 2],              max = range[i * 2 + 1];        const out = [statementBuilder.toString()];        if (min > expr.min) {          out.unshift("Math.max(", min, ", ");          out.push(")");        }        if (max < expr.max) {          out.unshift("Math.min(", max, ", ");          out.push(")");        }        out.unshift("dest[destOffset + ", i, "] = ");        out.push(";");        result.push(out.join(""));      }      return result.join("\n");    }  }  return PostScriptCompiler;}();exports.PostScriptCompiler = PostScriptCompiler;
 |