mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-22 16:18:08 +02:00
Attempt to handle DNL (Define Number of Lines) markers when parsing JPEG images (issue 8614)
Please refer to the specification, found at https://www.w3.org/Graphics/JPEG/itu-t81.pdf#page=49 Given how the JPEG decoder is currently implemented, we need to know the value of the scanLines parameter (among others) *before* parsing of the SOS (Start of Scan) data begins. Hence the best solution I could come up with here, is to re-parse the image in the *hopefully* rare case of JPEG images that include a DNL (Define Number of Lines) marker. Fixes 8614.
This commit is contained in:
parent
80441346a3
commit
bf4166e6c9
3 changed files with 62 additions and 8 deletions
|
@ -28,6 +28,19 @@ let JpegError = (function JpegErrorClosure() {
|
|||
return JpegError;
|
||||
})();
|
||||
|
||||
let DNLMarkerError = (function DNLMarkerErrorClosure() {
|
||||
function DNLMarkerError(message, scanLines) {
|
||||
this.message = message;
|
||||
this.scanLines = scanLines;
|
||||
}
|
||||
|
||||
DNLMarkerError.prototype = new Error();
|
||||
DNLMarkerError.prototype.name = 'DNLMarkerError';
|
||||
DNLMarkerError.constructor = DNLMarkerError;
|
||||
|
||||
return DNLMarkerError;
|
||||
})();
|
||||
|
||||
/**
|
||||
* This code was forked from https://github.com/notmasteryet/jpgjs.
|
||||
* The original version was created by GitHub user notmasteryet.
|
||||
|
@ -112,7 +125,8 @@ var JpegImage = (function JpegImageClosure() {
|
|||
}
|
||||
|
||||
function decodeScan(data, offset, frame, components, resetInterval,
|
||||
spectralStart, spectralEnd, successivePrev, successive) {
|
||||
spectralStart, spectralEnd, successivePrev, successive,
|
||||
parseDNLMarker = false) {
|
||||
var mcusPerLine = frame.mcusPerLine;
|
||||
var progressive = frame.progressive;
|
||||
|
||||
|
@ -127,6 +141,14 @@ var JpegImage = (function JpegImageClosure() {
|
|||
if (bitsData === 0xFF) {
|
||||
var nextByte = data[offset++];
|
||||
if (nextByte) {
|
||||
if (nextByte === 0xDC && parseDNLMarker) { // DNL == 0xFFDC
|
||||
offset += 2; // Skip data length.
|
||||
const scanLines = (data[offset++] << 8) | data[offset++];
|
||||
if (scanLines > 0 && scanLines !== frame.scanLines) {
|
||||
throw new DNLMarkerError(
|
||||
'Found DNL marker (0xFFDC) while parsing scan data', scanLines);
|
||||
}
|
||||
}
|
||||
throw new JpegError(
|
||||
`unexpected marker ${((bitsData << 8) | nextByte).toString(16)}`);
|
||||
}
|
||||
|
@ -635,7 +657,7 @@ var JpegImage = (function JpegImageClosure() {
|
|||
}
|
||||
|
||||
JpegImage.prototype = {
|
||||
parse: function parse(data) {
|
||||
parse(data, { dnlScanLines = null, } = {}) {
|
||||
|
||||
function readUint16() {
|
||||
var value = (data[offset] << 8) | data[offset + 1];
|
||||
|
@ -685,6 +707,7 @@ var JpegImage = (function JpegImageClosure() {
|
|||
var jfif = null;
|
||||
var adobe = null;
|
||||
var frame, resetInterval;
|
||||
let numSOSMarkers = 0;
|
||||
var quantizationTables = [];
|
||||
var huffmanTablesAC = [], huffmanTablesDC = [];
|
||||
var fileMarker = readUint16();
|
||||
|
@ -781,7 +804,8 @@ var JpegImage = (function JpegImageClosure() {
|
|||
frame.extended = (fileMarker === 0xFFC1);
|
||||
frame.progressive = (fileMarker === 0xFFC2);
|
||||
frame.precision = data[offset++];
|
||||
frame.scanLines = readUint16();
|
||||
const sofScanLines = readUint16();
|
||||
frame.scanLines = dnlScanLines || sofScanLines;
|
||||
frame.samplesPerLine = readUint16();
|
||||
frame.components = [];
|
||||
frame.componentIds = {};
|
||||
|
@ -839,6 +863,12 @@ var JpegImage = (function JpegImageClosure() {
|
|||
break;
|
||||
|
||||
case 0xFFDA: // SOS (Start of Scan)
|
||||
// A DNL marker (0xFFDC), if it exists, is only allowed at the end
|
||||
// of the first scan segment and may only occur once in an image.
|
||||
// Furthermore, to prevent an infinite loop, do *not* attempt to
|
||||
// parse DNL markers during re-parsing of the JPEG scan data.
|
||||
const parseDNLMarker = (++numSOSMarkers) === 1 && !dnlScanLines;
|
||||
|
||||
readUint16(); // scanLength
|
||||
var selectorsCount = data[offset++];
|
||||
var components = [], component;
|
||||
|
@ -853,11 +883,26 @@ var JpegImage = (function JpegImageClosure() {
|
|||
var spectralStart = data[offset++];
|
||||
var spectralEnd = data[offset++];
|
||||
var successiveApproximation = data[offset++];
|
||||
var processed = decodeScan(data, offset,
|
||||
frame, components, resetInterval,
|
||||
spectralStart, spectralEnd,
|
||||
successiveApproximation >> 4, successiveApproximation & 15);
|
||||
offset += processed;
|
||||
try {
|
||||
var processed = decodeScan(data, offset,
|
||||
frame, components, resetInterval,
|
||||
spectralStart, spectralEnd,
|
||||
successiveApproximation >> 4, successiveApproximation & 15,
|
||||
parseDNLMarker);
|
||||
offset += processed;
|
||||
} catch (ex) {
|
||||
if (ex instanceof DNLMarkerError) {
|
||||
warn('Attempting to re-parse JPEG image using "scanLines" ' +
|
||||
'parameter found in DNL marker (0xFFDC) segment.');
|
||||
return this.parse(data, { dnlScanLines: ex.scanLines, });
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFFDC: // DNL (Define Number of Lines)
|
||||
// Ignore the marker, since it's being handled in `decodeScan`.
|
||||
offset += 4;
|
||||
break;
|
||||
|
||||
case 0xFFFF: // Fill bytes
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue