Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 42 additions & 11 deletions src/core/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -694,16 +694,8 @@ class PDFImage {
}

async createImageData(forceRGBA = false, isOffscreenCanvasSupported = false) {
const drawWidth = this.drawWidth;
const drawHeight = this.drawHeight;
const imgData = {
width: drawWidth,
height: drawHeight,
interpolate: this.interpolate,
kind: 0,
data: null,
// Other fields are filled in below.
};
let drawWidth = this.drawWidth;
let drawHeight = this.drawHeight;

const numComps = this.numComps;
const originalWidth = this.width;
Expand All @@ -712,10 +704,49 @@ class PDFImage {

// Rows start at byte boundary.
const rowBytes = (originalWidth * numComps * bpc + 7) >> 3;
const mustBeResized =
let mustBeResized =
isOffscreenCanvasSupported &&
ImageResizer.needsToBeResized(drawWidth, drawHeight);

// When a mask is much larger than the image it applies to (as happens with
// high-resolution JBIG2 masks over a low-resolution image), `drawWidth` /
// `drawHeight` can be enormous (e.g. 20595x28611 ⇒ 2.35 GB RGBA buffer).
// Downscale early so the RGBA buffer fits in memory; `fillOpacity` and
// `colorSpace.fillRgb` resample from the original data into the smaller
// buffer. Only apply this when a mask/smask is inflating the output size
// beyond the source image, since other paths assume the data length
// matches `drawWidth * drawHeight`.
if (
mustBeResized &&
(drawWidth > originalWidth || drawHeight > originalHeight)
) {
const { MAX_AREA, MAX_DIM } = ImageResizer;
const area = drawWidth * drawHeight;
let scale = 1;
if (MAX_AREA > 0 && area > MAX_AREA) {
scale = Math.sqrt(area / MAX_AREA);
}
scale = Math.max(scale, drawWidth / MAX_DIM, drawHeight / MAX_DIM);
if (scale > 1) {
drawWidth = Math.max(1, Math.floor(drawWidth / scale));
drawHeight = Math.max(1, Math.floor(drawHeight / scale));
// The capped dims fit in canvas limits, so let the fast OffscreenCanvas
// path build the final bitmap directly — skipping an unnecessary
// Uint8ClampedArray allocation, BMP-encode and ImageBitmap round-trip
// through `ImageResizer`.
mustBeResized = ImageResizer.needsToBeResized(drawWidth, drawHeight);
}
}

const imgData = {
width: drawWidth,
height: drawHeight,
interpolate: this.interpolate,
kind: 0,
data: null,
// Other fields are filled in below.
};

if (!this.smask && !this.mask && this.colorSpace.name === "DeviceRGBA") {
imgData.kind = ImageKind.RGBA_32BPP;
const imgArray = (imgData.data = await this.getImageBytes(
Expand Down
1 change: 1 addition & 0 deletions test/pdfs/issue7802.pdf.link
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://github.com/mozilla/pdf.js/files/587268/Wheeling.VA.Daily.Intelligencer.1880.12.11889.pdf
8 changes: 8 additions & 0 deletions test/test_manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -14151,5 +14151,13 @@
"md5": "29c23022ab3d983733e1ee37fd0bc785",
"rounds": 1,
"type": "eq"
},
{
"id": "issue7802",
"file": "pdfs/issue7802.pdf",
"md5": "521cf7c408de56b08c58d9e6bdd92791",
"link": true,
"rounds": 1,
"type": "eq"
}
]
Loading