Merge pull request #5162 from yurydelendik/pramodhkp-fixupgstate2
[SVG] Reduces amount of used memory during PNG creation.
This commit is contained in:
commit
99b08ed223
@ -1865,7 +1865,9 @@ var OperatorList = (function OperatorListClosure() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
flush: function(lastChunk) {
|
flush: function(lastChunk) {
|
||||||
|
if (this.intent !== 'oplist') {
|
||||||
new QueueOptimizer().optimize(this);
|
new QueueOptimizer().optimize(this);
|
||||||
|
}
|
||||||
var transfers = getTransfers(this);
|
var transfers = getTransfers(this);
|
||||||
this.messageHandler.send('RenderPageChunk', {
|
this.messageHandler.send('RenderPageChunk', {
|
||||||
operatorList: {
|
operatorList: {
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/* globals PDFJS, FONT_IDENTITY_MATRIX, IDENTITY_MATRIX,
|
/* globals PDFJS, FONT_IDENTITY_MATRIX, IDENTITY_MATRIX,
|
||||||
isNum, OPS, Promise, Util, warn */
|
isNum, OPS, Promise, Util, warn, ImageKind, PDFJS */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
@ -29,6 +29,201 @@ function createScratchSVG(width, height) {
|
|||||||
return svg;
|
return svg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var convertImgDataToPng = (function convertImgDataToPngClosure() {
|
||||||
|
var PNG_HEADER =
|
||||||
|
new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
||||||
|
|
||||||
|
var CHUNK_WRAPPER_SIZE = 12;
|
||||||
|
|
||||||
|
var crcTable = new Int32Array(256);
|
||||||
|
for (var i = 0; i < 256; i++) {
|
||||||
|
var c = i;
|
||||||
|
for (var h = 0; h < 8; h++) {
|
||||||
|
if (c & 1) {
|
||||||
|
c = 0xedB88320 ^ ((c >> 1) & 0x7fffffff);
|
||||||
|
} else {
|
||||||
|
c = (c >> 1) & 0x7fffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crcTable[i] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
function crc32(data, start, end) {
|
||||||
|
var crc = -1;
|
||||||
|
for (var i = start; i < end; i++) {
|
||||||
|
var a = (crc ^ data[i]) & 0xff;
|
||||||
|
var b = crcTable[a];
|
||||||
|
crc = (crc >>> 8) ^ b;
|
||||||
|
}
|
||||||
|
return crc ^ -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writePngChunk(type, body, data, offset) {
|
||||||
|
var p = offset;
|
||||||
|
|
||||||
|
var len = body.length;
|
||||||
|
|
||||||
|
data[p] = len >> 24 & 0xff;
|
||||||
|
data[p + 1] = len >> 16 & 0xff;
|
||||||
|
data[p + 2] = len >> 8 & 0xff;
|
||||||
|
data[p + 3] = len & 0xff;
|
||||||
|
p += 4;
|
||||||
|
|
||||||
|
data[p] = type.charCodeAt(0) & 0xff;
|
||||||
|
data[p + 1] = type.charCodeAt(1) & 0xff;
|
||||||
|
data[p + 2] = type.charCodeAt(2) & 0xff;
|
||||||
|
data[p + 3] = type.charCodeAt(3) & 0xff;
|
||||||
|
p += 4;
|
||||||
|
|
||||||
|
data.set(body, p);
|
||||||
|
p += body.length;
|
||||||
|
|
||||||
|
var crc = crc32(data, offset + 4, p);
|
||||||
|
|
||||||
|
data[p] = crc >> 24 & 0xff;
|
||||||
|
data[p + 1] = crc >> 16 & 0xff;
|
||||||
|
data[p + 2] = crc >> 8 & 0xff;
|
||||||
|
data[p + 3] = crc & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
function adler32(data, start, end) {
|
||||||
|
var a = 1;
|
||||||
|
var b = 0;
|
||||||
|
for (var i = start; i < end; ++i) {
|
||||||
|
a = (a + (data[i] & 0xff)) % 65521;
|
||||||
|
b = (b + a) % 65521;
|
||||||
|
}
|
||||||
|
return (b << 16) | a;
|
||||||
|
}
|
||||||
|
|
||||||
|
function encode(imgData, kind) {
|
||||||
|
var width = imgData.width;
|
||||||
|
var height = imgData.height;
|
||||||
|
var bitDepth;
|
||||||
|
var colorType;
|
||||||
|
|
||||||
|
var bytes = imgData.data;
|
||||||
|
var lineSize;
|
||||||
|
switch (kind) {
|
||||||
|
case ImageKind.GRAYSCALE_1BPP:
|
||||||
|
colorType = 0;
|
||||||
|
bitDepth = 1;
|
||||||
|
lineSize = (width + 7) >> 3;
|
||||||
|
break;
|
||||||
|
case ImageKind.RGB_24BPP:
|
||||||
|
colorType = 2;
|
||||||
|
bitDepth = 8;
|
||||||
|
lineSize = width * 3;
|
||||||
|
break;
|
||||||
|
case ImageKind.RGBA_32BPP:
|
||||||
|
colorType = 6;
|
||||||
|
bitDepth = 8;
|
||||||
|
lineSize = width * 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('invalid format');
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefix every row with predictor 0
|
||||||
|
var literals = new Uint8Array((1 + lineSize) * height);
|
||||||
|
var offsetLiterals = 0, offsetBytes = 0;
|
||||||
|
var y, i;
|
||||||
|
for (y = 0; y < height; ++y) {
|
||||||
|
literals[offsetLiterals++] = 0; // no prediction
|
||||||
|
literals.set(bytes.subarray(offsetBytes, offsetBytes + lineSize),
|
||||||
|
offsetLiterals);
|
||||||
|
offsetBytes += lineSize;
|
||||||
|
offsetLiterals += lineSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kind === ImageKind.GRAYSCALE_1BPP) {
|
||||||
|
// inverting for B/W
|
||||||
|
offsetLiterals = 0;
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
offsetLiterals++; // skipping predictor
|
||||||
|
for (i = 0; i < lineSize; i++) {
|
||||||
|
literals[offsetLiterals++] ^= 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ihdr = new Uint8Array([
|
||||||
|
width >> 24 & 0xff,
|
||||||
|
width >> 16 & 0xff,
|
||||||
|
width >> 8 & 0xff,
|
||||||
|
width & 0xff,
|
||||||
|
height >> 24 & 0xff,
|
||||||
|
height >> 16 & 0xff,
|
||||||
|
height >> 8 & 0xff,
|
||||||
|
height & 0xff,
|
||||||
|
bitDepth, // bit depth
|
||||||
|
colorType, // color type
|
||||||
|
0x00, // compression method
|
||||||
|
0x00, // filter method
|
||||||
|
0x00 // interlace method
|
||||||
|
]);
|
||||||
|
|
||||||
|
var len = literals.length;
|
||||||
|
var maxBlockLength = 0xFFFF;
|
||||||
|
|
||||||
|
var deflateBlocks = Math.ceil(len / maxBlockLength);
|
||||||
|
var idat = new Uint8Array(2 + len + deflateBlocks * 5 + 4);
|
||||||
|
var pi = 0;
|
||||||
|
idat[pi++] = 0x78; // compression method and flags
|
||||||
|
idat[pi++] = 0x9c; // flags
|
||||||
|
|
||||||
|
var pos = 0;
|
||||||
|
while (len > maxBlockLength) {
|
||||||
|
// writing non-final DEFLATE blocks type 0 and length of 65535
|
||||||
|
idat[pi++] = 0x00;
|
||||||
|
idat[pi++] = 0xff;
|
||||||
|
idat[pi++] = 0xff;
|
||||||
|
idat[pi++] = 0x00;
|
||||||
|
idat[pi++] = 0x00;
|
||||||
|
idat.set(literals.subarray(pos, pos + maxBlockLength), pi);
|
||||||
|
pi += maxBlockLength;
|
||||||
|
pos += maxBlockLength;
|
||||||
|
len -= maxBlockLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// writing non-final DEFLATE blocks type 0
|
||||||
|
idat[pi++] = 0x01;
|
||||||
|
idat[pi++] = len & 0xff;
|
||||||
|
idat[pi++] = len >> 8 & 0xff;
|
||||||
|
idat[pi++] = (~len & 0xffff) & 0xff;
|
||||||
|
idat[pi++] = (~len & 0xffff) >> 8 & 0xff;
|
||||||
|
idat.set(literals.subarray(pos), pi);
|
||||||
|
pi += literals.length - pos;
|
||||||
|
|
||||||
|
var adler = adler32(literals, 0, literals.length); // checksum
|
||||||
|
idat[pi++] = adler >> 24 & 0xff;
|
||||||
|
idat[pi++] = adler >> 16 & 0xff;
|
||||||
|
idat[pi++] = adler >> 8 & 0xff;
|
||||||
|
idat[pi++] = adler & 0xff;
|
||||||
|
|
||||||
|
// PNG will consists: header, IHDR+data, IDAT+data, and IEND.
|
||||||
|
var pngLength = PNG_HEADER.length + CHUNK_WRAPPER_SIZE * 3 +
|
||||||
|
ihdr.length + idat.length;
|
||||||
|
var data = new Uint8Array(pngLength);
|
||||||
|
var offset = 0;
|
||||||
|
data.set(PNG_HEADER, offset);
|
||||||
|
offset += PNG_HEADER.length;
|
||||||
|
writePngChunk('IHDR', ihdr, data, offset);
|
||||||
|
offset += CHUNK_WRAPPER_SIZE + ihdr.length;
|
||||||
|
writePngChunk('IDATA', idat, data, offset);
|
||||||
|
offset += CHUNK_WRAPPER_SIZE + idat.length;
|
||||||
|
writePngChunk('IEND', new Uint8Array(0), data, offset);
|
||||||
|
|
||||||
|
return PDFJS.createObjectURL(data, 'image/png');
|
||||||
|
}
|
||||||
|
|
||||||
|
return function convertImgDataToPng(imgData) {
|
||||||
|
var kind = (imgData.kind === undefined ?
|
||||||
|
ImageKind.GRAYSCALE_1BPP : imgData.kind);
|
||||||
|
return encode(imgData, kind);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
var SVGExtraState = (function SVGExtraStateClosure() {
|
var SVGExtraState = (function SVGExtraStateClosure() {
|
||||||
function SVGExtraState(old) {
|
function SVGExtraState(old) {
|
||||||
// Are soft masks and alpha values shapes or opacities?
|
// Are soft masks and alpha values shapes or opacities?
|
||||||
@ -70,6 +265,8 @@ var SVGExtraState = (function SVGExtraStateClosure() {
|
|||||||
// Clipping
|
// Clipping
|
||||||
this.clipId = '';
|
this.clipId = '';
|
||||||
this.pendingClip = false;
|
this.pendingClip = false;
|
||||||
|
|
||||||
|
this.maskId = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
SVGExtraState.prototype = {
|
SVGExtraState.prototype = {
|
||||||
@ -109,7 +306,6 @@ function opListToTree(opList) {
|
|||||||
return opTree;
|
return opTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var SVGGraphics = (function SVGGraphicsClosure(ctx) {
|
var SVGGraphics = (function SVGGraphicsClosure(ctx) {
|
||||||
function SVGGraphics(commonObjs, objs) {
|
function SVGGraphics(commonObjs, objs) {
|
||||||
|
|
||||||
@ -130,6 +326,7 @@ var SVGGraphics = (function SVGGraphicsClosure(ctx) {
|
|||||||
var NORMAL_CLIP = {};
|
var NORMAL_CLIP = {};
|
||||||
var EO_CLIP = {};
|
var EO_CLIP = {};
|
||||||
var clipCount = 0;
|
var clipCount = 0;
|
||||||
|
var maskCount = 0;
|
||||||
|
|
||||||
SVGGraphics.prototype = {
|
SVGGraphics.prototype = {
|
||||||
save: function SVGGraphics_save() {
|
save: function SVGGraphics_save() {
|
||||||
@ -211,7 +408,8 @@ var SVGGraphics = (function SVGGraphicsClosure(ctx) {
|
|||||||
this.pgrp.appendChild(this.tgrp);
|
this.pgrp.appendChild(this.tgrp);
|
||||||
this.svg.appendChild(this.pgrp);
|
this.svg.appendChild(this.pgrp);
|
||||||
this.container.appendChild(this.svg);
|
this.container.appendChild(this.svg);
|
||||||
this.convertOpList(operatorList);
|
var opTree = this.convertOpList(operatorList);
|
||||||
|
this.executeOpTree(opTree);
|
||||||
},
|
},
|
||||||
|
|
||||||
convertOpList: function SVGGraphics_convertOpList(operatorList) {
|
convertOpList: function SVGGraphics_convertOpList(operatorList) {
|
||||||
@ -233,8 +431,6 @@ var SVGGraphics = (function SVGGraphicsClosure(ctx) {
|
|||||||
opList.push({'fnId' : fnId, 'fn': REVOPS[fnId], 'args': argsArray[x]});
|
opList.push({'fnId' : fnId, 'fn': REVOPS[fnId], 'args': argsArray[x]});
|
||||||
}
|
}
|
||||||
opTree = opListToTree(opList);
|
opTree = opListToTree(opList);
|
||||||
|
|
||||||
this.executeOpTree(opTree);
|
|
||||||
return opTree;
|
return opTree;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -331,6 +527,15 @@ var SVGGraphics = (function SVGGraphicsClosure(ctx) {
|
|||||||
case OPS.paintJpegXObject:
|
case OPS.paintJpegXObject:
|
||||||
this.paintJpegXObject(args[0], args[1], args[2]);
|
this.paintJpegXObject(args[0], args[1], args[2]);
|
||||||
break;
|
break;
|
||||||
|
case OPS.paintImageXObject:
|
||||||
|
this.paintImageXObject(args[0]);
|
||||||
|
break;
|
||||||
|
case OPS.paintInlineImageXObject:
|
||||||
|
this.paintInlineImageXObject(args[0]);
|
||||||
|
break;
|
||||||
|
case OPS.paintImageMaskXObject:
|
||||||
|
this.paintImageMaskXObject(args[0]);
|
||||||
|
break;
|
||||||
case OPS.closePath:
|
case OPS.closePath:
|
||||||
this.closePath();
|
this.closePath();
|
||||||
break;
|
break;
|
||||||
@ -635,6 +840,7 @@ var SVGGraphics = (function SVGGraphicsClosure(ctx) {
|
|||||||
endPath: function SVGGraphics_endPath() {
|
endPath: function SVGGraphics_endPath() {
|
||||||
var current = this.current;
|
var current = this.current;
|
||||||
if (current.pendingClip) {
|
if (current.pendingClip) {
|
||||||
|
this.cgrp.appendChild(this.tgrp);
|
||||||
this.pgrp.appendChild(this.cgrp);
|
this.pgrp.appendChild(this.cgrp);
|
||||||
} else {
|
} else {
|
||||||
this.pgrp.appendChild(this.tgrp);
|
this.pgrp.appendChild(this.tgrp);
|
||||||
@ -657,6 +863,8 @@ var SVGGraphics = (function SVGGraphicsClosure(ctx) {
|
|||||||
} else {
|
} else {
|
||||||
clipElement.setAttributeNS(null, 'clip-rule', 'nonzero');
|
clipElement.setAttributeNS(null, 'clip-rule', 'nonzero');
|
||||||
}
|
}
|
||||||
|
this.clippath.setAttributeNS(null, 'transform',
|
||||||
|
'matrix(' + this.transformMatrix + ')');
|
||||||
this.clippath.appendChild(clipElement);
|
this.clippath.appendChild(clipElement);
|
||||||
this.defs.appendChild(this.clippath);
|
this.defs.appendChild(this.clippath);
|
||||||
|
|
||||||
@ -779,8 +987,7 @@ var SVGGraphics = (function SVGGraphicsClosure(ctx) {
|
|||||||
this.tgrp.appendChild(rect);
|
this.tgrp.appendChild(rect);
|
||||||
},
|
},
|
||||||
|
|
||||||
paintJpegXObject:
|
paintJpegXObject: function SVGGraphics_paintJpegXObject(objId, w, h) {
|
||||||
function SVGGraphics_paintJpegXObject(objId, w, h) {
|
|
||||||
var current = this.current;
|
var current = this.current;
|
||||||
var imgObj = this.objs.get(objId);
|
var imgObj = this.objs.get(objId);
|
||||||
var imgEl = document.createElementNS(NS, 'svg:image');
|
var imgEl = document.createElementNS(NS, 'svg:image');
|
||||||
@ -800,6 +1007,78 @@ var SVGGraphics = (function SVGGraphicsClosure(ctx) {
|
|||||||
this.pgrp.appendChild(this.tgrp);
|
this.pgrp.appendChild(this.tgrp);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
paintImageXObject: function SVGGraphics_paintImageXObject(objId) {
|
||||||
|
var imgData = this.objs.get(objId);
|
||||||
|
if (!imgData) {
|
||||||
|
warn('Dependent image isn\'t ready yet');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.paintInlineImageXObject(imgData);
|
||||||
|
},
|
||||||
|
|
||||||
|
paintInlineImageXObject:
|
||||||
|
function SVGGraphics_paintInlineImageXObject(imgData, mask) {
|
||||||
|
var current = this.current;
|
||||||
|
var width = imgData.width;
|
||||||
|
var height = imgData.height;
|
||||||
|
|
||||||
|
var imgSrc = convertImgDataToPng(imgData);
|
||||||
|
var cliprect = document.createElementNS(NS, 'svg:rect');
|
||||||
|
cliprect.setAttributeNS(null, 'x', 0);
|
||||||
|
cliprect.setAttributeNS(null, 'y', 0);
|
||||||
|
cliprect.setAttributeNS(null, 'width', width);
|
||||||
|
cliprect.setAttributeNS(null, 'height', height);
|
||||||
|
current.element = cliprect;
|
||||||
|
this.clip('nonzero');
|
||||||
|
var imgEl = document.createElementNS(NS, 'svg:image');
|
||||||
|
imgEl.setAttributeNS(XLINK_NS, 'href', imgSrc);
|
||||||
|
imgEl.setAttributeNS(null, 'x', 0);
|
||||||
|
imgEl.setAttributeNS(null, 'y', -height);
|
||||||
|
imgEl.setAttributeNS(null, 'width', width + 'px');
|
||||||
|
imgEl.setAttributeNS(null, 'height', height + 'px');
|
||||||
|
imgEl.setAttributeNS(null, 'transform', 'scale(' + (1 / width) +
|
||||||
|
', ' + (-1 / height) + ')');
|
||||||
|
if (mask) {
|
||||||
|
mask.appendChild(imgEl);
|
||||||
|
} else {
|
||||||
|
this.tgrp.appendChild(imgEl);
|
||||||
|
}
|
||||||
|
if (current.pendingClip) {
|
||||||
|
this.cgrp.appendChild(this.tgrp);
|
||||||
|
this.pgrp.appendChild(this.cgrp);
|
||||||
|
} else {
|
||||||
|
this.pgrp.appendChild(this.tgrp);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
paintImageMaskXObject:
|
||||||
|
function SVGGraphics_paintImageMaskXObject(imgData) {
|
||||||
|
var current = this.current;
|
||||||
|
|
||||||
|
var width = imgData.width;
|
||||||
|
var height = imgData.height;
|
||||||
|
|
||||||
|
var img = convertImgDataToPng(imgData);
|
||||||
|
var fillColor = current.fillColor;
|
||||||
|
|
||||||
|
current.maskId = 'mask' + maskCount++;
|
||||||
|
var mask = document.createElementNS(NS, 'svg:mask');
|
||||||
|
mask.setAttributeNS(null, 'id', current.maskId);
|
||||||
|
|
||||||
|
var rect = document.createElementNS(NS, 'svg:rect');
|
||||||
|
rect.setAttributeNS(null, 'x', 0);
|
||||||
|
rect.setAttributeNS(null, 'y', 0);
|
||||||
|
rect.setAttributeNS(null, 'width', width);
|
||||||
|
rect.setAttributeNS(null, 'height', height);
|
||||||
|
rect.setAttributeNS(null, 'fill', fillColor);
|
||||||
|
rect.setAttributeNS(null, 'mask', 'url(#' + current.maskId +')');
|
||||||
|
this.defs.appendChild(mask);
|
||||||
|
this.tgrp.appendChild(rect);
|
||||||
|
|
||||||
|
this.paintInlineImageXObject(imgData, mask);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
return SVGGraphics;
|
return SVGGraphics;
|
||||||
})();
|
})();
|
||||||
|
Loading…
Reference in New Issue
Block a user