|
|
|
/* Copyright 2017 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.
|
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
|
|
value: true
|
|
|
|
});
|
|
|
|
exports.Jbig2Image = undefined;
|
|
|
|
|
|
|
|
var _util = require('../shared/util');
|
|
|
|
|
|
|
|
var _arithmetic_decoder = require('./arithmetic_decoder');
|
|
|
|
|
|
|
|
var Jbig2Error = function Jbig2ErrorClosure() {
|
|
|
|
function Jbig2Error(msg) {
|
|
|
|
this.message = 'JBIG2 error: ' + msg;
|
|
|
|
}
|
|
|
|
Jbig2Error.prototype = new Error();
|
|
|
|
Jbig2Error.prototype.name = 'Jbig2Error';
|
|
|
|
Jbig2Error.constructor = Jbig2Error;
|
|
|
|
return Jbig2Error;
|
|
|
|
}();
|
|
|
|
var Jbig2Image = function Jbig2ImageClosure() {
|
|
|
|
function ContextCache() {}
|
|
|
|
ContextCache.prototype = {
|
|
|
|
getContexts: function getContexts(id) {
|
|
|
|
if (id in this) {
|
|
|
|
return this[id];
|
|
|
|
}
|
|
|
|
return this[id] = new Int8Array(1 << 16);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
function DecodingContext(data, start, end) {
|
|
|
|
this.data = data;
|
|
|
|
this.start = start;
|
|
|
|
this.end = end;
|
|
|
|
}
|
|
|
|
DecodingContext.prototype = {
|
|
|
|
get decoder() {
|
|
|
|
var decoder = new _arithmetic_decoder.ArithmeticDecoder(this.data, this.start, this.end);
|
|
|
|
return (0, _util.shadow)(this, 'decoder', decoder);
|
|
|
|
},
|
|
|
|
get contextCache() {
|
|
|
|
var cache = new ContextCache();
|
|
|
|
return (0, _util.shadow)(this, 'contextCache', cache);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
function decodeInteger(contextCache, procedure, decoder) {
|
|
|
|
var contexts = contextCache.getContexts(procedure);
|
|
|
|
var prev = 1;
|
|
|
|
function readBits(length) {
|
|
|
|
var v = 0;
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
var bit = decoder.readBit(contexts, prev);
|
|
|
|
prev = prev < 256 ? prev << 1 | bit : (prev << 1 | bit) & 511 | 256;
|
|
|
|
v = v << 1 | bit;
|
|
|
|
}
|
|
|
|
return v >>> 0;
|
|
|
|
}
|
|
|
|
var sign = readBits(1);
|
|
|
|
var value = readBits(1) ? readBits(1) ? readBits(1) ? readBits(1) ? readBits(1) ? readBits(32) + 4436 : readBits(12) + 340 : readBits(8) + 84 : readBits(6) + 20 : readBits(4) + 4 : readBits(2);
|
|
|
|
return sign === 0 ? value : value > 0 ? -value : null;
|
|
|
|
}
|
|
|
|
function decodeIAID(contextCache, decoder, codeLength) {
|
|
|
|
var contexts = contextCache.getContexts('IAID');
|
|
|
|
var prev = 1;
|
|
|
|
for (var i = 0; i < codeLength; i++) {
|
|
|
|
var bit = decoder.readBit(contexts, prev);
|
|
|
|
prev = prev << 1 | bit;
|
|
|
|
}
|
|
|
|
if (codeLength < 31) {
|
|
|
|
return prev & (1 << codeLength) - 1;
|
|
|
|
}
|
|
|
|
return prev & 0x7FFFFFFF;
|
|
|
|
}
|
|
|
|
var SegmentTypes = ['SymbolDictionary', null, null, null, 'IntermediateTextRegion', null, 'ImmediateTextRegion', 'ImmediateLosslessTextRegion', null, null, null, null, null, null, null, null, 'patternDictionary', null, null, null, 'IntermediateHalftoneRegion', null, 'ImmediateHalftoneRegion', 'ImmediateLosslessHalftoneRegion', null, null, null, null, null, null, null, null, null, null, null, null, 'IntermediateGenericRegion', null, 'ImmediateGenericRegion', 'ImmediateLosslessGenericRegion', 'IntermediateGenericRefinementRegion', null, 'ImmediateGenericRefinementRegion', 'ImmediateLosslessGenericRefinementRegion', null, null, null, null, 'PageInformation', 'EndOfPage', 'EndOfStripe', 'EndOfFile', 'Profiles', 'Tables', null, null, null, null, null, null, null, null, 'Extension'];
|
|
|
|
var CodingTemplates = [[{
|
|
|
|
x: -1,
|
|
|
|
y: -2
|
|
|
|
}, {
|
|
|
|
x: 0,
|
|
|
|
y: -2
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: -2
|
|
|
|
}, {
|
|
|
|
x: -2,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 0,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 2,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -4,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: -3,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: -2,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: 0
|
|
|
|
}], [{
|
|
|
|
x: -1,
|
|
|
|
y: -2
|
|
|
|
}, {
|
|
|
|
x: 0,
|
|
|
|
y: -2
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: -2
|
|
|
|
}, {
|
|
|
|
x: 2,
|
|
|
|
y: -2
|
|
|
|
}, {
|
|
|
|
x: -2,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 0,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 2,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -3,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: -2,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: 0
|
|
|
|
}], [{
|
|
|
|
x: -1,
|
|
|
|
y: -2
|
|
|
|
}, {
|
|
|
|
x: 0,
|
|
|
|
y: -2
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: -2
|
|
|
|
}, {
|
|
|
|
x: -2,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 0,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -2,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: 0
|
|
|
|
}], [{
|
|
|
|
x: -3,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -2,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 0,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -4,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: -3,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: -2,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: 0
|
|
|
|
}]];
|
|
|
|
var RefinementTemplates = [{
|
|
|
|
coding: [{
|
|
|
|
x: 0,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: 0
|
|
|
|
}],
|
|
|
|
reference: [{
|
|
|
|
x: 0,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: 0,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: 1
|
|
|
|
}, {
|
|
|
|
x: 0,
|
|
|
|
y: 1
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: 1
|
|
|
|
}]
|
|
|
|
}, {
|
|
|
|
coding: [{
|
|
|
|
x: -1,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 0,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: 0
|
|
|
|
}],
|
|
|
|
reference: [{
|
|
|
|
x: 0,
|
|
|
|
y: -1
|
|
|
|
}, {
|
|
|
|
x: -1,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: 0,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: 0
|
|
|
|
}, {
|
|
|
|
x: 0,
|
|
|
|
y: 1
|
|
|
|
}, {
|
|
|
|
x: 1,
|
|
|
|
y: 1
|
|
|
|
}]
|
|
|
|
}];
|
|
|
|
var ReusedContexts = [0x9B25, 0x0795, 0x00E5, 0x0195];
|
|
|
|
var RefinementReusedContexts = [0x0020, 0x0008];
|
|
|
|
function decodeBitmapTemplate0(width, height, decodingContext) {
|
|
|
|
var decoder = decodingContext.decoder;
|
|
|
|
var contexts = decodingContext.contextCache.getContexts('GB');
|
|
|
|
var contextLabel,
|
|
|
|
i,
|
|
|
|
j,
|
|
|
|
pixel,
|
|
|
|
row,
|
|
|
|
row1,
|
|
|
|
row2,
|
|
|
|
bitmap = [];
|
|
|
|
var OLD_PIXEL_MASK = 0x7BF7;
|
|
|
|
for (i = 0; i < height; i++) {
|
|
|
|
row = bitmap[i] = new Uint8Array(width);
|
|
|
|
row1 = i < 1 ? row : bitmap[i - 1];
|
|
|
|
row2 = i < 2 ? row : bitmap[i - 2];
|
|
|
|
contextLabel = row2[0] << 13 | row2[1] << 12 | row2[2] << 11 | row1[0] << 7 | row1[1] << 6 | row1[2] << 5 | row1[3] << 4;
|
|
|
|
for (j = 0; j < width; j++) {
|
|
|
|
row[j] = pixel = decoder.readBit(contexts, contextLabel);
|
|
|
|
contextLabel = (contextLabel & OLD_PIXEL_MASK) << 1 | (j + 3 < width ? row2[j + 3] << 11 : 0) | (j + 4 < width ? row1[j + 4] << 4 : 0) | pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bitmap;
|
|
|
|
}
|
|
|
|
function decodeBitmap(mmr, width, height, templateIndex, prediction, skip, at, decodingContext) {
|
|
|
|
if (mmr) {
|
|
|
|
throw new Jbig2Error('MMR encoding is not supported');
|
|
|
|
}
|
|
|
|
if (templateIndex === 0 && !skip && !prediction && at.length === 4 && at[0].x === 3 && at[0].y === -1 && at[1].x === -3 && at[1].y === -1 && at[2].x === 2 && at[2].y === -2 && at[3].x === -2 && at[3].y === -2) {
|
|
|
|
return decodeBitmapTemplate0(width, height, decodingContext);
|
|
|
|
}
|
|
|
|
var useskip = !!skip;
|
|
|
|
var template = CodingTemplates[templateIndex].concat(at);
|
|
|
|
template.sort(function (a, b) {
|
|
|
|
return a.y - b.y || a.x - b.x;
|
|
|
|
});
|
|
|
|
var templateLength = template.length;
|
|
|
|
var templateX = new Int8Array(templateLength);
|
|
|
|
var templateY = new Int8Array(templateLength);
|
|
|
|
var changingTemplateEntries = [];
|
|
|
|
var reuseMask = 0,
|
|
|
|
minX = 0,
|
|
|
|
maxX = 0,
|
|
|
|
minY = 0;
|
|
|
|
var c, k;
|
|
|
|
for (k = 0; k < templateLength; k++) {
|
|
|
|
templateX[k] = template[k].x;
|
|
|
|
templateY[k] = template[k].y;
|
|
|
|
minX = Math.min(minX, template[k].x);
|
|
|
|
maxX = Math.max(maxX, template[k].x);
|
|
|
|
minY = Math.min(minY, template[k].y);
|
|
|
|
if (k < templateLength - 1 && template[k].y === template[k + 1].y && template[k].x === template[k + 1].x - 1) {
|
|
|
|
reuseMask |= 1 << templateLength - 1 - k;
|
|
|
|
} else {
|
|
|
|
changingTemplateEntries.push(k);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var changingEntriesLength = changingTemplateEntries.length;
|
|
|
|
var changingTemplateX = new Int8Array(changingEntriesLength);
|
|
|
|
var changingTemplateY = new Int8Array(changingEntriesLength);
|
|
|
|
var changingTemplateBit = new Uint16Array(changingEntriesLength);
|
|
|
|
for (c = 0; c < changingEntriesLength; c++) {
|
|
|
|
k = changingTemplateEntries[c];
|
|
|
|
changingTemplateX[c] = template[k].x;
|
|
|
|
changingTemplateY[c] = template[k].y;
|
|
|
|
changingTemplateBit[c] = 1 << templateLength - 1 - k;
|
|
|
|
}
|
|
|
|
var sbb_left = -minX;
|
|
|
|
var sbb_top = -minY;
|
|
|
|
var sbb_right = width - maxX;
|
|
|
|
var pseudoPixelContext = ReusedContexts[templateIndex];
|
|
|
|
var row = new Uint8Array(width);
|
|
|
|
var bitmap = [];
|
|
|
|
var decoder = decodingContext.decoder;
|
|
|
|
var contexts = decodingContext.contextCache.getContexts('GB');
|
|
|
|
var ltp = 0,
|
|
|
|
j,
|
|
|
|
i0,
|
|
|
|
j0,
|
|
|
|
contextLabel = 0,
|
|
|
|
bit,
|
|
|
|
shift;
|
|
|
|
for (var i = 0; i < height; i++) {
|
|
|
|
if (prediction) {
|
|
|
|
var sltp = decoder.readBit(contexts, pseudoPixelContext);
|
|
|
|
ltp ^= sltp;
|
|
|
|
if (ltp) {
|
|
|
|
bitmap.push(row);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
row = new Uint8Array(row);
|
|
|
|
bitmap.push(row);
|
|
|
|
for (j = 0; j < width; j++) {
|
|
|
|
if (useskip && skip[i][j]) {
|
|
|
|
row[j] = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (j >= sbb_left && j < sbb_right && i >= sbb_top) {
|
|
|
|
contextLabel = contextLabel << 1 & reuseMask;
|
|
|
|
for (k = 0; k < changingEntriesLength; k++) {
|
|
|
|
i0 = i + changingTemplateY[k];
|
|
|
|
j0 = j + changingTemplateX[k];
|
|
|
|
bit = bitmap[i0][j0];
|
|
|
|
if (bit) {
|
|
|
|
bit = changingTemplateBit[k];
|
|
|
|
contextLabel |= bit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
contextLabel = 0;
|
|
|
|
shift = templateLength - 1;
|
|
|
|
for (k = 0; k < templateLength; k++, shift--) {
|
|
|
|
j0 = j + templateX[k];
|
|
|
|
if (j0 >= 0 && j0 < width) {
|
|
|
|
i0 = i + templateY[k];
|
|
|
|
if (i0 >= 0) {
|
|
|
|
bit = bitmap[i0][j0];
|
|
|
|
if (bit) {
|
|
|
|
contextLabel |= bit << shift;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var pixel = decoder.readBit(contexts, contextLabel);
|
|
|
|
row[j] = pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bitmap;
|
|
|
|
}
|
|
|
|
function decodeRefinement(width, height, templateIndex, referenceBitmap, offsetX, offsetY, prediction, at, decodingContext) {
|
|
|
|
var codingTemplate = RefinementTemplates[templateIndex].coding;
|
|
|
|
if (templateIndex === 0) {
|
|
|
|
codingTemplate = codingTemplate.concat([at[0]]);
|
|
|
|
}
|
|
|
|
var codingTemplateLength = codingTemplate.length;
|
|
|
|
var codingTemplateX = new Int32Array(codingTemplateLength);
|
|
|
|
var codingTemplateY = new Int32Array(codingTemplateLength);
|
|
|
|
var k;
|
|
|
|
for (k = 0; k < codingTemplateLength; k++) {
|
|
|
|
codingTemplateX[k] = codingTemplate[k].x;
|
|
|
|
codingTemplateY[k] = codingTemplate[k].y;
|
|
|
|
}
|
|
|
|
var referenceTemplate = RefinementTemplates[templateIndex].reference;
|
|
|
|
if (templateIndex === 0) {
|
|
|
|
referenceTemplate = referenceTemplate.concat([at[1]]);
|
|
|
|
}
|
|
|
|
var referenceTemplateLength = referenceTemplate.length;
|
|
|
|
var referenceTemplateX = new Int32Array(referenceTemplateLength);
|
|
|
|
var referenceTemplateY = new Int32Array(referenceTemplateLength);
|
|
|
|
for (k = 0; k < referenceTemplateLength; k++) {
|
|
|
|
referenceTemplateX[k] = referenceTemplate[k].x;
|
|
|
|
referenceTemplateY[k] = referenceTemplate[k].y;
|
|
|
|
}
|
|
|
|
var referenceWidth = referenceBitmap[0].length;
|
|
|
|
var referenceHeight = referenceBitmap.length;
|
|
|
|
var pseudoPixelContext = RefinementReusedContexts[templateIndex];
|
|
|
|
var bitmap = [];
|
|
|
|
var decoder = decodingContext.decoder;
|
|
|
|
var contexts = decodingContext.contextCache.getContexts('GR');
|
|
|
|
var ltp = 0;
|
|
|
|
for (var i = 0; i < height; i++) {
|
|
|
|
if (prediction) {
|
|
|
|
var sltp = decoder.readBit(contexts, pseudoPixelContext);
|
|
|
|
ltp ^= sltp;
|
|
|
|
if (ltp) {
|
|
|
|
throw new Jbig2Error('prediction is not supported');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var row = new Uint8Array(width);
|
|
|
|
bitmap.push(row);
|
|
|
|
for (var j = 0; j < width; j++) {
|
|
|
|
var i0, j0;
|
|
|
|
var contextLabel = 0;
|
|
|
|
for (k = 0; k < codingTemplateLength; k++) {
|
|
|
|
i0 = i + codingTemplateY[k];
|
|
|
|
j0 = j + codingTemplateX[k];
|
|
|
|
if (i0 < 0 || j0 < 0 || j0 >= width) {
|
|
|
|
contextLabel <<= 1;
|
|
|
|
} else {
|
|
|
|
contextLabel = contextLabel << 1 | bitmap[i0][j0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (k = 0; k < referenceTemplateLength; k++) {
|
|
|
|
i0 = i + referenceTemplateY[k] - offsetY;
|
|
|
|
j0 = j + referenceTemplateX[k] - offsetX;
|
|
|
|
if (i0 < 0 || i0 >= referenceHeight || j0 < 0 || j0 >= referenceWidth) {
|
|
|
|
contextLabel <<= 1;
|
|
|
|
} else {
|
|
|
|
contextLabel = contextLabel << 1 | referenceBitmap[i0][j0];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var pixel = decoder.readBit(contexts, contextLabel);
|
|
|
|
row[j] = pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bitmap;
|
|
|
|
}
|
|
|
|
function decodeSymbolDictionary(huffman, refinement, symbols, numberOfNewSymbols, numberOfExportedSymbols, huffmanTables, templateIndex, at, refinementTemplateIndex, refinementAt, decodingContext) {
|
|
|
|
if (huffman) {
|
|
|
|
throw new Jbig2Error('huffman is not supported');
|
|
|
|
}
|
|
|
|
var newSymbols = [];
|
|
|
|
var currentHeight = 0;
|
|
|
|
var symbolCodeLength = (0, _util.log2)(symbols.length + numberOfNewSymbols);
|
|
|
|
var decoder = decodingContext.decoder;
|
|
|
|
var contextCache = decodingContext.contextCache;
|
|
|
|
while (newSymbols.length < numberOfNewSymbols) {
|
|
|
|
var deltaHeight = decodeInteger(contextCache, 'IADH', decoder);
|
|
|
|
currentHeight += deltaHeight;
|
|
|
|
var currentWidth = 0;
|
|
|
|
while (true) {
|
|
|
|
var deltaWidth = decodeInteger(contextCache, 'IADW', decoder);
|
|
|
|
if (deltaWidth === null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
currentWidth += deltaWidth;
|
|
|
|
var bitmap;
|
|
|
|
if (refinement) {
|
|
|
|
var numberOfInstances = decodeInteger(contextCache, 'IAAI', decoder);
|
|
|
|
if (numberOfInstances > 1) {
|
|
|
|
bitmap = decodeTextRegion(huffman, refinement, currentWidth, currentHeight, 0, numberOfInstances, 1, symbols.concat(newSymbols), symbolCodeLength, 0, 0, 1, 0, huffmanTables, refinementTemplateIndex, refinementAt, decodingContext);
|
|
|
|
} else {
|
|
|
|
var symbolId = decodeIAID(contextCache, decoder, symbolCodeLength);
|
|
|
|
var rdx = decodeInteger(contextCache, 'IARDX', decoder);
|
|
|
|
var rdy = decodeInteger(contextCache, 'IARDY', decoder);
|
|
|
|
var symbol = symbolId < symbols.length ? symbols[symbolId] : newSymbols[symbolId - symbols.length];
|
|
|
|
bitmap = decodeRefinement(currentWidth, currentHeight, refinementTemplateIndex, symbol, rdx, rdy, false, refinementAt, decodingContext);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bitmap = decodeBitmap(false, currentWidth, currentHeight, templateIndex, false, null, at, decodingContext);
|
|
|
|
}
|
|
|
|
newSymbols.push(bitmap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var exportedSymbols = [];
|
|
|
|
var flags = [],
|
|
|
|
currentFlag = false;
|
|
|
|
var totalSymbolsLength = symbols.length + numberOfNewSymbols;
|
|
|
|
while (flags.length < totalSymbolsLength) {
|
|
|
|
var runLength = decodeInteger(contextCache, 'IAEX', decoder);
|
|
|
|
while (runLength--) {
|
|
|
|
flags.push(currentFlag);
|
|
|
|
}
|
|
|
|
currentFlag = !currentFlag;
|
|
|
|
}
|
|
|
|
for (var i = 0, ii = symbols.length; i < ii; i++) {
|
|
|
|
if (flags[i]) {
|
|
|
|
exportedSymbols.push(symbols[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (var j = 0; j < numberOfNewSymbols; i++, j++) {
|
|
|
|
if (flags[i]) {
|
|
|
|
exportedSymbols.push(newSymbols[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return exportedSymbols;
|
|
|
|
}
|
|
|
|
function decodeTextRegion(huffman, refinement, width, height, defaultPixelValue, numberOfSymbolInstances, stripSize, inputSymbols, symbolCodeLength, transposed, dsOffset, referenceCorner, combinationOperator, huffmanTables, refinementTemplateIndex, refinementAt, decodingContext) {
|
|
|
|
if (huffman) {
|
|
|
|
throw new Jbig2Error('huffman is not supported');
|
|
|
|
}
|
|
|
|
var bitmap = [];
|
|
|
|
var i, row;
|
|
|
|
for (i = 0; i < height; i++) {
|
|
|
|
row = new Uint8Array(width);
|
|
|
|
if (defaultPixelValue) {
|
|
|
|
for (var j = 0; j < width; j++) {
|
|
|
|
row[j] = defaultPixelValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bitmap.push(row);
|
|
|
|
}
|
|
|
|
var decoder = decodingContext.decoder;
|
|
|
|
var contextCache = decodingContext.contextCache;
|
|
|
|
var stripT = -decodeInteger(contextCache, 'IADT', decoder);
|
|
|
|
var firstS = 0;
|
|
|
|
i = 0;
|
|
|
|
while (i < numberOfSymbolInstances) {
|
|
|
|
var deltaT = decodeInteger(contextCache, 'IADT', decoder);
|
|
|
|
stripT += deltaT;
|
|
|
|
var deltaFirstS = decodeInteger(contextCache, 'IAFS', decoder);
|
|
|
|
firstS += deltaFirstS;
|
|
|
|
var currentS = firstS;
|
|
|
|
do {
|
|
|
|
var currentT = stripSize === 1 ? 0 : decodeInteger(contextCache, 'IAIT', decoder);
|
|
|
|
var t = stripSize * stripT + currentT;
|
|
|
|
var symbolId = decodeIAID(contextCache, decoder, symbolCodeLength);
|
|
|
|
var applyRefinement = refinement && decodeInteger(contextCache, 'IARI', decoder);
|
|
|
|
var symbolBitmap = inputSymbols[symbolId];
|
|
|
|
var symbolWidth = symbolBitmap[0].length;
|
|
|
|
var symbolHeight = symbolBitmap.length;
|
|
|
|
if (applyRefinement) {
|
|
|
|
var rdw = decodeInteger(contextCache, 'IARDW', decoder);
|
|
|
|
var rdh = decodeInteger(contextCache, 'IARDH', decoder);
|
|
|
|
var rdx = decodeInteger(contextCache, 'IARDX', decoder);
|
|
|
|
var rdy = decodeInteger(contextCache, 'IARDY', decoder);
|
|
|
|
symbolWidth += rdw;
|
|
|
|
symbolHeight += rdh;
|
|
|
|
symbolBitmap = decodeRefinement(symbolWidth, symbolHeight, refinementTemplateIndex, symbolBitmap, (rdw >> 1) + rdx, (rdh >> 1) + rdy, false, refinementAt, decodingContext);
|
|
|
|
}
|
|
|
|
var offsetT = t - (referenceCorner & 1 ? 0 : symbolHeight);
|
|
|
|
var offsetS = currentS - (referenceCorner & 2 ? symbolWidth : 0);
|
|
|
|
var s2, t2, symbolRow;
|
|
|
|
if (transposed) {
|
|
|
|
for (s2 = 0; s2 < symbolHeight; s2++) {
|
|
|
|
row = bitmap[offsetS + s2];
|
|
|
|
if (!row) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
symbolRow = symbolBitmap[s2];
|
|
|
|
var maxWidth = Math.min(width - offsetT, symbolWidth);
|
|
|
|
switch (combinationOperator) {
|
|
|
|
case 0:
|
|
|
|
for (t2 = 0; t2 < maxWidth; t2++) {
|
|
|
|
row[offsetT + t2] |= symbolRow[t2];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
for (t2 = 0; t2 < maxWidth; t2++) {
|
|
|
|
row[offsetT + t2] ^= symbolRow[t2];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Jbig2Error('operator ' + combinationOperator + ' is not supported');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
currentS += symbolHeight - 1;
|
|
|
|
} else {
|
|
|
|
for (t2 = 0; t2 < symbolHeight; t2++) {
|
|
|
|
row = bitmap[offsetT + t2];
|
|
|
|
if (!row) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
symbolRow = symbolBitmap[t2];
|
|
|
|
switch (combinationOperator) {
|
|
|
|
case 0:
|
|
|
|
for (s2 = 0; s2 < symbolWidth; s2++) {
|
|
|
|
row[offsetS + s2] |= symbolRow[s2];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
for (s2 = 0; s2 < symbolWidth; s2++) {
|
|
|
|
row[offsetS + s2] ^= symbolRow[s2];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Jbig2Error('operator ' + combinationOperator + ' is not supported');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
currentS += symbolWidth - 1;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
var deltaS = decodeInteger(contextCache, 'IADS', decoder);
|
|
|
|
if (deltaS === null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
currentS += deltaS + dsOffset;
|
|
|
|
} while (true);
|
|
|
|
}
|
|
|
|
return bitmap;
|
|
|
|
}
|
|
|
|
function readSegmentHeader(data, start) {
|
|
|
|
var segmentHeader = {};
|
|
|
|
segmentHeader.number = (0, _util.readUint32)(data, start);
|
|
|
|
var flags = data[start + 4];
|
|
|
|
var segmentType = flags & 0x3F;
|
|
|
|
if (!SegmentTypes[segmentType]) {
|
|
|
|
throw new Jbig2Error('invalid segment type: ' + segmentType);
|
|
|
|
}
|
|
|
|
segmentHeader.type = segmentType;
|
|
|
|
segmentHeader.typeName = SegmentTypes[segmentType];
|
|
|
|
segmentHeader.deferredNonRetain = !!(flags & 0x80);
|
|
|
|
var pageAssociationFieldSize = !!(flags & 0x40);
|
|
|
|
var referredFlags = data[start + 5];
|
|
|
|
var referredToCount = referredFlags >> 5 & 7;
|
|
|
|
var retainBits = [referredFlags & 31];
|
|
|
|
var position = start + 6;
|
|
|
|
if (referredFlags === 7) {
|
|
|
|
referredToCount = (0, _util.readUint32)(data, position - 1) & 0x1FFFFFFF;
|
|
|
|
position += 3;
|
|
|
|
var bytes = referredToCount + 7 >> 3;
|
|
|
|
retainBits[0] = data[position++];
|
|
|
|
while (--bytes > 0) {
|
|
|
|
retainBits.push(data[position++]);
|
|
|
|
}
|
|
|
|
} else if (referredFlags === 5 || referredFlags === 6) {
|
|
|
|
throw new Jbig2Error('invalid referred-to flags');
|
|
|
|
}
|
|
|
|
segmentHeader.retainBits = retainBits;
|
|
|
|
var referredToSegmentNumberSize = segmentHeader.number <= 256 ? 1 : segmentHeader.number <= 65536 ? 2 : 4;
|
|
|
|
var referredTo = [];
|
|
|
|
var i, ii;
|
|
|
|
for (i = 0; i < referredToCount; i++) {
|
|
|
|
var number = referredToSegmentNumberSize === 1 ? data[position] : referredToSegmentNumberSize === 2 ? (0, _util.readUint16)(data, position) : (0, _util.readUint32)(data, position);
|
|
|
|
referredTo.push(number);
|
|
|
|
position += referredToSegmentNumberSize;
|
|
|
|
}
|
|
|
|
segmentHeader.referredTo = referredTo;
|
|
|
|
if (!pageAssociationFieldSize) {
|
|
|
|
segmentHeader.pageAssociation = data[position++];
|
|
|
|
} else {
|
|
|
|
segmentHeader.pageAssociation = (0, _util.readUint32)(data, position);
|
|
|
|
position += 4;
|
|
|
|
}
|
|
|
|
segmentHeader.length = (0, _util.readUint32)(data, position);
|
|
|
|
position += 4;
|
|
|
|
if (segmentHeader.length === 0xFFFFFFFF) {
|
|
|
|
if (segmentType === 38) {
|
|
|
|
var genericRegionInfo = readRegionSegmentInformation(data, position);
|
|
|
|
var genericRegionSegmentFlags = data[position + RegionSegmentInformationFieldLength];
|
|
|
|
var genericRegionMmr = !!(genericRegionSegmentFlags & 1);
|
|
|
|
var searchPatternLength = 6;
|
|
|
|
var searchPattern = new Uint8Array(searchPatternLength);
|
|
|
|
if (!genericRegionMmr) {
|
|
|
|
searchPattern[0] = 0xFF;
|
|
|
|
searchPattern[1] = 0xAC;
|
|
|
|
}
|
|
|
|
searchPattern[2] = genericRegionInfo.height >>> 24 & 0xFF;
|
|
|
|
searchPattern[3] = genericRegionInfo.height >> 16 & 0xFF;
|
|
|
|
searchPattern[4] = genericRegionInfo.height >> 8 & 0xFF;
|
|
|
|
searchPattern[5] = genericRegionInfo.height & 0xFF;
|
|
|
|
for (i = position, ii = data.length; i < ii; i++) {
|
|
|
|
var j = 0;
|
|
|
|
while (j < searchPatternLength && searchPattern[j] === data[i + j]) {
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
if (j === searchPatternLength) {
|
|
|
|
segmentHeader.length = i + searchPatternLength;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (segmentHeader.length === 0xFFFFFFFF) {
|
|
|
|
throw new Jbig2Error('segment end was not found');
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw new Jbig2Error('invalid unknown segment length');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
segmentHeader.headerEnd = position;
|
|
|
|
return segmentHeader;
|
|
|
|
}
|
|
|
|
function readSegments(header, data, start, end) {
|
|
|
|
var segments = [];
|
|
|
|
var position = start;
|
|
|
|
while (position < end) {
|
|
|
|
var segmentHeader = readSegmentHeader(data, position);
|
|
|
|
position = segmentHeader.headerEnd;
|
|
|
|
var segment = {
|
|
|
|
header: segmentHeader,
|
|
|
|
data: data
|
|
|
|
};
|
|
|
|
if (!header.randomAccess) {
|
|
|
|
segment.start = position;
|
|
|
|
position += segmentHeader.length;
|
|
|
|
segment.end = position;
|
|
|
|
}
|
|
|
|
segments.push(segment);
|
|
|
|
if (segmentHeader.type === 51) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (header.randomAccess) {
|
|
|
|
for (var i = 0, ii = segments.length; i < ii; i++) {
|
|
|
|
segments[i].start = position;
|
|
|
|
position += segments[i].header.length;
|
|
|
|
segments[i].end = position;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return segments;
|
|
|
|
}
|
|
|
|
function readRegionSegmentInformation(data, start) {
|
|
|
|
return {
|
|
|
|
width: (0, _util.readUint32)(data, start),
|
|
|
|
height: (0, _util.readUint32)(data, start + 4),
|
|
|
|
x: (0, _util.readUint32)(data, start + 8),
|
|
|
|
y: (0, _util.readUint32)(data, start + 12),
|
|
|
|
combinationOperator: data[start + 16] & 7
|
|
|
|
};
|
|
|
|
}
|
|
|
|
var RegionSegmentInformationFieldLength = 17;
|
|
|
|
function processSegment(segment, visitor) {
|
|
|
|
var header = segment.header;
|
|
|
|
var data = segment.data,
|
|
|
|
position = segment.start,
|
|
|
|
end = segment.end;
|
|
|
|
var args, at, i, atLength;
|
|
|
|
switch (header.type) {
|
|
|
|
case 0:
|
|
|
|
var dictionary = {};
|
|
|
|
var dictionaryFlags = (0, _util.readUint16)(data, position);
|
|
|
|
dictionary.huffman = !!(dictionaryFlags & 1);
|
|
|
|
dictionary.refinement = !!(dictionaryFlags & 2);
|
|
|
|
dictionary.huffmanDHSelector = dictionaryFlags >> 2 & 3;
|
|
|
|
dictionary.huffmanDWSelector = dictionaryFlags >> 4 & 3;
|
|
|
|
dictionary.bitmapSizeSelector = dictionaryFlags >> 6 & 1;
|
|
|
|
dictionary.aggregationInstancesSelector = dictionaryFlags >> 7 & 1;
|
|
|
|
dictionary.bitmapCodingContextUsed = !!(dictionaryFlags & 256);
|
|
|
|
dictionary.bitmapCodingContextRetained = !!(dictionaryFlags & 512);
|
|
|
|
dictionary.template = dictionaryFlags >> 10 & 3;
|
|
|
|
dictionary.refinementTemplate = dictionaryFlags >> 12 & 1;
|
|
|
|
position += 2;
|
|
|
|
if (!dictionary.huffman) {
|
|
|
|
atLength = dictionary.template === 0 ? 4 : 1;
|
|
|
|
at = [];
|
|
|
|
for (i = 0; i < atLength; i++) {
|
|
|
|
at.push({
|
|
|
|
x: (0, _util.readInt8)(data, position),
|
|
|
|
y: (0, _util.readInt8)(data, position + 1)
|
|
|
|
});
|
|
|
|
position += 2;
|
|
|
|
}
|
|
|
|
dictionary.at = at;
|
|
|
|
}
|
|
|
|
if (dictionary.refinement && !dictionary.refinementTemplate) {
|
|
|
|
at = [];
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
at.push({
|
|
|
|
x: (0, _util.readInt8)(data, position),
|
|
|
|
y: (0, _util.readInt8)(data, position + 1)
|
|
|
|
});
|
|
|
|
position += 2;
|
|
|
|
}
|
|
|
|
dictionary.refinementAt = at;
|
|
|
|
}
|
|
|
|
dictionary.numberOfExportedSymbols = (0, _util.readUint32)(data, position);
|
|
|
|
position += 4;
|
|
|
|
dictionary.numberOfNewSymbols = (0, _util.readUint32)(data, position);
|
|
|
|
position += 4;
|
|
|
|
args = [dictionary, header.number, header.referredTo, data, position, end];
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
case 7:
|
|
|
|
var textRegion = {};
|
|
|
|
textRegion.info = readRegionSegmentInformation(data, position);
|
|
|
|
position += RegionSegmentInformationFieldLength;
|
|
|
|
var textRegionSegmentFlags = (0, _util.readUint16)(data, position);
|
|
|
|
position += 2;
|
|
|
|
textRegion.huffman = !!(textRegionSegmentFlags & 1);
|
|
|
|
textRegion.refinement = !!(textRegionSegmentFlags & 2);
|
|
|
|
textRegion.stripSize = 1 << (textRegionSegmentFlags >> 2 & 3);
|
|
|
|
textRegion.referenceCorner = textRegionSegmentFlags >> 4 & 3;
|
|
|
|
textRegion.transposed = !!(textRegionSegmentFlags & 64);
|
|
|
|
textRegion.combinationOperator = textRegionSegmentFlags >> 7 & 3;
|
|
|
|
textRegion.defaultPixelValue = textRegionSegmentFlags >> 9 & 1;
|
|
|
|
textRegion.dsOffset = textRegionSegmentFlags << 17 >> 27;
|
|
|
|
textRegion.refinementTemplate = textRegionSegmentFlags >> 15 & 1;
|
|
|
|
if (textRegion.huffman) {
|
|
|
|
var textRegionHuffmanFlags = (0, _util.readUint16)(data, position);
|
|
|
|
position += 2;
|
|
|
|
textRegion.huffmanFS = textRegionHuffmanFlags & 3;
|
|
|
|
textRegion.huffmanDS = textRegionHuffmanFlags >> 2 & 3;
|
|
|
|
textRegion.huffmanDT = textRegionHuffmanFlags >> 4 & 3;
|
|
|
|
textRegion.huffmanRefinementDW = textRegionHuffmanFlags >> 6 & 3;
|
|
|
|
textRegion.huffmanRefinementDH = textRegionHuffmanFlags >> 8 & 3;
|
|
|
|
textRegion.huffmanRefinementDX = textRegionHuffmanFlags >> 10 & 3;
|
|
|
|
textRegion.huffmanRefinementDY = textRegionHuffmanFlags >> 12 & 3;
|
|
|
|
textRegion.huffmanRefinementSizeSelector = !!(textRegionHuffmanFlags & 14);
|
|
|
|
}
|
|
|
|
if (textRegion.refinement && !textRegion.refinementTemplate) {
|
|
|
|
at = [];
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
at.push({
|
|
|
|
x: (0, _util.readInt8)(data, position),
|
|
|
|
y: (0, _util.readInt8)(data, position + 1)
|
|
|
|
});
|
|
|
|
position += 2;
|
|
|
|
}
|
|
|
|
textRegion.refinementAt = at;
|
|
|
|
}
|
|
|
|
textRegion.numberOfSymbolInstances = (0, _util.readUint32)(data, position);
|
|
|
|
position += 4;
|
|
|
|
if (textRegion.huffman) {
|
|
|
|
throw new Jbig2Error('huffman is not supported');
|
|
|
|
}
|
|
|
|
args = [textRegion, header.referredTo, data, position, end];
|
|
|
|
break;
|
|
|
|
case 38:
|
|
|
|
case 39:
|
|
|
|
var genericRegion = {};
|
|
|
|
genericRegion.info = readRegionSegmentInformation(data, position);
|
|
|
|
position += RegionSegmentInformationFieldLength;
|
|
|
|
var genericRegionSegmentFlags = data[position++];
|
|
|
|
genericRegion.mmr = !!(genericRegionSegmentFlags & 1);
|
|
|
|
genericRegion.template = genericRegionSegmentFlags >> 1 & 3;
|
|
|
|
genericRegion.prediction = !!(genericRegionSegmentFlags & 8);
|
|
|
|
if (!genericRegion.mmr) {
|
|
|
|
atLength = genericRegion.template === 0 ? 4 : 1;
|
|
|
|
at = [];
|
|
|
|
for (i = 0; i < atLength; i++) {
|
|
|
|
at.push({
|
|
|
|
x: (0, _util.readInt8)(data, position),
|
|
|
|
y: (0, _util.readInt8)(data, position + 1)
|
|
|
|
});
|
|
|
|
position += 2;
|
|
|
|
}
|
|
|
|
genericRegion.at = at;
|
|
|
|
}
|
|
|
|
args = [genericRegion, data, position, end];
|
|
|
|
break;
|
|
|
|
case 48:
|
|
|
|
var pageInfo = {
|
|
|
|
width: (0, _util.readUint32)(data, position),
|
|
|
|
height: (0, _util.readUint32)(data, position + 4),
|
|
|
|
resolutionX: (0, _util.readUint32)(data, position + 8),
|
|
|
|
resolutionY: (0, _util.readUint32)(data, position + 12)
|
|
|
|
};
|
|
|
|
if (pageInfo.height === 0xFFFFFFFF) {
|
|
|
|
delete pageInfo.height;
|
|
|
|
}
|
|
|
|
var pageSegmentFlags = data[position + 16];
|
|
|
|
(0, _util.readUint16)(data, position + 17);
|
|
|
|
pageInfo.lossless = !!(pageSegmentFlags & 1);
|
|
|
|
pageInfo.refinement = !!(pageSegmentFlags & 2);
|
|
|
|
pageInfo.defaultPixelValue = pageSegmentFlags >> 2 & 1;
|
|
|
|
pageInfo.combinationOperator = pageSegmentFlags >> 3 & 3;
|
|
|
|
pageInfo.requiresBuffer = !!(pageSegmentFlags & 32);
|
|
|
|
pageInfo.combinationOperatorOverride = !!(pageSegmentFlags & 64);
|
|
|
|
args = [pageInfo];
|
|
|
|
break;
|
|
|
|
case 49:
|
|
|
|
break;
|
|
|
|
case 50:
|
|
|
|
break;
|
|
|
|
case 51:
|
|
|
|
break;
|
|
|
|
case 62:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Jbig2Error('segment type ' + header.typeName + '(' + header.type + ')' + ' is not implemented');
|
|
|
|
}
|
|
|
|
var callbackName = 'on' + header.typeName;
|
|
|
|
if (callbackName in visitor) {
|
|
|
|
visitor[callbackName].apply(visitor, args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function processSegments(segments, visitor) {
|
|
|
|
for (var i = 0, ii = segments.length; i < ii; i++) {
|
|
|
|
processSegment(segments[i], visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function parseJbig2(data, start, end) {
|
|
|
|
var position = start;
|
|
|
|
if (data[position] !== 0x97 || data[position + 1] !== 0x4A || data[position + 2] !== 0x42 || data[position + 3] !== 0x32 || data[position + 4] !== 0x0D || data[position + 5] !== 0x0A || data[position + 6] !== 0x1A || data[position + 7] !== 0x0A) {
|
|
|
|
throw new Jbig2Error('invalid header');
|
|
|
|
}
|
|
|
|
var header = {};
|
|
|
|
position += 8;
|
|
|
|
var flags = data[position++];
|
|
|
|
header.randomAccess = !(flags & 1);
|
|
|
|
if (!(flags & 2)) {
|
|
|
|
header.numberOfPages = (0, _util.readUint32)(data, position);
|
|
|
|
position += 4;
|
|
|
|
}
|
|
|
|
readSegments(header, data, position, end);
|
|
|
|
throw new Error('Not implemented');
|
|
|
|
}
|
|
|
|
function parseJbig2Chunks(chunks) {
|
|
|
|
var visitor = new SimpleSegmentVisitor();
|
|
|
|
for (var i = 0, ii = chunks.length; i < ii; i++) {
|
|
|
|
var chunk = chunks[i];
|
|
|
|
var segments = readSegments({}, chunk.data, chunk.start, chunk.end);
|
|
|
|
processSegments(segments, visitor);
|
|
|
|
}
|
|
|
|
return visitor.buffer;
|
|
|
|
}
|
|
|
|
function SimpleSegmentVisitor() {}
|
|
|
|
SimpleSegmentVisitor.prototype = {
|
|
|
|
onPageInformation: function SimpleSegmentVisitor_onPageInformation(info) {
|
|
|
|
this.currentPageInfo = info;
|
|
|
|
var rowSize = info.width + 7 >> 3;
|
|
|
|
var buffer = new Uint8Array(rowSize * info.height);
|
|
|
|
if (info.defaultPixelValue) {
|
|
|
|
for (var i = 0, ii = buffer.length; i < ii; i++) {
|
|
|
|
buffer[i] = 0xFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.buffer = buffer;
|
|
|
|
},
|
|
|
|
drawBitmap: function SimpleSegmentVisitor_drawBitmap(regionInfo, bitmap) {
|
|
|
|
var pageInfo = this.currentPageInfo;
|
|
|
|
var width = regionInfo.width,
|
|
|
|
height = regionInfo.height;
|
|
|
|
var rowSize = pageInfo.width + 7 >> 3;
|
|
|
|
var combinationOperator = pageInfo.combinationOperatorOverride ? regionInfo.combinationOperator : pageInfo.combinationOperator;
|
|
|
|
var buffer = this.buffer;
|
|
|
|
var mask0 = 128 >> (regionInfo.x & 7);
|
|
|
|
var offset0 = regionInfo.y * rowSize + (regionInfo.x >> 3);
|
|
|
|
var i, j, mask, offset;
|
|
|
|
switch (combinationOperator) {
|
|
|
|
case 0:
|
|
|
|
for (i = 0; i < height; i++) {
|
|
|
|
mask = mask0;
|
|
|
|
offset = offset0;
|
|
|
|
for (j = 0; j < width; j++) {
|
|
|
|
if (bitmap[i][j]) {
|
|
|
|
buffer[offset] |= mask;
|
|
|
|
}
|
|
|
|
mask >>= 1;
|
|
|
|
if (!mask) {
|
|
|
|
mask = 128;
|
|
|
|
offset++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
offset0 += rowSize;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
for (i = 0; i < height; i++) {
|
|
|
|
mask = mask0;
|
|
|
|
offset = offset0;
|
|
|
|
for (j = 0; j < width; j++) {
|
|
|
|
if (bitmap[i][j]) {
|
|
|
|
buffer[offset] ^= mask;
|
|
|
|
}
|
|
|
|
mask >>= 1;
|
|
|
|
if (!mask) {
|
|
|
|
mask = 128;
|
|
|
|
offset++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
offset0 += rowSize;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Jbig2Error('operator ' + combinationOperator + ' is not supported');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onImmediateGenericRegion: function SimpleSegmentVisitor_onImmediateGenericRegion(region, data, start, end) {
|
|
|
|
var regionInfo = region.info;
|
|
|
|
var decodingContext = new DecodingContext(data, start, end);
|
|
|
|
var bitmap = decodeBitmap(region.mmr, regionInfo.width, regionInfo.height, region.template, region.prediction, null, region.at, decodingContext);
|
|
|
|
this.drawBitmap(regionInfo, bitmap);
|
|
|
|
},
|
|
|
|
onImmediateLosslessGenericRegion: function SimpleSegmentVisitor_onImmediateLosslessGenericRegion() {
|
|
|
|
this.onImmediateGenericRegion.apply(this, arguments);
|
|
|
|
},
|
|
|
|
onSymbolDictionary: function SimpleSegmentVisitor_onSymbolDictionary(dictionary, currentSegment, referredSegments, data, start, end) {
|
|
|
|
var huffmanTables;
|
|
|
|
if (dictionary.huffman) {
|
|
|
|
throw new Jbig2Error('huffman is not supported');
|
|
|
|
}
|
|
|
|
var symbols = this.symbols;
|
|
|
|
if (!symbols) {
|
|
|
|
this.symbols = symbols = {};
|
|
|
|
}
|
|
|
|
var inputSymbols = [];
|
|
|
|
for (var i = 0, ii = referredSegments.length; i < ii; i++) {
|
|
|
|
inputSymbols = inputSymbols.concat(symbols[referredSegments[i]]);
|
|
|
|
}
|
|
|
|
var decodingContext = new DecodingContext(data, start, end);
|
|
|
|
symbols[currentSegment] = decodeSymbolDictionary(dictionary.huffman, dictionary.refinement, inputSymbols, dictionary.numberOfNewSymbols, dictionary.numberOfExportedSymbols, huffmanTables, dictionary.template, dictionary.at, dictionary.refinementTemplate, dictionary.refinementAt, decodingContext);
|
|
|
|
},
|
|
|
|
onImmediateTextRegion: function SimpleSegmentVisitor_onImmediateTextRegion(region, referredSegments, data, start, end) {
|
|
|
|
var regionInfo = region.info;
|
|
|
|
var huffmanTables;
|
|
|
|
var symbols = this.symbols;
|
|
|
|
var inputSymbols = [];
|
|
|
|
for (var i = 0, ii = referredSegments.length; i < ii; i++) {
|
|
|
|
inputSymbols = inputSymbols.concat(symbols[referredSegments[i]]);
|
|
|
|
}
|
|
|
|
var symbolCodeLength = (0, _util.log2)(inputSymbols.length);
|
|
|
|
var decodingContext = new DecodingContext(data, start, end);
|
|
|
|
var bitmap = decodeTextRegion(region.huffman, region.refinement, regionInfo.width, regionInfo.height, region.defaultPixelValue, region.numberOfSymbolInstances, region.stripSize, inputSymbols, symbolCodeLength, region.transposed, region.dsOffset, region.referenceCorner, region.combinationOperator, huffmanTables, region.refinementTemplate, region.refinementAt, decodingContext);
|
|
|
|
this.drawBitmap(regionInfo, bitmap);
|
|
|
|
},
|
|
|
|
onImmediateLosslessTextRegion: function SimpleSegmentVisitor_onImmediateLosslessTextRegion() {
|
|
|
|
this.onImmediateTextRegion.apply(this, arguments);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
function Jbig2Image() {}
|
|
|
|
Jbig2Image.prototype = {
|
|
|
|
parseChunks: function Jbig2Image_parseChunks(chunks) {
|
|
|
|
return parseJbig2Chunks(chunks);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return Jbig2Image;
|
|
|
|
}();
|
|
|
|
exports.Jbig2Image = Jbig2Image;
|