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

com.levigo.jbig2.JBIG2Document 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;

import java.io.EOFException;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.imageio.stream.ImageInputStream;

import com.levigo.jbig2.io.SubInputStream;
import com.levigo.jbig2.util.log.Logger;
import com.levigo.jbig2.util.log.LoggerFactory;

/**
 * This class represents the document structure with its pages and global segments.
 */
class JBIG2Document {
  private static final Logger log = LoggerFactory.getLogger(JBIG2Document.class);

  /** ID string in file header, see ISO/IEC 14492:2001, D.4.1 */
  private int[] FILE_HEADER_ID = {
      0x97, 0x4A, 0x42, 0x32, 0x0D, 0x0A, 0x1A, 0x0A
  };

  /**
   * This map contains all pages of this document. The key is the number of the page.
   */
  private final Map pages = new TreeMap();


  /** BASIC INFORMATION ABOUT THE CURRENT JBIG2 DOCUMENT */

  /** The length of the file header if exists */
  private short fileHeaderLength = 9;

  /**
   * According to D.4.2 - File header bit 0
   * 

* This flag contains information about the file organization: *

    *
  • {@code 1} for sequential
  • *
  • {@code 0} for random-access
  • *
* You can use the constants {@link #RANDOM} and {@link JBIG2Document#SEQUENTIAL}. */ private short organisationType = SEQUENTIAL; public static final int RANDOM = 0; public static final int SEQUENTIAL = 1; /** * According to D.4.2 - Bit 1
*
    *
  • {@code true} if amount of pages is unknown, amount of pages field is not present
  • *
  • {@code false} if there is a field in the file header where the amount of pages can be read
  • *
*/ private boolean amountOfPagesUnknown = true; /** * According to D.4.3 - Number of pages field (4 bytes). Only present if * {@link #amountOfPagesUnknown} is {@code false}. */ private int amountOfPages; /** Defines whether extended Template is used. */ private boolean gbUseExtTemplate; /** * This is the source data stream wrapped into a {@link SubInputStream}. */ private final SubInputStream subInputStream; /** * Flag: *
    *
  • {@code true} if stream is embedded in another file format and the file header is missing
  • *
  • {@code false} if stream is created of a native jbig2 file and the file header should be * present
  • *
*/ /** * Holds a load of segments, that aren't associated with a page. */ private JBIG2Globals globalSegments; protected JBIG2Document(ImageInputStream input) throws IOException { this(input, null); } protected JBIG2Document(ImageInputStream input, JBIG2Globals globals) throws IOException { if (input == null) throw new IllegalArgumentException("imageInputStream must not be null"); this.subInputStream = new SubInputStream(input, 0, Long.MAX_VALUE); this.globalSegments = globals; mapStream(); } /** * Retrieves the segment with the given segment number considering only segments that aren't * associated with a page. * * @param segmentNr - The number of the wanted segment. * @return The requested {@link SegmentHeader}. */ SegmentHeader getGlobalSegment(int segmentNr) { if (null != globalSegments) { return globalSegments.getSegment(segmentNr); } if (log.isErrorEnabled()) { log.error("Segment not found. Returning null."); } return null; } /** * Retrieves a {@link JBIG2Page} specified by the given page number. * * @param pageNumber - The page number of the wanted {@link JBIG2Page}. * * @return The requested {@link JBIG2Page}. */ protected JBIG2Page getPage(int pageNumber) { return pages.get(pageNumber); } /** * Retrieves the amount of pages in this JBIG2 document. If the pages are striped, the document * will be completely parsed and the amount of pages will be gathered. * * @return The amount of pages in this JBIG2 document. * @throws IOException */ protected int getAmountOfPages() throws IOException { if (amountOfPagesUnknown || amountOfPages == 0) { if (pages.size() == 0) { mapStream(); } return pages.size(); } else { return amountOfPages; } } /** * This method maps the stream and stores all segments. */ private void mapStream() throws IOException { final List segments = new LinkedList(); long offset = 0; int segmentType = 0; /* * Parse the file header if there is one. */ if (isFileHeaderPresent()) { parseFileHeader(); offset += fileHeaderLength; } if (globalSegments == null) { globalSegments = new JBIG2Globals(); } JBIG2Page page = null; /* * If organisation type is random-access: walk through the segment headers until EOF segment * appears (specified with segment number 51) */ while (segmentType != 51 && !reachedEndOfStream(offset)) { SegmentHeader segment = new SegmentHeader(this, subInputStream, offset, organisationType); final int associatedPage = segment.getPageAssociation(); segmentType = segment.getSegmentType(); if (associatedPage != 0) { page = getPage(associatedPage); if (page == null) { page = new JBIG2Page(this, associatedPage); pages.put(associatedPage, page); } page.add(segment); } else { globalSegments.addSegment(segment.getSegmentNr(), segment); } segments.add(segment); if (JBIG2ImageReader.DEBUG) { if (log.isDebugEnabled()) { log.debug(segment.toString()); } } offset = subInputStream.getStreamPosition(); // Sequential organization skips data part and sets the offset if (organisationType == SEQUENTIAL) { offset += segment.getSegmentDataLength(); } } /* * Random organization: segment headers are finished. Data part starts and the offset can be * set. */ determineRandomDataOffsets(segments, offset); } private boolean isFileHeaderPresent() throws IOException { final SubInputStream input = subInputStream; input.mark(); for (int magicByte : FILE_HEADER_ID) { if (magicByte != input.read()) { input.reset(); return false; } } input.reset(); return true; } /** * Determines the start of the data parts and sets the offset. * * @param segments * @param offset */ private void determineRandomDataOffsets(List segments, long offset) { if (organisationType == RANDOM) { for (SegmentHeader s : segments) { s.setSegmentDataStartOffset(offset); offset += s.getSegmentDataLength(); } } } /** * This method reads the stream and sets variables for information about organization type and * length etc. * * @return * @throws IOException */ private void parseFileHeader() throws IOException { subInputStream.seek(0); /* D.4.1 - ID string, read will be skipped */ subInputStream.skipBytes(8); /* * D.4.2 Header flag (1 byte) */ // Bit 3-7 are reserved and must be 0 subInputStream.readBits(5); // Bit 2 - Indicates if extended templates are used if (subInputStream.readBit() == 1) { gbUseExtTemplate = true; } // Bit 1 - Indicates if amount of pages are unknown if (subInputStream.readBit() != 1) { amountOfPagesUnknown = false; } // Bit 0 - Indicates file organisation type organisationType = (short) subInputStream.readBit(); // fileHeaderLength = 9; /* * D.4.3 Number of pages (field is only present if amount of pages are 'NOT unknown') */ if (!amountOfPagesUnknown) { amountOfPages = (int) subInputStream.readUnsignedInt(); fileHeaderLength = 13; } } /** * This method checks, if the stream is at its end to avoid {@link EOFException}s and reads 32 * bits. * * @param offset * @return
  • {@code true} if end of stream reached
  • {@code false} if there are more bytes to * read * @throws IOException */ private boolean reachedEndOfStream(long offset) throws IOException { try { subInputStream.seek(offset); subInputStream.readBits(32); return false; } catch (EOFException e) { return true; } catch (IndexOutOfBoundsException e) { return true; } } protected JBIG2Globals getGlobalSegments() { return globalSegments; } protected boolean isAmountOfPagesUnknown() { return amountOfPagesUnknown; } boolean isGbUseExtTemplate() { return gbUseExtTemplate; } }




  • © 2015 - 2024 Weber Informatics LLC | Privacy Policy