yanchengPowerSupply/node_modules/pdfjs-dist/lib/core/image.js

764 lines
19 KiB
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.PDFImage = void 0;
var _util = require("../shared/util.js");
var _image_utils = require("../shared/image_utils.js");
var _base_stream = require("./base_stream.js");
var _colorspace = require("./colorspace.js");
var _decode_stream = require("./decode_stream.js");
var _jpeg_stream = require("./jpeg_stream.js");
var _jpx = require("./jpx.js");
var _primitives = require("./primitives.js");
function decodeAndClamp(value, addend, coefficient, max) {
value = addend + value * coefficient;
if (value < 0) {
value = 0;
} else if (value > max) {
value = max;
}
return value;
}
function resizeImageMask(src, bpc, w1, h1, w2, h2) {
const length = w2 * h2;
let dest;
if (bpc <= 8) {
dest = new Uint8Array(length);
} else if (bpc <= 16) {
dest = new Uint16Array(length);
} else {
dest = new Uint32Array(length);
}
const xRatio = w1 / w2;
const yRatio = h1 / h2;
let i,
j,
py,
newIndex = 0,
oldIndex;
const xScaled = new Uint16Array(w2);
const w1Scanline = w1;
for (i = 0; i < w2; i++) {
xScaled[i] = Math.floor(i * xRatio);
}
for (i = 0; i < h2; i++) {
py = Math.floor(i * yRatio) * w1Scanline;
for (j = 0; j < w2; j++) {
oldIndex = py + xScaled[j];
dest[newIndex++] = src[oldIndex];
}
}
return dest;
}
class PDFImage {
constructor({
xref,
res,
image,
isInline = false,
smask = null,
mask = null,
isMask = false,
pdfFunctionFactory,
localColorSpaceCache
}) {
this.image = image;
const dict = image.dict;
const filter = dict.get("F", "Filter");
let filterName;
if (filter instanceof _primitives.Name) {
filterName = filter.name;
} else if (Array.isArray(filter)) {
const filterZero = xref.fetchIfRef(filter[0]);
if (filterZero instanceof _primitives.Name) {
filterName = filterZero.name;
}
}
switch (filterName) {
case "JPXDecode":
const jpxImage = new _jpx.JpxImage();
jpxImage.parseImageProperties(image.stream);
image.stream.reset();
image.width = jpxImage.width;
image.height = jpxImage.height;
image.bitsPerComponent = jpxImage.bitsPerComponent;
image.numComps = jpxImage.componentsCount;
break;
case "JBIG2Decode":
image.bitsPerComponent = 1;
image.numComps = 1;
break;
}
let width = dict.get("W", "Width");
let height = dict.get("H", "Height");
if (Number.isInteger(image.width) && image.width > 0 && Number.isInteger(image.height) && image.height > 0 && (image.width !== width || image.height !== height)) {
(0, _util.warn)("PDFImage - using the Width/Height of the image data, " + "rather than the image dictionary.");
width = image.width;
height = image.height;
}
if (width < 1 || height < 1) {
throw new _util.FormatError(`Invalid image width: ${width} or height: ${height}`);
}
this.width = width;
this.height = height;
this.interpolate = dict.get("I", "Interpolate");
this.imageMask = dict.get("IM", "ImageMask") || false;
this.matte = dict.get("Matte") || false;
let bitsPerComponent = image.bitsPerComponent;
if (!bitsPerComponent) {
bitsPerComponent = dict.get("BPC", "BitsPerComponent");
if (!bitsPerComponent) {
if (this.imageMask) {
bitsPerComponent = 1;
} else {
throw new _util.FormatError(`Bits per component missing in image: ${this.imageMask}`);
}
}
}
this.bpc = bitsPerComponent;
if (!this.imageMask) {
let colorSpace = dict.getRaw("CS") || dict.getRaw("ColorSpace");
if (!colorSpace) {
(0, _util.info)("JPX images (which do not require color spaces)");
switch (image.numComps) {
case 1:
colorSpace = _primitives.Name.get("DeviceGray");
break;
case 3:
colorSpace = _primitives.Name.get("DeviceRGB");
break;
case 4:
colorSpace = _primitives.Name.get("DeviceCMYK");
break;
default:
throw new Error(`JPX images with ${image.numComps} color components not supported.`);
}
}
this.colorSpace = _colorspace.ColorSpace.parse({
cs: colorSpace,
xref,
resources: isInline ? res : null,
pdfFunctionFactory,
localColorSpaceCache
});
this.numComps = this.colorSpace.numComps;
}
this.decode = dict.getArray("D", "Decode");
this.needsDecode = false;
if (this.decode && (this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode, bitsPerComponent) || isMask && !_colorspace.ColorSpace.isDefaultDecode(this.decode, 1))) {
this.needsDecode = true;
const max = (1 << bitsPerComponent) - 1;
this.decodeCoefficients = [];
this.decodeAddends = [];
const isIndexed = this.colorSpace && this.colorSpace.name === "Indexed";
for (let i = 0, j = 0; i < this.decode.length; i += 2, ++j) {
const dmin = this.decode[i];
const dmax = this.decode[i + 1];
this.decodeCoefficients[j] = isIndexed ? (dmax - dmin) / max : dmax - dmin;
this.decodeAddends[j] = isIndexed ? dmin : max * dmin;
}
}
if (smask) {
this.smask = new PDFImage({
xref,
res,
image: smask,
isInline,
pdfFunctionFactory,
localColorSpaceCache
});
} else if (mask) {
if (mask instanceof _base_stream.BaseStream) {
const maskDict = mask.dict,
imageMask = maskDict.get("IM", "ImageMask");
if (!imageMask) {
(0, _util.warn)("Ignoring /Mask in image without /ImageMask.");
} else {
this.mask = new PDFImage({
xref,
res,
image: mask,
isInline,
isMask: true,
pdfFunctionFactory,
localColorSpaceCache
});
}
} else {
this.mask = mask;
}
}
}
static async buildImage({
xref,
res,
image,
isInline = false,
pdfFunctionFactory,
localColorSpaceCache
}) {
const imageData = image;
let smaskData = null;
let maskData = null;
const smask = image.dict.get("SMask");
const mask = image.dict.get("Mask");
if (smask) {
if (smask instanceof _base_stream.BaseStream) {
smaskData = smask;
} else {
(0, _util.warn)("Unsupported /SMask format.");
}
} else if (mask) {
if (mask instanceof _base_stream.BaseStream || Array.isArray(mask)) {
maskData = mask;
} else {
(0, _util.warn)("Unsupported /Mask format.");
}
}
return new PDFImage({
xref,
res,
image: imageData,
isInline,
smask: smaskData,
mask: maskData,
pdfFunctionFactory,
localColorSpaceCache
});
}
static createRawMask({
imgArray,
width,
height,
imageIsFromDecodeStream,
inverseDecode,
interpolate
}) {
const computedLength = (width + 7 >> 3) * height;
const actualLength = imgArray.byteLength;
const haveFullData = computedLength === actualLength;
let data, i;
if (imageIsFromDecodeStream && (!inverseDecode || haveFullData)) {
data = imgArray;
} else if (!inverseDecode) {
data = new Uint8Array(imgArray);
} else {
data = new Uint8Array(computedLength);
data.set(imgArray);
data.fill(0xff, actualLength);
}
if (inverseDecode) {
for (i = 0; i < actualLength; i++) {
data[i] ^= 0xff;
}
}
return {
data,
width,
height,
interpolate
};
}
static createMask({
imgArray,
width,
height,
imageIsFromDecodeStream,
inverseDecode,
interpolate
}) {
const isSingleOpaquePixel = width === 1 && height === 1 && inverseDecode === (imgArray.length === 0 || !!(imgArray[0] & 128));
if (isSingleOpaquePixel) {
return {
isSingleOpaquePixel
};
}
if (_util.FeatureTest.isOffscreenCanvasSupported) {
const canvas = new OffscreenCanvas(width, height);
const ctx = canvas.getContext("2d");
const imgData = ctx.createImageData(width, height);
(0, _image_utils.applyMaskImageData)({
src: imgArray,
dest: imgData.data,
width,
height,
inverseDecode
});
ctx.putImageData(imgData, 0, 0);
const bitmap = canvas.transferToImageBitmap();
return {
data: null,
width,
height,
interpolate,
bitmap
};
}
return this.createRawMask({
imgArray,
width,
height,
inverseDecode,
imageIsFromDecodeStream,
interpolate
});
}
get drawWidth() {
return Math.max(this.width, this.smask && this.smask.width || 0, this.mask && this.mask.width || 0);
}
get drawHeight() {
return Math.max(this.height, this.smask && this.smask.height || 0, this.mask && this.mask.height || 0);
}
decodeBuffer(buffer) {
const bpc = this.bpc;
const numComps = this.numComps;
const decodeAddends = this.decodeAddends;
const decodeCoefficients = this.decodeCoefficients;
const max = (1 << bpc) - 1;
let i, ii;
if (bpc === 1) {
for (i = 0, ii = buffer.length; i < ii; i++) {
buffer[i] = +!buffer[i];
}
return;
}
let index = 0;
for (i = 0, ii = this.width * this.height; i < ii; i++) {
for (let j = 0; j < numComps; j++) {
buffer[index] = decodeAndClamp(buffer[index], decodeAddends[j], decodeCoefficients[j], max);
index++;
}
}
}
getComponents(buffer) {
const bpc = this.bpc;
if (bpc === 8) {
return buffer;
}
const width = this.width;
const height = this.height;
const numComps = this.numComps;
const length = width * height * numComps;
let bufferPos = 0;
let output;
if (bpc <= 8) {
output = new Uint8Array(length);
} else if (bpc <= 16) {
output = new Uint16Array(length);
} else {
output = new Uint32Array(length);
}
const rowComps = width * numComps;
const max = (1 << bpc) - 1;
let i = 0,
ii,
buf;
if (bpc === 1) {
let mask, loop1End, loop2End;
for (let j = 0; j < height; j++) {
loop1End = i + (rowComps & ~7);
loop2End = i + rowComps;
while (i < loop1End) {
buf = buffer[bufferPos++];
output[i] = buf >> 7 & 1;
output[i + 1] = buf >> 6 & 1;
output[i + 2] = buf >> 5 & 1;
output[i + 3] = buf >> 4 & 1;
output[i + 4] = buf >> 3 & 1;
output[i + 5] = buf >> 2 & 1;
output[i + 6] = buf >> 1 & 1;
output[i + 7] = buf & 1;
i += 8;
}
if (i < loop2End) {
buf = buffer[bufferPos++];
mask = 128;
while (i < loop2End) {
output[i++] = +!!(buf & mask);
mask >>= 1;
}
}
}
} else {
let bits = 0;
buf = 0;
for (i = 0, ii = length; i < ii; ++i) {
if (i % rowComps === 0) {
buf = 0;
bits = 0;
}
while (bits < bpc) {
buf = buf << 8 | buffer[bufferPos++];
bits += 8;
}
const remainingBits = bits - bpc;
let value = buf >> remainingBits;
if (value < 0) {
value = 0;
} else if (value > max) {
value = max;
}
output[i] = value;
buf &= (1 << remainingBits) - 1;
bits = remainingBits;
}
}
return output;
}
fillOpacity(rgbaBuf, width, height, actualHeight, image) {
const smask = this.smask;
const mask = this.mask;
let alphaBuf, sw, sh, i, ii, j;
if (smask) {
sw = smask.width;
sh = smask.height;
alphaBuf = new Uint8ClampedArray(sw * sh);
smask.fillGrayBuffer(alphaBuf);
if (sw !== width || sh !== height) {
alphaBuf = resizeImageMask(alphaBuf, smask.bpc, sw, sh, width, height);
}
} else if (mask) {
if (mask instanceof PDFImage) {
sw = mask.width;
sh = mask.height;
alphaBuf = new Uint8ClampedArray(sw * sh);
mask.numComps = 1;
mask.fillGrayBuffer(alphaBuf);
for (i = 0, ii = sw * sh; i < ii; ++i) {
alphaBuf[i] = 255 - alphaBuf[i];
}
if (sw !== width || sh !== height) {
alphaBuf = resizeImageMask(alphaBuf, mask.bpc, sw, sh, width, height);
}
} else if (Array.isArray(mask)) {
alphaBuf = new Uint8ClampedArray(width * height);
const numComps = this.numComps;
for (i = 0, ii = width * height; i < ii; ++i) {
let opacity = 0;
const imageOffset = i * numComps;
for (j = 0; j < numComps; ++j) {
const color = image[imageOffset + j];
const maskOffset = j * 2;
if (color < mask[maskOffset] || color > mask[maskOffset + 1]) {
opacity = 255;
break;
}
}
alphaBuf[i] = opacity;
}
} else {
throw new _util.FormatError("Unknown mask format.");
}
}
if (alphaBuf) {
for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {
rgbaBuf[j] = alphaBuf[i];
}
} else {
for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {
rgbaBuf[j] = 255;
}
}
}
undoPreblend(buffer, width, height) {
const matte = this.smask && this.smask.matte;
if (!matte) {
return;
}
const matteRgb = this.colorSpace.getRgb(matte, 0);
const matteR = matteRgb[0];
const matteG = matteRgb[1];
const matteB = matteRgb[2];
const length = width * height * 4;
for (let i = 0; i < length; i += 4) {
const alpha = buffer[i + 3];
if (alpha === 0) {
buffer[i] = 255;
buffer[i + 1] = 255;
buffer[i + 2] = 255;
continue;
}
const k = 255 / alpha;
buffer[i] = (buffer[i] - matteR) * k + matteR;
buffer[i + 1] = (buffer[i + 1] - matteG) * k + matteG;
buffer[i + 2] = (buffer[i + 2] - matteB) * k + matteB;
}
}
createImageData(forceRGBA = false) {
const drawWidth = this.drawWidth;
const drawHeight = this.drawHeight;
const imgData = {
width: drawWidth,
height: drawHeight,
interpolate: this.interpolate,
kind: 0,
data: null
};
const numComps = this.numComps;
const originalWidth = this.width;
const originalHeight = this.height;
const bpc = this.bpc;
const rowBytes = originalWidth * numComps * bpc + 7 >> 3;
if (!forceRGBA) {
let kind;
if (this.colorSpace.name === "DeviceGray" && bpc === 1) {
kind = _util.ImageKind.GRAYSCALE_1BPP;
} else if (this.colorSpace.name === "DeviceRGB" && bpc === 8 && !this.needsDecode) {
kind = _util.ImageKind.RGB_24BPP;
}
if (kind && !this.smask && !this.mask && drawWidth === originalWidth && drawHeight === originalHeight) {
imgData.kind = kind;
imgData.data = this.getImageBytes(originalHeight * rowBytes, {});
if (this.needsDecode) {
(0, _util.assert)(kind === _util.ImageKind.GRAYSCALE_1BPP, "PDFImage.createImageData: The image must be grayscale.");
const buffer = imgData.data;
for (let i = 0, ii = buffer.length; i < ii; i++) {
buffer[i] ^= 0xff;
}
}
return imgData;
}
if (this.image instanceof _jpeg_stream.JpegStream && !this.smask && !this.mask) {
let imageLength = originalHeight * rowBytes;
switch (this.colorSpace.name) {
case "DeviceGray":
imageLength *= 3;
case "DeviceRGB":
case "DeviceCMYK":
imgData.kind = _util.ImageKind.RGB_24BPP;
imgData.data = this.getImageBytes(imageLength, {
drawWidth,
drawHeight,
forceRGB: true
});
return imgData;
}
}
}
const imgArray = this.getImageBytes(originalHeight * rowBytes, {
internal: true
});
const actualHeight = 0 | imgArray.length / rowBytes * drawHeight / originalHeight;
const comps = this.getComponents(imgArray);
let alpha01, maybeUndoPreblend;
if (!forceRGBA && !this.smask && !this.mask) {
imgData.kind = _util.ImageKind.RGB_24BPP;
imgData.data = new Uint8ClampedArray(drawWidth * drawHeight * 3);
alpha01 = 0;
maybeUndoPreblend = false;
} else {
imgData.kind = _util.ImageKind.RGBA_32BPP;
imgData.data = new Uint8ClampedArray(drawWidth * drawHeight * 4);
alpha01 = 1;
maybeUndoPreblend = true;
this.fillOpacity(imgData.data, drawWidth, drawHeight, actualHeight, comps);
}
if (this.needsDecode) {
this.decodeBuffer(comps);
}
this.colorSpace.fillRgb(imgData.data, originalWidth, originalHeight, drawWidth, drawHeight, actualHeight, bpc, comps, alpha01);
if (maybeUndoPreblend) {
this.undoPreblend(imgData.data, drawWidth, actualHeight);
}
return imgData;
}
fillGrayBuffer(buffer) {
const numComps = this.numComps;
if (numComps !== 1) {
throw new _util.FormatError(`Reading gray scale from a color image: ${numComps}`);
}
const width = this.width;
const height = this.height;
const bpc = this.bpc;
const rowBytes = width * numComps * bpc + 7 >> 3;
const imgArray = this.getImageBytes(height * rowBytes, {
internal: true
});
const comps = this.getComponents(imgArray);
let i, length;
if (bpc === 1) {
length = width * height;
if (this.needsDecode) {
for (i = 0; i < length; ++i) {
buffer[i] = comps[i] - 1 & 255;
}
} else {
for (i = 0; i < length; ++i) {
buffer[i] = -comps[i] & 255;
}
}
return;
}
if (this.needsDecode) {
this.decodeBuffer(comps);
}
length = width * height;
const scale = 255 / ((1 << bpc) - 1);
for (i = 0; i < length; ++i) {
buffer[i] = scale * comps[i];
}
}
getImageBytes(length, {
drawWidth,
drawHeight,
forceRGB = false,
internal = false
}) {
this.image.reset();
this.image.drawWidth = drawWidth || this.width;
this.image.drawHeight = drawHeight || this.height;
this.image.forceRGB = !!forceRGB;
const imageBytes = this.image.getBytes(length);
if (internal || this.image instanceof _decode_stream.DecodeStream) {
return imageBytes;
}
(0, _util.assert)(imageBytes instanceof Uint8Array, 'PDFImage.getImageBytes: Unsupported "imageBytes" type.');
return new Uint8Array(imageBytes);
}
}
exports.PDFImage = PDFImage;