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

org.jpedal.jbig2.jai.JBIG2ImageReader Maven / Gradle / Ivy

/**
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 * 

* Project Info: http://www.idrsolutions.com * Help section for developers at http://www.idrsolutions.com/java-pdf-library-support/ *

* (C) Copyright 1997-2008, IDRsolutions and Contributors. * Main Developer: Simon Barnett *

* This file is part of JPedal *

* Copyright (c) 2008, IDRsolutions * All rights reserved. *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the IDRsolutions nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. *

* THIS SOFTWARE IS PROVIDED BY IDRsolutions ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL IDRsolutions BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *

* Other JBIG2 image decoding implementations include * jbig2dec (http://jbig2dec.sourceforge.net/) * xpdf (http://www.foolabs.com/xpdf/) *

* The final draft JBIG2 specification can be found at http://www.jpeg.org/public/fcd14492.pdf *

* All three of the above resources were used in the writing of this software, with methodologies, * processes and inspiration taken from all three. *

* --------------- * JBIG2ImageReader.java * --------------- */ package org.jpedal.jbig2.jai; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.imageio.ImageReadParam; import javax.imageio.ImageReader; import javax.imageio.ImageTypeSpecifier; import javax.imageio.metadata.IIOMetadata; import javax.imageio.spi.ImageReaderSpi; import javax.imageio.stream.ImageInputStream; import org.jpedal.jbig2.JBIG2Decoder; import org.jpedal.jbig2.JBIG2Exception; import org.jpedal.jbig2.image.JBIG2Bitmap; public class JBIG2ImageReader extends ImageReader { private JBIG2Decoder decoder; private ImageInputStream stream; private boolean readFile; protected JBIG2ImageReader(ImageReaderSpi originatingProvider) { // Save the identity of the ImageReaderSpi subclass that invoked this // constructor. super(originatingProvider); } public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) { super.setInput(input, seekForwardOnly, ignoreMetadata); if (input == null) { this.stream = null; return; } // The input source must be an ImageInputStream because the originating // provider -- the JBIG2ImageReaderSpi class -- passes // STANDARD_INPUT_TYPE // -- an array consisting only of ImageInputStream -- to its superclass // in its constructor call. if (input instanceof ImageInputStream) this.stream = (ImageInputStream) input; else throw new IllegalArgumentException("ImageInputStream expected!"); } public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException { BufferedImage dst = null; try { // Calculate and return a Rectangle that identifies the region of // the // source image that should be read: // // 1. If param is null, the upper-left corner of the region is (0, // 0), // and the width and height are specified by the width and height // arguments. In other words, the entire image is read. // // 2. If param is not null // // 2.1 If param.getSourceRegion() returns a non-null Rectangle, the // region is calculated as the intersection of param's Rectangle // and the earlier (0, 0, width, height Rectangle). // // 2.2 param.getSubsamplingXOffset() is added to the region's x // coordinate and subtracted from its width. // // 2.3 param.getSubsamplingYOffset() is added to the region's y // coordinate and subtracted from its height. int width = getWidth(imageIndex); int height = getHeight(imageIndex); Rectangle sourceRegion = getSourceRegion(param, width, height); // Source subsampling is used to return a scaled-down source image. // Default 1 values for X and Y subsampling indicate that a // non-scaled // source image will be returned. int sourceXSubsampling = 1; int sourceYSubsampling = 1; // The destination offset determines the starting location in the // destination where decoded pixels are placed. Default (0, 0) // values indicate the upper-left corner. Point destinationOffset = new Point(0, 0); // If param is not null, override the source subsampling, and // destination offset defaults. if (param != null) { sourceXSubsampling = param.getSourceXSubsampling(); sourceYSubsampling = param.getSourceYSubsampling(); destinationOffset = param.getDestinationOffset(); } // Obtain a BufferedImage into which decoded pixels will be placed. // This destination will be returned to the application. // // 1. If param is not null // // 1.1 If param.getDestination() returns a BufferedImage // // 1.1.1 Return this BufferedImage // // Else // // 1.1.2 Invoke param.getDestinationType (). // // 1.1.3 If the returned ImageTypeSpecifier equals // getImageTypes (0) (see below), return its BufferedImage. // // 2. If param is null or a BufferedImage has not been obtained // // 2.1 Return getImageTypes (0)'s BufferedImage. dst = getDestination(param, getImageTypes(0), width, height); // Create a WritableRaster for the destination. WritableRaster wrDst = dst.getRaster(); JBIG2Bitmap bitmap = decoder.getPageAsJBIG2Bitmap(imageIndex).getSlice(sourceRegion.x, sourceRegion.y, sourceRegion.width, sourceRegion.height); BufferedImage image = bitmap.getBufferedImage(); int newWidth = (int) (image.getWidth() * (1 / (double) sourceXSubsampling)); int newHeight = (int) (image.getHeight() * (1 / (double) sourceYSubsampling)); BufferedImage scaledImage = scaleImage(image.getRaster(), newWidth, newHeight, 1, 1); Raster raster = null; if (scaledImage != null) { raster = scaledImage.getRaster(); } else raster = image.getRaster(); wrDst.setRect(destinationOffset.x, destinationOffset.y, raster); } catch (RuntimeException e) { e.printStackTrace(); } return dst; } public IIOMetadata getImageMetadata(int imageIndex) throws IOException { return null; } public IIOMetadata getStreamMetadata() throws IOException { return null; } public Iterator getImageTypes(int imageIndex) throws IOException { readFile(); checkIndex(imageIndex); // Create a List of ImageTypeSpecifiers that identify the possible image // types to which the single JBIG2 image can be decoded. An // ImageTypeSpecifier is used with ImageReader's getDestination() method // to return an appropriate BufferedImage that contains the decoded // image, and is accessed by an application. List l = new ArrayList(); // The JBIG2 reader only uses a single List entry. This entry describes // a // BufferedImage of TYPE_INT_RGB, which is a commonly used image type. l.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY)); // Return an iterator that retrieves elements from the list. return l.iterator(); } public int getNumImages(boolean allowSearch) throws IOException { readFile(); return decoder.getNumberOfPages(); } public int getHeight(int imageIndex) throws IOException { readFile(); checkIndex(imageIndex); return decoder.getPageAsJBIG2Bitmap(imageIndex).getHeight(); } public int getWidth(int imageIndex) throws IOException { readFile(); checkIndex(imageIndex); return decoder.getPageAsJBIG2Bitmap(imageIndex).getWidth(); } private void checkIndex(int imageIndex) { int noOfPages = decoder.getNumberOfPages(); if (imageIndex < 0 || imageIndex > noOfPages) throw new IndexOutOfBoundsException("Bad index!"); } private static BufferedImage scaleImage(Raster ras, int pX, int pY, int comp, int d) { int w = ras.getWidth(); int h = ras.getHeight(); byte[] data = ((DataBufferByte) ras.getDataBuffer()).getData(); // see what we could reduce to and still be big enough for page int newW = w, newH = h; int sampling = 1; int smallestH = pY << 2; // double so comparison works int smallestW = pX << 2; // cannot be smaller than page while (newW > smallestW && newH > smallestH) { sampling = sampling << 1; newW = newW >> 1; newH = newH >> 1; } int scaleX = w / pX; if (scaleX < 1) scaleX = 1; int scaleY = h / pY; if (scaleY < 1) scaleY = 1; // choose smaller value so at least size of page sampling = scaleX; if (sampling > scaleY) sampling = scaleY; // switch to 8 bit and reduce bw image size by averaging if (sampling > 1) { newW = w / sampling; newH = h / sampling; if (d == 1) { int size = newW * newH; byte[] newData = new byte[size]; final int[] flag = {1, 2, 4, 8, 16, 32, 64, 128}; int origLineLength = (w + 7) >> 3; int bit; byte currentByte; // scan all pixels and down-sample for (int y = 0; y < newH; y++) { for (int x = 0; x < newW; x++) { int bytes = 0, count = 0; // allow for edges in number of pixels left int wCount = sampling, hCount = sampling; int wGapLeft = w - x; int hGapLeft = h - y; if (wCount > wGapLeft) wCount = wGapLeft; if (hCount > hGapLeft) hCount = hGapLeft; // count pixels in sample we will make into a pixel (ie // 2x2 is 4 pixels , 4x4 is 16 pixels) for (int yy = 0; yy < hCount; yy++) { for (int xx = 0; xx < wCount; xx++) { currentByte = data[((yy + (y * sampling)) * origLineLength) + (((x * sampling) + xx) >> 3)]; bit = currentByte & flag[7 - (((x * sampling) + xx) & 7)]; if (bit != 0) bytes++; count++; } } // set value as white or average of pixels int offset = x + (newW * y); if (count > 0) { newData[offset] = (byte) ((255 * bytes) / count); } else { newData[offset] = (byte) 255; } } } data = newData; h = newH; w = newW; d = 8; // imageMask=false; } else if (d == 8) { int x = 0, y = 0, xx = 0, yy = 0, jj = 0, origLineLength = 0; try { // black and white if (w * h == data.length) comp = 1; byte[] newData = new byte[newW * newH * comp]; // System.err.println(w+" "+h+" "+data.length+" // comp="+comp+" scaling="+sampling+" "+decodeColorData); origLineLength = w * comp; // System.err.println("size="+w*h*comp+" filter"+filter+" // scaling="+sampling+" comp="+comp); // System.err.println("w="+w+" h="+h+" data="+data.length+" // origLineLength="+origLineLength+" sampling="+sampling); // scan all pixels and down-sample for (y = 0; y < newH; y++) { for (x = 0; x < newW; x++) { // allow for edges in number of pixels left int wCount = sampling, hCount = sampling; int wGapLeft = w - x; int hGapLeft = h - y; if (wCount > wGapLeft) wCount = wGapLeft; if (hCount > hGapLeft) hCount = hGapLeft; for (jj = 0; jj < comp; jj++) { int byteTotal = 0, count = 0; // count pixels in sample we will make into a // pixel (ie 2x2 is 4 pixels , 4x4 is 16 pixels) for (yy = 0; yy < hCount; yy++) { for (xx = 0; xx < wCount; xx++) { byteTotal = byteTotal + (data[((yy + (y * sampling)) * origLineLength) + (((x * sampling * comp) + (xx * comp) + jj))] & 255); count++; } } // set value as white or average of pixels if (count > 0) // if(index==null) newData[jj + (x * comp) + (newW * y * comp)] = (byte) ((byteTotal) / count); // else // newData[x+(newW*y)]=(byte)(((index[1] & // 255)*byteTotal)/count); else { // if(index==null) // newData[jj+x+(newW*y*comp)]=(byte) 255; // else // newData[x+(newW*y)]=index[0]; } } } } data = newData; h = newH; w = newW; } catch (Exception e) { e.printStackTrace(); } } } if (sampling > 1) { final int[] bands = {0}; // System.out.println("w=" + w + " h=" + h + " size=" + // data.length); // WritableRaster raster =Raster.createPackedRaster(new // DataBufferByte(newData, newData.length), newW, newH, 1, null); Raster raster = Raster.createInterleavedRaster(new DataBufferByte(data, data.length), w, h, w, 1, bands, null); BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY); image.setData(raster); return image; } else { return null; } } private void readFile() { // Do not allow this header to be read more than once. if (readFile) return; // Make sure that the application has set the input source. if (stream == null) throw new IllegalStateException("No input stream!"); // Read the header. decoder = new JBIG2Decoder(); try { byte[] data; int size = (int) stream.length(); if (size == -1) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] temp = new byte[8192]; for (int len = 0; (len = stream.read(temp)) > 0; ) { bos.write(temp, 0, len); } bos.close(); data = bos.toByteArray(); } else { data = new byte[size]; stream.readFully(data); } decoder.decodeJBIG2(data); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (JBIG2Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } readFile = true; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy