| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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. |
| */ |
| |
| package org.apache.pdfbox.jbig2.segments; |
| |
| import java.awt.Rectangle; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| |
| import org.apache.pdfbox.jbig2.Bitmap; |
| import org.apache.pdfbox.jbig2.Dictionary; |
| import org.apache.pdfbox.jbig2.JBIG2ImageReader; |
| import org.apache.pdfbox.jbig2.Region; |
| import org.apache.pdfbox.jbig2.SegmentHeader; |
| import org.apache.pdfbox.jbig2.decoder.arithmetic.ArithmeticDecoder; |
| import org.apache.pdfbox.jbig2.decoder.arithmetic.ArithmeticIntegerDecoder; |
| import org.apache.pdfbox.jbig2.decoder.arithmetic.CX; |
| import org.apache.pdfbox.jbig2.decoder.huffman.EncodedTable; |
| import org.apache.pdfbox.jbig2.decoder.huffman.HuffmanTable; |
| import org.apache.pdfbox.jbig2.decoder.huffman.StandardTables; |
| import org.apache.pdfbox.jbig2.err.IntegerMaxValueException; |
| import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException; |
| import org.apache.pdfbox.jbig2.image.Bitmaps; |
| import org.apache.pdfbox.jbig2.io.SubInputStream; |
| import org.apache.pdfbox.jbig2.util.log.Logger; |
| import org.apache.pdfbox.jbig2.util.log.LoggerFactory; |
| |
| /** |
| * This class represents the data of segment type "Symbol dictionary". Parsing is described in 7.4.2.1.1 - 7.4.1.1.5 and |
| * decoding procedure is described in 6.5. |
| */ |
| public class SymbolDictionary implements Dictionary |
| { |
| |
| private final Logger log = LoggerFactory.getLogger(SymbolDictionary.class); |
| |
| private SubInputStream subInputStream; |
| |
| /** Symbol dictionary flags, 7.4.2.1.1 */ |
| private short sdrTemplate; |
| private byte sdTemplate; |
| private boolean isCodingContextRetained; |
| private boolean isCodingContextUsed; |
| private short sdHuffAggInstanceSelection; |
| private short sdHuffBMSizeSelection; |
| private short sdHuffDecodeWidthSelection; |
| private short sdHuffDecodeHeightSelection; |
| private boolean useRefinementAggregation; |
| private boolean isHuffmanEncoded; |
| |
| /** Symbol dictionary AT flags, 7.4.2.1.2 */ |
| private short[] sdATX; |
| private short[] sdATY; |
| |
| /** Symbol dictionary refinement AT flags, 7.4.2.1.3 */ |
| private short[] sdrATX; |
| private short[] sdrATY; |
| |
| /** Number of exported symbols, 7.4.2.1.4 */ |
| private int amountOfExportSymbolss; |
| |
| /** Number of new symbols, 7.4.2.1.5 */ |
| private int amountOfNewSymbols; |
| |
| /** Further parameters */ |
| private SegmentHeader segmentHeader; |
| private int amountOfImportedSymbolss; |
| private ArrayList<Bitmap> importSymbols; |
| private int amountOfDecodedSymbols; |
| private Bitmap[] newSymbols; |
| |
| /** User-supplied tables * */ |
| private HuffmanTable dhTable; |
| private HuffmanTable dwTable; |
| private HuffmanTable bmSizeTable; |
| private HuffmanTable aggInstTable; |
| |
| /** Return value of that segment */ |
| private ArrayList<Bitmap> exportSymbols; |
| private ArrayList<Bitmap> sbSymbols; |
| |
| private ArithmeticDecoder arithmeticDecoder; |
| private ArithmeticIntegerDecoder iDecoder; |
| |
| private TextRegion textRegion; |
| private GenericRegion genericRegion; |
| private GenericRefinementRegion genericRefinementRegion; |
| private CX cx; |
| |
| private CX cxIADH; |
| private CX cxIADW; |
| private CX cxIAAI; |
| private CX cxIAEX; |
| private CX cxIARDX; |
| private CX cxIARDY; |
| private CX cxIADT; |
| |
| protected CX cxIAID; |
| private int sbSymCodeLen; |
| |
| public SymbolDictionary() |
| { |
| } |
| |
| public SymbolDictionary(final SubInputStream subInputStream, final SegmentHeader segmentHeader) |
| throws IOException |
| { |
| this.subInputStream = subInputStream; |
| this.segmentHeader = segmentHeader; |
| } |
| |
| private void parseHeader() |
| throws IOException, InvalidHeaderValueException, IntegerMaxValueException |
| { |
| readRegionFlags(); |
| setAtPixels(); |
| setRefinementAtPixels(); |
| readAmountOfExportedSymbols(); |
| readAmountOfNewSymbols(); |
| setInSyms(); |
| |
| if (isCodingContextUsed) |
| { |
| SegmentHeader[] rtSegments = segmentHeader.getRtSegments(); |
| |
| for (int i = rtSegments.length - 1; i >= 0; i--) |
| { |
| |
| if (rtSegments[i].getSegmentType() == 0) |
| { |
| SymbolDictionary symbolDictionary = (SymbolDictionary) rtSegments[i] |
| .getSegmentData(); |
| |
| if (symbolDictionary.isCodingContextRetained) |
| { |
| /* 7.4.2.2 3) */ |
| setRetainedCodingContexts(symbolDictionary); |
| } |
| break; |
| } |
| } |
| } |
| |
| this.checkInput(); |
| } |
| |
| private void readRegionFlags() throws IOException |
| { |
| /* Bit 13-15 */ |
| subInputStream.readBits(3); // Dirty read... reserved bits must be 0 |
| |
| /* Bit 12 */ |
| sdrTemplate = (short) subInputStream.readBit(); |
| |
| /* Bit 10-11 */ |
| sdTemplate = (byte) (subInputStream.readBits(2) & 0xf); |
| |
| /* Bit 9 */ |
| if (subInputStream.readBit() == 1) |
| { |
| isCodingContextRetained = true; |
| } |
| |
| /* Bit 8 */ |
| if (subInputStream.readBit() == 1) |
| { |
| isCodingContextUsed = true; |
| } |
| |
| /* Bit 7 */ |
| sdHuffAggInstanceSelection = (short) subInputStream.readBit(); |
| |
| /* Bit 6 */ |
| sdHuffBMSizeSelection = (short) subInputStream.readBit(); |
| |
| /* Bit 4-5 */ |
| sdHuffDecodeWidthSelection = (short) (subInputStream.readBits(2) & 0xf); |
| |
| /* Bit 2-3 */ |
| sdHuffDecodeHeightSelection = (short) (subInputStream.readBits(2) & 0xf); |
| |
| /* Bit 1 */ |
| if (subInputStream.readBit() == 1) |
| { |
| useRefinementAggregation = true; |
| } |
| |
| /* Bit 0 */ |
| if (subInputStream.readBit() == 1) |
| { |
| isHuffmanEncoded = true; |
| } |
| } |
| |
| private void setAtPixels() throws IOException |
| { |
| if (!isHuffmanEncoded) |
| { |
| if (sdTemplate == 0) |
| { |
| readAtPixels(4); |
| } |
| else |
| { |
| readAtPixels(1); |
| } |
| } |
| } |
| |
| private void setRefinementAtPixels() throws IOException |
| { |
| if (useRefinementAggregation && sdrTemplate == 0) |
| { |
| readRefinementAtPixels(2); |
| } |
| } |
| |
| private void readAtPixels(final int amountOfPixels) throws IOException |
| { |
| sdATX = new short[amountOfPixels]; |
| sdATY = new short[amountOfPixels]; |
| |
| for (int i = 0; i < amountOfPixels; i++) |
| { |
| sdATX[i] = subInputStream.readByte(); |
| sdATY[i] = subInputStream.readByte(); |
| } |
| } |
| |
| private void readRefinementAtPixels(final int amountOfAtPixels) throws IOException |
| { |
| sdrATX = new short[amountOfAtPixels]; |
| sdrATY = new short[amountOfAtPixels]; |
| |
| for (int i = 0; i < amountOfAtPixels; i++) |
| { |
| sdrATX[i] = subInputStream.readByte(); |
| sdrATY[i] = subInputStream.readByte(); |
| } |
| } |
| |
| private void readAmountOfExportedSymbols() throws IOException |
| { |
| amountOfExportSymbolss = (int) subInputStream.readBits(32); // & 0xffffffff; |
| } |
| |
| private void readAmountOfNewSymbols() throws IOException |
| { |
| amountOfNewSymbols = (int) subInputStream.readBits(32); // & 0xffffffff; |
| } |
| |
| private void setInSyms() |
| throws IOException, InvalidHeaderValueException, IntegerMaxValueException |
| { |
| if (segmentHeader.getRtSegments() != null) |
| { |
| retrieveImportSymbols(); |
| } |
| else |
| { |
| importSymbols = new ArrayList<Bitmap>(); |
| } |
| } |
| |
| private void setRetainedCodingContexts(final SymbolDictionary sd) |
| { |
| this.arithmeticDecoder = sd.arithmeticDecoder; |
| this.isHuffmanEncoded = sd.isHuffmanEncoded; |
| this.useRefinementAggregation = sd.useRefinementAggregation; |
| this.sdTemplate = sd.sdTemplate; |
| this.sdrTemplate = sd.sdrTemplate; |
| this.sdATX = sd.sdATX; |
| this.sdATY = sd.sdATY; |
| this.sdrATX = sd.sdrATX; |
| this.sdrATY = sd.sdrATY; |
| this.cx = sd.cx; |
| } |
| |
| private void checkInput() throws InvalidHeaderValueException |
| { |
| if (sdHuffDecodeHeightSelection == 2) |
| { |
| log.info("sdHuffDecodeHeightSelection = " + sdHuffDecodeHeightSelection |
| + " (value not permitted)"); |
| } |
| |
| if (sdHuffDecodeWidthSelection == 2) |
| { |
| log.info("sdHuffDecodeWidthSelection = " + sdHuffDecodeWidthSelection |
| + " (value not permitted)"); |
| } |
| |
| if (isHuffmanEncoded) |
| { |
| if (sdTemplate != 0) |
| { |
| log.info("sdTemplate = " + sdTemplate + " (should be 0)"); |
| sdTemplate = 0; |
| } |
| if (!useRefinementAggregation) |
| { |
| if (isCodingContextRetained) |
| { |
| log.info("isCodingContextRetained = " + isCodingContextRetained |
| + " (should be 0)"); |
| isCodingContextRetained = false; |
| } |
| |
| if (isCodingContextUsed) |
| { |
| log.info("isCodingContextUsed = " + isCodingContextUsed + " (should be 0)"); |
| isCodingContextUsed = false; |
| } |
| } |
| |
| } |
| else |
| { |
| if (sdHuffBMSizeSelection != 0) |
| { |
| log.info("sdHuffBMSizeSelection should be 0"); |
| sdHuffBMSizeSelection = 0; |
| } |
| if (sdHuffDecodeWidthSelection != 0) |
| { |
| log.info("sdHuffDecodeWidthSelection should be 0"); |
| sdHuffDecodeWidthSelection = 0; |
| } |
| if (sdHuffDecodeHeightSelection != 0) |
| { |
| log.info("sdHuffDecodeHeightSelection should be 0"); |
| sdHuffDecodeHeightSelection = 0; |
| } |
| } |
| |
| if (!useRefinementAggregation) |
| { |
| if (sdrTemplate != 0) |
| { |
| log.info("sdrTemplate = " + sdrTemplate + " (should be 0)"); |
| sdrTemplate = 0; |
| } |
| } |
| |
| if (!isHuffmanEncoded || !useRefinementAggregation) |
| { |
| if (sdHuffAggInstanceSelection != 0) |
| { |
| log.info("sdHuffAggInstanceSelection = " + sdHuffAggInstanceSelection |
| + " (should be 0)"); |
| sdHuffAggInstanceSelection = 0; |
| } |
| } |
| } |
| |
| /** |
| * 6.5.5 Decoding the symbol dictionary |
| * |
| * @return List of decoded symbol bitmaps as an <code>ArrayList</code> |
| */ |
| public ArrayList<Bitmap> getDictionary() |
| throws IOException, IntegerMaxValueException, InvalidHeaderValueException |
| { |
| long timestamp = System.currentTimeMillis(); |
| if (null == exportSymbols) |
| { |
| |
| if (useRefinementAggregation) |
| sbSymCodeLen = getSbSymCodeLen(); |
| |
| if (!isHuffmanEncoded) |
| { |
| setCodingStatistics(); |
| } |
| |
| /* 6.5.5 1) */ |
| newSymbols = new Bitmap[amountOfNewSymbols]; |
| |
| /* 6.5.5 2) */ |
| int[] newSymbolsWidths = null; |
| if (isHuffmanEncoded && !useRefinementAggregation) |
| { |
| newSymbolsWidths = new int[amountOfNewSymbols]; |
| } |
| |
| setSymbolsArray(); |
| |
| /* 6.5.5 3) */ |
| int heightClassHeight = 0; |
| amountOfDecodedSymbols = 0; |
| |
| /* 6.5.5 4 a) */ |
| while (amountOfDecodedSymbols < amountOfNewSymbols) |
| { |
| |
| /* 6.5.5 4 b) */ |
| heightClassHeight += decodeHeightClassDeltaHeight(); |
| int symbolWidth = 0; |
| int totalWidth = 0; |
| final int heightClassFirstSymbolIndex = amountOfDecodedSymbols; |
| |
| /* 6.5.5 4 c) */ |
| |
| // Repeat until OOB - OOB sends a break; |
| while (true) |
| { |
| /* 4 c) i) */ |
| final long differenceWidth = decodeDifferenceWidth(); |
| |
| /* |
| * If result is OOB, then all the symbols in this height class has been decoded; proceed to step 4 |
| * d). Also exit, if the expected number of symbols have been decoded. |
| * |
| * The latter exit condition guards against pathological cases where a symbol's DW never contains |
| * OOB and thus never terminates. |
| */ |
| if (differenceWidth == Long.MAX_VALUE |
| || amountOfDecodedSymbols >= amountOfNewSymbols) |
| { |
| break; |
| } |
| |
| symbolWidth += differenceWidth; |
| totalWidth += symbolWidth; |
| |
| /* 4 c) ii) */ |
| if (!isHuffmanEncoded || useRefinementAggregation) |
| { |
| if (!useRefinementAggregation) |
| { |
| // 6.5.8.1 - Direct coded |
| decodeDirectlyThroughGenericRegion(symbolWidth, heightClassHeight); |
| } |
| else |
| { |
| // 6.5.8.2 - Refinement/Aggregate-coded |
| decodeAggregate(symbolWidth, heightClassHeight); |
| } |
| } |
| else if (isHuffmanEncoded && !useRefinementAggregation) |
| { |
| /* 4 c) iii) */ |
| newSymbolsWidths[amountOfDecodedSymbols] = symbolWidth; |
| } |
| amountOfDecodedSymbols++; |
| } |
| |
| /* 6.5.5 4 d) */ |
| if (isHuffmanEncoded && !useRefinementAggregation) |
| { |
| /* 6.5.9 */ |
| final long bmSize; |
| if (sdHuffBMSizeSelection == 0) |
| { |
| bmSize = StandardTables.getTable(1).decode(subInputStream); |
| } |
| else |
| { |
| bmSize = huffDecodeBmSize(); |
| } |
| |
| subInputStream.skipBits(); |
| |
| final Bitmap heightClassCollectiveBitmap = decodeHeightClassCollectiveBitmap( |
| bmSize, heightClassHeight, totalWidth); |
| |
| subInputStream.skipBits(); |
| decodeHeightClassBitmap(heightClassCollectiveBitmap, |
| heightClassFirstSymbolIndex, heightClassHeight, newSymbolsWidths); |
| } |
| } |
| |
| /* 5) */ |
| /* 6.5.10 1) - 5) */ |
| |
| final int[] exFlags = getToExportFlags(); |
| |
| /* 6.5.10 6) - 8) */ |
| setExportedSymbols(exFlags); |
| } |
| |
| if (JBIG2ImageReader.PERFORMANCE_TEST) |
| log.info("SYMBOL DECODING: " + (System.currentTimeMillis() - timestamp) + " ms"); |
| |
| // DictionaryViewer.viewSymbols(sdExSyms); |
| |
| return exportSymbols; |
| } |
| |
| private void setCodingStatistics() throws IOException |
| { |
| if (cxIADT == null) |
| { |
| cxIADT = new CX(512, 1); |
| } |
| |
| if (cxIADH == null) |
| { |
| cxIADH = new CX(512, 1); |
| } |
| |
| if (cxIADW == null) |
| { |
| cxIADW = new CX(512, 1); |
| } |
| |
| if (cxIAAI == null) |
| { |
| cxIAAI = new CX(512, 1); |
| } |
| |
| if (cxIAEX == null) |
| { |
| cxIAEX = new CX(512, 1); |
| } |
| |
| if (useRefinementAggregation && cxIAID == null) |
| { |
| cxIAID = new CX(1 << sbSymCodeLen, 1); |
| cxIARDX = new CX(512, 1); |
| cxIARDY = new CX(512, 1); |
| } |
| |
| if (cx == null) |
| { |
| cx = new CX(65536, 1); |
| } |
| |
| if (arithmeticDecoder == null) |
| { |
| arithmeticDecoder = new ArithmeticDecoder(subInputStream); |
| } |
| |
| if (iDecoder == null) |
| { |
| iDecoder = new ArithmeticIntegerDecoder(arithmeticDecoder); |
| } |
| |
| } |
| |
| private final void decodeHeightClassBitmap(final Bitmap heightClassCollectiveBitmap, |
| final int heightClassFirstSymbol, final int heightClassHeight, |
| final int[] newSymbolsWidths) |
| throws IntegerMaxValueException, InvalidHeaderValueException, IOException |
| { |
| |
| for (int i = heightClassFirstSymbol; i < amountOfDecodedSymbols; i++) |
| { |
| int startColumn = 0; |
| |
| for (int j = heightClassFirstSymbol; j <= i - 1; j++) |
| { |
| startColumn += newSymbolsWidths[j]; |
| } |
| |
| final Rectangle roi = new Rectangle(startColumn, 0, newSymbolsWidths[i], |
| heightClassHeight); |
| final Bitmap symbolBitmap = Bitmaps.extract(roi, heightClassCollectiveBitmap); |
| newSymbols[i] = symbolBitmap; |
| sbSymbols.add(symbolBitmap); |
| } |
| } |
| |
| private final void decodeAggregate(final int symbolWidth, final int heightClassHeight) |
| throws IOException, InvalidHeaderValueException, IntegerMaxValueException |
| { |
| // 6.5.8.2 1) |
| // 6.5.8.2.1 - Number of symbol instances in aggregation |
| final long amountOfRefinementAggregationInstances; |
| if (isHuffmanEncoded) |
| { |
| log.info( |
| "Refinement or aggregate-coded symbols may couse problems with huffman decoding!"); |
| amountOfRefinementAggregationInstances = huffDecodeRefAggNInst(); |
| } |
| else |
| { |
| amountOfRefinementAggregationInstances = iDecoder.decode(cxIAAI); |
| } |
| |
| if (amountOfRefinementAggregationInstances > 1) |
| { |
| // 6.5.8.2 2) |
| decodeThroughTextRegion(symbolWidth, heightClassHeight, |
| amountOfRefinementAggregationInstances); |
| } |
| else if (amountOfRefinementAggregationInstances == 1) |
| { |
| // 6.5.8.2 3) refers to 6.5.8.2.2 |
| decodeRefinedSymbol(symbolWidth, heightClassHeight); |
| } |
| } |
| |
| private final long huffDecodeRefAggNInst() throws IOException, InvalidHeaderValueException |
| { |
| if (sdHuffAggInstanceSelection == 0) |
| { |
| return StandardTables.getTable(1).decode(subInputStream); |
| } |
| else if (sdHuffAggInstanceSelection == 1) |
| { |
| if (aggInstTable == null) |
| { |
| int aggregationInstanceNumber = 0; |
| |
| if (sdHuffDecodeHeightSelection == 3) |
| { |
| aggregationInstanceNumber++; |
| } |
| if (sdHuffDecodeWidthSelection == 3) |
| { |
| aggregationInstanceNumber++; |
| } |
| if (sdHuffBMSizeSelection == 3) |
| { |
| aggregationInstanceNumber++; |
| } |
| |
| aggInstTable = getUserTable(aggregationInstanceNumber); |
| } |
| return aggInstTable.decode(subInputStream); |
| } |
| return 0; |
| } |
| |
| private final void decodeThroughTextRegion(final int symbolWidth, final int heightClassHeight, |
| final long amountOfRefinementAggregationInstances) |
| throws IOException, IntegerMaxValueException, InvalidHeaderValueException |
| { |
| if (textRegion == null) |
| { |
| textRegion = new TextRegion(subInputStream, null); |
| |
| textRegion.setContexts(cx, // default context |
| new CX(512, 1), // IADT |
| new CX(512, 1), // IAFS |
| new CX(512, 1), // IADS |
| new CX(512, 1), // IAIT |
| cxIAID, // IAID |
| new CX(512, 1), // IARDW |
| new CX(512, 1), // IARDH |
| new CX(512, 1), // IARDX |
| new CX(512, 1) // IARDY |
| ); |
| } |
| |
| // 6.5.8.2.4 Concatenating the array used as parameter later. |
| setSymbolsArray(); |
| |
| // 6.5.8.2 2) Parameters set according to Table 17, page 36 |
| textRegion.setParameters(arithmeticDecoder, iDecoder, isHuffmanEncoded, true, symbolWidth, |
| heightClassHeight, amountOfRefinementAggregationInstances, 1, |
| (amountOfImportedSymbolss + amountOfDecodedSymbols), (short) 0, (short) 0, |
| (short) 0, (short) 1, (short) 0, (short) 0, (short) 0, (short) 0, (short) 0, |
| (short) 0, (short) 0, (short) 0, (short) 0, sdrTemplate, sdrATX, sdrATY, sbSymbols, |
| sbSymCodeLen); |
| |
| addSymbol(textRegion); |
| } |
| |
| private final void decodeRefinedSymbol(final int symbolWidth, final int heightClassHeight) |
| throws IOException, InvalidHeaderValueException, IntegerMaxValueException |
| { |
| |
| final int id; |
| final int rdx; |
| final int rdy; |
| // long symInRefSize = 0; |
| if (isHuffmanEncoded) |
| { |
| /* 2) - 4) */ |
| id = (int) subInputStream.readBits(sbSymCodeLen); |
| rdx = (int) StandardTables.getTable(15).decode(subInputStream); |
| rdy = (int) StandardTables.getTable(15).decode(subInputStream); |
| |
| /* 5) a) */ |
| /* symInRefSize = */StandardTables.getTable(1).decode(subInputStream); |
| |
| /* 5) b) - Skip over remaining bits */ |
| subInputStream.skipBits(); |
| } |
| else |
| { |
| /* 2) - 4) */ |
| id = iDecoder.decodeIAID(cxIAID, sbSymCodeLen); |
| rdx = (int) iDecoder.decode(cxIARDX); |
| rdy = (int) iDecoder.decode(cxIARDY); |
| } |
| |
| /* 6) */ |
| setSymbolsArray(); |
| final Bitmap ibo = sbSymbols.get(id); |
| decodeNewSymbols(symbolWidth, heightClassHeight, ibo, rdx, rdy); |
| |
| /* 7) */ |
| if (isHuffmanEncoded) |
| { |
| subInputStream.skipBits(); |
| // Make sure that the processed bytes are equal to the value read in step 5 a) |
| } |
| } |
| |
| private final void decodeNewSymbols(final int symWidth, final int hcHeight, final Bitmap ibo, |
| final int rdx, final int rdy) |
| throws IOException, InvalidHeaderValueException, IntegerMaxValueException |
| { |
| if (genericRefinementRegion == null) |
| { |
| genericRefinementRegion = new GenericRefinementRegion(subInputStream); |
| |
| if (arithmeticDecoder == null) |
| { |
| arithmeticDecoder = new ArithmeticDecoder(subInputStream); |
| } |
| |
| if (cx == null) |
| { |
| cx = new CX(65536, 1); |
| } |
| } |
| |
| // Parameters as shown in Table 18, page 36 |
| genericRefinementRegion.setParameters(cx, arithmeticDecoder, sdrTemplate, symWidth, |
| hcHeight, ibo, rdx, rdy, false, sdrATX, sdrATY); |
| |
| addSymbol(genericRefinementRegion); |
| } |
| |
| private final void decodeDirectlyThroughGenericRegion(final int symWidth, final int hcHeight) |
| throws IOException, IntegerMaxValueException, InvalidHeaderValueException |
| { |
| if (genericRegion == null) |
| { |
| genericRegion = new GenericRegion(subInputStream); |
| } |
| |
| // Parameters set according to Table 16, page 35 |
| genericRegion.setParameters(false, sdTemplate, false, false, sdATX, sdATY, symWidth, |
| hcHeight, cx, arithmeticDecoder); |
| |
| addSymbol(genericRegion); |
| } |
| |
| private final void addSymbol(final Region region) |
| throws IntegerMaxValueException, InvalidHeaderValueException, IOException |
| { |
| final Bitmap symbol = region.getRegionBitmap(); |
| newSymbols[amountOfDecodedSymbols] = symbol; |
| sbSymbols.add(symbol); |
| } |
| |
| private final long decodeDifferenceWidth() throws IOException, InvalidHeaderValueException |
| { |
| if (isHuffmanEncoded) |
| { |
| switch (sdHuffDecodeWidthSelection) |
| { |
| case 0: |
| return StandardTables.getTable(2).decode(subInputStream); |
| case 1: |
| return StandardTables.getTable(3).decode(subInputStream); |
| case 3: |
| if (dwTable == null) |
| { |
| int dwNr = 0; |
| |
| if (sdHuffDecodeHeightSelection == 3) |
| { |
| dwNr++; |
| } |
| dwTable = getUserTable(dwNr); |
| } |
| |
| return dwTable.decode(subInputStream); |
| } |
| } |
| else |
| { |
| return iDecoder.decode(cxIADW); |
| } |
| return 0; |
| } |
| |
| private final long decodeHeightClassDeltaHeight() |
| throws IOException, InvalidHeaderValueException |
| { |
| if (isHuffmanEncoded) |
| { |
| return decodeHeightClassDeltaHeightWithHuffman(); |
| } |
| else |
| { |
| return iDecoder.decode(cxIADH); |
| } |
| } |
| |
| /** |
| * 6.5.6 if isHuffmanEncoded |
| * |
| * @return long - Result of decoding HCDH |
| * @throws IOException |
| * @throws InvalidHeaderValueException |
| */ |
| private final long decodeHeightClassDeltaHeightWithHuffman() |
| throws IOException, InvalidHeaderValueException |
| { |
| switch (sdHuffDecodeHeightSelection) |
| { |
| case 0: |
| return StandardTables.getTable(4).decode(subInputStream); |
| case 1: |
| return StandardTables.getTable(5).decode(subInputStream); |
| case 3: |
| if (dhTable == null) |
| { |
| dhTable = getUserTable(0); |
| } |
| return dhTable.decode(subInputStream); |
| } |
| |
| return 0; |
| } |
| |
| private final Bitmap decodeHeightClassCollectiveBitmap(final long bmSize, |
| final int heightClassHeight, final int totalWidth) throws IOException |
| { |
| if (bmSize == 0) |
| { |
| final Bitmap heightClassCollectiveBitmap = new Bitmap(totalWidth, heightClassHeight); |
| |
| for (int i = 0; i < heightClassCollectiveBitmap.getByteArray().length; i++) |
| { |
| heightClassCollectiveBitmap.setByte(i, subInputStream.readByte()); |
| } |
| |
| return heightClassCollectiveBitmap; |
| } |
| else |
| { |
| if (genericRegion == null) |
| { |
| genericRegion = new GenericRegion(subInputStream); |
| } |
| |
| genericRegion.setParameters(true, subInputStream.getStreamPosition(), bmSize, |
| heightClassHeight, totalWidth); |
| |
| return genericRegion.getRegionBitmap(); |
| } |
| } |
| |
| private void setExportedSymbols(final int[] toExportFlags) |
| { |
| exportSymbols = new ArrayList<Bitmap>(amountOfExportSymbolss); |
| |
| for (int i = 0; i < amountOfImportedSymbolss + amountOfNewSymbols; i++) |
| { |
| |
| if (toExportFlags[i] == 1) |
| { |
| if (i < amountOfImportedSymbolss) |
| { |
| exportSymbols.add(importSymbols.get(i)); |
| } |
| else |
| { |
| exportSymbols.add(newSymbols[i - amountOfImportedSymbolss]); |
| } |
| } |
| } |
| } |
| |
| private int[] getToExportFlags() throws IOException, InvalidHeaderValueException |
| { |
| int currentExportFlag = 0; |
| long exRunLength = 0; |
| final int[] exportFlags = new int[amountOfImportedSymbolss + amountOfNewSymbols]; |
| |
| for (int exportIndex = 0; exportIndex < amountOfImportedSymbolss |
| + amountOfNewSymbols; exportIndex += exRunLength) |
| { |
| |
| if (isHuffmanEncoded) |
| { |
| exRunLength = StandardTables.getTable(1).decode(subInputStream); |
| } |
| else |
| { |
| exRunLength = iDecoder.decode(cxIAEX); |
| } |
| |
| if (exRunLength != 0) |
| { |
| for (int index = exportIndex; index < exportIndex + exRunLength; index++) |
| { |
| exportFlags[index] = currentExportFlag; |
| } |
| } |
| |
| currentExportFlag = (currentExportFlag == 0) ? 1 : 0; |
| } |
| |
| return exportFlags; |
| } |
| |
| private final long huffDecodeBmSize() throws IOException, InvalidHeaderValueException |
| { |
| if (bmSizeTable == null) |
| { |
| int bmNr = 0; |
| |
| if (sdHuffDecodeHeightSelection == 3) |
| { |
| bmNr++; |
| } |
| |
| if (sdHuffDecodeWidthSelection == 3) |
| { |
| bmNr++; |
| } |
| |
| bmSizeTable = getUserTable(bmNr); |
| } |
| return bmSizeTable.decode(subInputStream); |
| } |
| |
| /** |
| * 6.5.8.2.3 - Setting SBSYMCODES and SBSYMCODELEN |
| * |
| * @return Result of computing SBSYMCODELEN |
| * @throws IOException |
| */ |
| private int getSbSymCodeLen() throws IOException |
| { |
| if (isHuffmanEncoded) |
| { |
| return Math.max( |
| (int) (Math.ceil( |
| Math.log(amountOfImportedSymbolss + amountOfNewSymbols) / Math.log(2))), |
| 1); |
| } |
| else |
| { |
| return (int) (Math |
| .ceil(Math.log(amountOfImportedSymbolss + amountOfNewSymbols) / Math.log(2))); |
| } |
| } |
| |
| /** |
| * 6.5.8.2.4 - Setting SBSYMS |
| * |
| * @throws IOException |
| * @throws InvalidHeaderValueException |
| * @throws IntegerMaxValueException |
| */ |
| private final void setSymbolsArray() |
| throws IOException, InvalidHeaderValueException, IntegerMaxValueException |
| { |
| if (importSymbols == null) |
| { |
| retrieveImportSymbols(); |
| } |
| |
| if (sbSymbols == null) |
| { |
| sbSymbols = new ArrayList<Bitmap>(); |
| sbSymbols.addAll(importSymbols); |
| } |
| } |
| |
| /** |
| * Concatenates symbols from all referred-to segments. |
| * |
| * @throws IOException |
| * @throws InvalidHeaderValueException |
| * @throws IntegerMaxValueException |
| */ |
| private void retrieveImportSymbols() |
| throws IOException, InvalidHeaderValueException, IntegerMaxValueException |
| { |
| importSymbols = new ArrayList<Bitmap>(); |
| for (final SegmentHeader referredToSegmentHeader : segmentHeader.getRtSegments()) |
| { |
| if (referredToSegmentHeader.getSegmentType() == 0) |
| { |
| final SymbolDictionary sd = (SymbolDictionary) referredToSegmentHeader |
| .getSegmentData(); |
| importSymbols.addAll(sd.getDictionary()); |
| amountOfImportedSymbolss += sd.amountOfExportSymbolss; |
| } |
| } |
| } |
| |
| private HuffmanTable getUserTable(final int tablePosition) |
| throws InvalidHeaderValueException, IOException |
| { |
| int tableCounter = 0; |
| |
| for (final SegmentHeader referredToSegmentHeader : segmentHeader.getRtSegments()) |
| { |
| if (referredToSegmentHeader.getSegmentType() == 53) |
| { |
| if (tableCounter == tablePosition) |
| { |
| final Table t = (Table) referredToSegmentHeader.getSegmentData(); |
| return new EncodedTable(t); |
| } |
| else |
| { |
| tableCounter++; |
| } |
| } |
| } |
| return null; |
| } |
| |
| public void init(final SegmentHeader header, final SubInputStream sis) |
| throws InvalidHeaderValueException, IntegerMaxValueException, IOException |
| { |
| this.subInputStream = sis; |
| this.segmentHeader = header; |
| parseHeader(); |
| } |
| } |