blob: a4261e9c7f20149ad93c035c1b7bdf09ca5ee12d [file] [log] [blame]
/**
* 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.SegmentHeader;
import org.apache.pdfbox.jbig2.err.InvalidHeaderValueException;
import org.apache.pdfbox.jbig2.image.Bitmaps;
import org.apache.pdfbox.jbig2.io.SubInputStream;
/**
* This class represents the segment type "Pattern dictionary", 7.4.4.
*/
public class PatternDictionary implements Dictionary
{
private SubInputStream subInputStream;
/** Segment data structure (only necessary if MMR is used) */
private long dataHeaderOffset;
private long dataHeaderLength;
private long dataOffset;
private long dataLength;
private short[] gbAtX = null;
private short[] gbAtY = null;
/** Pattern dictionary flags, 7.4.4.1.1 */
private boolean isMMREncoded;
private byte hdTemplate;
/** Width of the patterns in the pattern dictionary, 7.4.4.1.2 */
private short hdpWidth;
/** Height of the patterns in the pattern dictionary, 7.4.4.1.3 */
private short hdpHeight;
/** Decoded bitmaps, stored to be used by segments, that refer to it */
private ArrayList<Bitmap> patterns;
/**
* Largest gray-scale value, 7.4.4.1.4
*
* Value: one less than the number of patterns defined in this pattern dictionary
*/
private int grayMax;
private void parseHeader() throws IOException, InvalidHeaderValueException
{
/* Bit 3-7 */
subInputStream.readBits(5); // Dirty read ...
/* Bit 1-2 */
readTemplate();
/* Bit 0 */
readIsMMREncoded();
readPatternWidthAndHeight();
readGrayMax();
/* Segment data structure */
computeSegmentDataStructure();
this.checkInput();
}
private void readTemplate() throws IOException
{
/* Bit 1-2 */
hdTemplate = (byte) subInputStream.readBits(2);
}
private void readIsMMREncoded() throws IOException
{
/* Bit 0 */
if (subInputStream.readBit() == 1)
{
isMMREncoded = true;
}
}
private void readPatternWidthAndHeight() throws IOException
{
hdpWidth = subInputStream.readByte();
hdpHeight = subInputStream.readByte();
}
private void readGrayMax() throws IOException
{
grayMax = (int) (subInputStream.readBits(32) & 0xffffffff);
}
private void computeSegmentDataStructure() throws IOException
{
dataOffset = subInputStream.getStreamPosition();
dataHeaderLength = dataOffset - dataHeaderOffset;
dataLength = subInputStream.length() - dataHeaderLength;
}
private void checkInput() throws InvalidHeaderValueException
{
if (hdpHeight < 1 || hdpWidth < 1)
{
throw new InvalidHeaderValueException("Width/Heigth must be greater than zero.");
}
}
/**
* This method decodes a pattern dictionary segment and returns an array of {@link Bitmap} s. Each of this
* {@link Bitmap}s is a pattern.<br>
* The procedure is described in 6.7.5 (page 43).
*
* @return An array of {@link Bitmap}s as result of the decoding procedure.
*/
public ArrayList<Bitmap> getDictionary() throws IOException, InvalidHeaderValueException
{
if (null == patterns)
{
if (!isMMREncoded)
{
setGbAtPixels();
}
// 2)
final GenericRegion genericRegion = new GenericRegion(subInputStream);
genericRegion.setParameters(isMMREncoded, dataOffset, dataLength, hdpHeight,
(grayMax + 1) * hdpWidth, hdTemplate, false, false, gbAtX, gbAtY);
final Bitmap collectiveBitmap = genericRegion.getRegionBitmap();
// 4)
extractPatterns(collectiveBitmap);
}
return patterns;
}
private void extractPatterns(Bitmap collectiveBitmap)
{
// 3)
int gray = 0;
patterns = new ArrayList<Bitmap>(grayMax + 1);
// 4)
while (gray <= grayMax)
{
// 4) a) Retrieve a pattern bitmap by extracting it out of the collective bitmap
final Rectangle roi = new Rectangle(hdpWidth * gray, 0, hdpWidth, hdpHeight);
final Bitmap patternBitmap = Bitmaps.extract(roi, collectiveBitmap);
patterns.add(patternBitmap);
// 4) b)
gray++;
}
}
private void setGbAtPixels()
{
if (hdTemplate == 0)
{
gbAtX = new short[4];
gbAtY = new short[4];
gbAtX[0] = (short) -hdpWidth;
gbAtY[0] = 0;
gbAtX[1] = -3;
gbAtY[1] = -1;
gbAtX[2] = 2;
gbAtY[2] = -2;
gbAtX[3] = -2;
gbAtY[3] = -2;
}
else
{
gbAtX = new short[1];
gbAtY = new short[1];
gbAtX[0] = (short) -hdpWidth;
gbAtY[0] = 0;
}
}
public void init(SegmentHeader header, SubInputStream sis)
throws InvalidHeaderValueException, IOException
{
this.subInputStream = sis;
parseHeader();
}
protected boolean isMMREncoded()
{
return isMMREncoded;
}
protected byte getHdTemplate()
{
return hdTemplate;
}
protected short getHdpWidth()
{
return hdpWidth;
}
protected short getHdpHeight()
{
return hdpHeight;
}
protected int getGrayMax()
{
return grayMax;
}
}