: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
number = readUint32(data, position);
position += referredToSegmentNumberSize;
segmentHeader.referredTo = referredTo;
if (!pageAssociationFieldSize) {
segmentHeader.pageAssociation = data[position++];
segmentHeader.pageAssociation = readUint32(data, position);
segmentHeader.length = readUint32(data, position);
if (segmentHeader.length === 0xffffffff) {
if (segmentType === 38) {
const genericRegionInfo = readRegionSegmentInformation(data, position);
const genericRegionSegmentFlags = data[position + RegionSegmentInformationFieldLength];
const genericRegionMmr = !!(genericRegionSegmentFlags & 1);
const searchPatternLength = 6;
const searchPattern = new Uint8Array(searchPatternLength);
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++) {
while (j < searchPatternLength && searchPattern[j] === data[i + j]) {
if (j === searchPatternLength) {
segmentHeader.length = i + searchPatternLength;
if (segmentHeader.length === 0xffffffff) {
throw new Jbig2Error("segment end was not found");
throw new Jbig2Error("invalid unknown segment length");
segmentHeader.headerEnd = position;
function readSegments(header, data, start, end) {
const segmentHeader = readSegmentHeader(data, position);
position = segmentHeader.headerEnd;
if (!header.randomAccess) {
segment.start = position;
position += segmentHeader.length;
if (segmentHeader.type === 51) {
if (header.randomAccess) {
for (let i = 0, ii = segments.length; i < ii; i++) {
segments[i].start = position;
position += segments[i].header.length;
segments[i].end = position;
function readRegionSegmentInformation(data, start) {
width: readUint32(data, start),
height: readUint32(data, start + 4),
x: readUint32(data, start + 8),
y: readUint32(data, start + 12),
combinationOperator: data[start + 16] & 7
const RegionSegmentInformationFieldLength = 17;
function processSegment(segment, visitor) {
const header = segment.header;
const data = segment.data,
let position = segment.start;
let args, at, i, atLength;
const dictionaryFlags = 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;
if (!dictionary.huffman) {
atLength = dictionary.template === 0 ? 4 : 1;
for (i = 0; i < atLength; i++) {
x: readInt8(data, position),
y: readInt8(data, position + 1)
if (dictionary.refinement && !dictionary.refinementTemplate) {
for (i = 0; i < 2; i++) {
x: readInt8(data, position),
y: readInt8(data, position + 1)
dictionary.refinementAt = at;
dictionary.numberOfExportedSymbols = readUint32(data, position);
dictionary.numberOfNewSymbols = readUint32(data, position);
args = [dictionary, header.number, header.referredTo, data, position, end];
textRegion.info = readRegionSegmentInformation(data, position);
position += RegionSegmentInformationFieldLength;
const textRegionSegmentFlags = readUint16(data, position);
textRegion.huffman = !!(textRegionSegmentFlags & 1);
textRegion.refinement = !!(textRegionSegmentFlags & 2);
textRegion.logStripSize = textRegionSegmentFlags >> 2 & 3;
textRegion.stripSize = 1 << textRegion.logStripSize;
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) {
const textRegionHuffmanFlags = readUint16(data, position);
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 & 0x4000);
if (textRegion.refinement && !textRegion.refinementTemplate) {
for (i = 0; i < 2; i++) {
x: readInt8(data, position),
y: readInt8(data, position + 1)
textRegion.refinementAt = at;
textRegion.numberOfSymbolInstances = readUint32(data, position);
args = [textRegion, header.referredTo, data, position, end];
const patternDictionary = {};
const patternDictionaryFlags = data[position++];
patternDictionary.mmr = !!(patternDictionaryFlags & 1);
patternDictionary.template = patternDictionaryFlags >> 1 & 3;
patternDictionary.patternWidth = data[position++];
patternDictionary.patternHeight = data[position++];
patternDictionary.maxPatternIndex = readUint32(data, position);
args = [patternDictionary, header.number, data, position, end];
const halftoneRegion = {};
halftoneRegion.info = readRegionSegmentInformation(data, position);
position += RegionSegmentInformationFieldLength;
const halftoneRegionFlags = data[position++];
halftoneRegion.mmr = !!(halftoneRegionFlags & 1);
halftoneRegion.template = halftoneRegionFlags >> 1 & 3;
halftoneRegion.enableSkip = !!(halftoneRegionFlags & 8);
halftoneRegion.combinationOperator = halftoneRegionFlags >> 4 & 7;
halftoneRegion.defaultPixelValue = halftoneRegionFlags >> 7 & 1;
halftoneRegion.gridWidth = readUint32(data, position);
halftoneRegion.gridHeight = readUint32(data, position);
halftoneRegion.gridOffsetX = readUint32(data, position) & 0xffffffff;
halftoneRegion.gridOffsetY = readUint32(data, position) & 0xffffffff;
halftoneRegion.gridVectorX = readUint16(data, position);
halftoneRegion.gridVectorY = readUint16(data, position);
args = [halftoneRegion, header.referredTo, data, position, end];
const genericRegion = {};
genericRegion.info = readRegionSegmentInformation(data, position);
position += RegionSegmentInformationFieldLength;
const 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;
for (i = 0; i < atLength; i++) {
x: readInt8(data, position),
y: readInt8(data, position + 1)
args = [genericRegion, data, position, end];
width: readUint32(data, position),
height: readUint32(data, position + 4),
resolutionX: readUint32(data, position + 8),
resolutionY: readUint32(data, position + 12)
if (pageInfo.height === 0xffffffff) {
const pageSegmentFlags = data[position + 16];
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 = [header.number, data, position, end];
throw new Jbig2Error(`segment type ${header.typeName}(${header.type}) is not implemented`);
const callbackName = "on" + header.typeName;
if (callbackName in visitor) {
visitor[callbackName].apply(visitor, args);
function processSegments(segments, visitor) {
for (let i = 0, ii = segments.length; i < ii; i++) {
processSegment(segments[i], visitor);
function parseJbig2Chunks(chunks) {
const visitor = new SimpleSegmentVisitor();
for (let i = 0, ii = chunks.length; i < ii; i++) {
const segments = readSegments({}, chunk.data, chunk.start, chunk.end);
processSegments(segments, visitor);
function parseJbig2(data) {
throw new Error("Not implemented: parseJbig2");
class SimpleSegmentVisitor {
onPageInformation(info) {
this.currentPageInfo = info;
const rowSize = info.width + 7 >> 3;
const buffer = new Uint8ClampedArray(rowSize * info.height);
if (info.defaultPixelValue) {
drawBitmap(regionInfo, bitmap) {
const pageInfo = this.currentPageInfo;
const width = regionInfo.width,
height = regionInfo.height;
const rowSize = pageInfo.width + 7 >> 3;
const combinationOperator = pageInfo.combinationOperatorOverride ? regionInfo.combinationOperator : pageInfo.combinationOperator;
const buffer = this.buffer;
const mask0 = 128 >> (regionInfo.x & 7);
let offset0 = regionInfo.y * rowSize + (regionInfo.x >> 3);
switch (combinationOperator) {
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
throw new Jbig2Error(`operator ${combinationOperator} is not supported`);
onImmediateGenericRegion(region, data, start, end) {
const regionInfo = region.info;
const decodingContext = new DecodingContext(data, start, end);
const bitmap = decodeBitmap(region.mmr, regionInfo.width, regionInfo.height, region.template, region.prediction, null, region.at, decodingContext);
this.drawBitmap(regionInfo, bitmap);
onImmediateLosslessGenericRegion() {
this.onImmediateGenericRegion(...arguments);
onSymbolDictionary(dictionary, currentSegment, referredSegments, data, start, end) {
let huffmanTables, huffmanInput;
if (dictionary.huffman) {
huffmanTables = getSymbolDictionaryHuffmanTables(dictionary, referredSegments, this.customTables);
huffmanInput = new Reader(data, start, end);
let symbols = this.symbols;
this.symbols = symbols = {};
for (const referredSegment of referredSegments) {
const referredSymbols = symbols[referredSegment];
inputSymbols.push(...referredSymbols);
const 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, huffmanInput);
onImmediateTextRegion(region, referredSegments, data, start, end) {
const regionInfo = region.info;
let huffmanTables, huffmanInput;
const symbols = this.symbols;
for (const referredSegment of referredSegments) {
const referredSymbols = symbols[referredSegment];
inputSymbols.push(...referredSymbols);
const symbolCodeLength = log2(inputSymbols.length);
huffmanInput = new Reader(data, start, end);
huffmanTables = getTextRegionHuffmanTables(region, referredSegments, this.customTables, inputSymbols.length, huffmanInput);
const decodingContext = new DecodingContext(data, start, end);
const 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, region.logStripSize, huffmanInput);
this.drawBitmap(regionInfo, bitmap);
onImmediateLosslessTextRegion() {
this.onImmediateTextRegion(...arguments);
onPatternDictionary(dictionary, currentSegment, data, start, end) {
let patterns = this.patterns;
this.patterns = patterns = {};
const decodingContext = new DecodingContext(data, start, end);
patterns[currentSegment] = decodePatternDictionary(dictionary.mmr, dictionary.patternWidth, dictionary.patternHeight, dictionary.maxPatternIndex, dictionary.template, decodingContext);
onImmediateHalftoneRegion(region, referredSegments, data, start, end) {
const patterns = this.patterns[referredSegments[0]];
const regionInfo = region.info;
const decodingContext = new DecodingContext(data, start, end);
const bitmap = decodeHalftoneRegion(region.mmr, patterns, region.template, regionInfo.width, regionInfo.height, region.defaultPixelValue, region.enableSkip, region.combinationOperator, region.gridWidth, region.gridHeight, region.gridOffsetX, region.gridOffsetY, region.gridVectorX, region.gridVectorY, decodingContext);
this.drawBitmap(regionInfo, bitmap);
onImmediateLosslessHalftoneRegion() {
this.onImmediateHalftoneRegion(...arguments);
onTables(currentSegment, data, start, end) {
let customTables = this.customTables;
this.customTables = customTables = {};
customTables[currentSegment] = decodeTablesSegment(data, start, end);
if (lineData.length === 2) {
this.prefixLength = lineData[0];
this.prefixCode = lineData[1];
this.isLowerRange = false;
this.rangeLow = lineData[0];
this.prefixLength = lineData[1];
this.rangeLength = lineData[2];
this.prefixCode = lineData[3];
this.isLowerRange = lineData[4] === "lower";
this.rangeLength = line.rangeLength;
this.rangeLow = line.rangeLow;
this.isLowerRange = line.isLowerRange;
const bit = line.prefixCode >> shift & 1;
this.children[bit] = new HuffmanTreeNode(line);
let node = this.children[bit];
this.children[bit] = node = new HuffmanTreeNode(null);
node.buildTree(line, shift - 1);
const htOffset = reader.readBits(this.rangeLength);
return this.rangeLow + (this.isLowerRange ? -htOffset : htOffset);
const node = this.children[reader.readBit()];
throw new Jbig2Error("invalid Huffman data");
return node.decodeNode(reader);
constructor(lines, prefixCodesDone) {
this.assignPrefixCodes(lines);
this.rootNode = new HuffmanTreeNode(null);
for (let i = 0, ii = lines.length; i < ii; i++) {