All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.levigo.jbig2.segments.SymbolDictionary Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 1995-2015 levigo holding gmbh.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

package com.levigo.jbig2.segments;

import java.awt.Rectangle;
import java.io.IOException;
import java.util.ArrayList;

import com.levigo.jbig2.Bitmap;
import com.levigo.jbig2.Dictionary;
import com.levigo.jbig2.JBIG2ImageReader;
import com.levigo.jbig2.Region;
import com.levigo.jbig2.SegmentHeader;
import com.levigo.jbig2.decoder.arithmetic.ArithmeticDecoder;
import com.levigo.jbig2.decoder.arithmetic.ArithmeticIntegerDecoder;
import com.levigo.jbig2.decoder.arithmetic.CX;
import com.levigo.jbig2.decoder.huffman.EncodedTable;
import com.levigo.jbig2.decoder.huffman.HuffmanTable;
import com.levigo.jbig2.decoder.huffman.StandardTables;
import com.levigo.jbig2.err.IntegerMaxValueException;
import com.levigo.jbig2.err.InvalidHeaderValueException;
import com.levigo.jbig2.image.Bitmaps;
import com.levigo.jbig2.io.SubInputStream;
import com.levigo.jbig2.util.log.Logger;
import com.levigo.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 amountOfNewSymbolss;

  /** Further parameters */
  private SegmentHeader segmentHeader;
  private int amountOfImportedSymbolss;
  private ArrayList 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 exportSymbols;
  private ArrayList 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(SubInputStream subInputStream, 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 {
    amountOfNewSymbolss = (int) subInputStream.readBits(32); // & 0xffffffff;
  }

  private void setInSyms() throws IOException, InvalidHeaderValueException, IntegerMaxValueException {
    if (segmentHeader.getRtSegments() != null) {
      retrieveImportSymbols();
    } else {
      importSymbols = new ArrayList();
    }
  }

  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 ArrayList
   */
  public ArrayList 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[amountOfNewSymbolss];

      /* 6.5.5 2) */
      int[] newSymbolsWidths = null;
      if (isHuffmanEncoded && !useRefinementAggregation) {
        newSymbolsWidths = new int[amountOfNewSymbolss];
      }

      setSymbolsArray();

      /* 6.5.5 3) */
      int heightClassHeight = 0;
      amountOfDecodedSymbols = 0;

      /* 6.5.5 4 a) */
      while (amountOfDecodedSymbols != amountOfNewSymbolss) {

        /* 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)
          if (differenceWidth == Long.MAX_VALUE) {
            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(int[] toExportFlags) {
    exportSymbols = new ArrayList(amountOfExportSymbolss);

    for (int i = 0; i < amountOfImportedSymbolss + amountOfNewSymbolss; 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 + amountOfNewSymbolss];

    for (int exportIndex = 0; exportIndex < amountOfImportedSymbolss + amountOfNewSymbolss; 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 + amountOfNewSymbolss) / Math.log(2))), 1);
    } else {
      return (int) (Math.ceil(Math.log(amountOfImportedSymbolss + amountOfNewSymbolss) / 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();
      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();
    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();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy