com.sun.pdfview.PDFImage Maven / Gradle / Ivy
/*
* $Id: PDFImage.java,v 1.9 2009/03/12 13:23:54 tomoke Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.awt.Color;
import java.awt.Point;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.MultiPixelPackedSampleModel;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import com.sun.pdfview.colorspace.IndexedColor;
import com.sun.pdfview.colorspace.PDFColorSpace;
import com.sun.pdfview.function.FunctionType0;
/**
* Encapsulates a PDF Image
*/
public class PDFImage {
public static void dump(PDFObject obj) throws IOException {
p("dumping PDF object: " + obj);
if (obj == null) {
return;
}
HashMap dict = obj.getDictionary();
p(" dict = " + dict);
for (Object key : dict.keySet()) {
p("key = " + key + " value = " + dict.get(key));
}
}
public static void p(String string) {
System.out.println(string);
}
/** color key mask. Array of start/end pairs of ranges of color components to
* mask out. If a component falls within any of the ranges it is clear. */
private int[] colorKeyMask = null;
/** the width of this image in pixels */
private int width;
/** the height of this image in pixels */
private int height;
/** the colorspace to interpret the samples in */
private PDFColorSpace colorSpace;
/** the number of bits per sample component */
private int bpc;
/** whether this image is a mask or not */
private boolean imageMask = false;
/** the SMask image, if any */
private PDFImage sMask;
/** the decode array */
private float[] decode;
/** the actual image data */
private PDFObject imageObj;
/**
* Create an instance of a PDFImage
*/
protected PDFImage(PDFObject imageObj) {
this.imageObj = imageObj;
}
/**
* Read a PDFImage from an image dictionary and stream
*
* @param obj the PDFObject containing the image's dictionary and stream
* @param resources the current resources
*/
public static PDFImage createImage(PDFObject obj, Map resources)
throws IOException {
// create the image
PDFImage image = new PDFImage(obj);
// get the width (required)
PDFObject widthObj = obj.getDictRef("Width");
if (widthObj == null) {
throw new PDFParseException("Unable to read image width: " + obj);
}
image.setWidth(widthObj.getIntValue());
// get the height (required)
PDFObject heightObj = obj.getDictRef("Height");
if (heightObj == null) {
throw new PDFParseException("Unable to get image height: " + obj);
}
image.setHeight(heightObj.getIntValue());
// figure out if we are an image mask (optional)
PDFObject imageMaskObj = obj.getDictRef("ImageMask");
if (imageMaskObj != null) {
image.setImageMask(imageMaskObj.getBooleanValue());
}
// read the bpc and colorspace (required except for masks)
if (image.isImageMask()) {
image.setBitsPerComponent(1);
// create the indexed color space for the mask
// [PATCHED by [email protected]] - default value od Decode according to PDF spec. is [0, 1]
// so the color arry should be:
Color[] colors = {Color.BLACK, Color.WHITE};
PDFObject imageMaskDecode = obj.getDictRef("Decode");
if (imageMaskDecode != null) {
PDFObject[] array = imageMaskDecode.getArray();
float decode0 = array[0].getFloatValue();
if (decode0 == 1.0f) {
colors = new Color[]{Color.WHITE, Color.BLACK};
}
}
image.setColorSpace(new IndexedColor(colors));
} else {
// get the bits per component (required)
PDFObject bpcObj = obj.getDictRef("BitsPerComponent");
if (bpcObj == null) {
throw new PDFParseException("Unable to get bits per component: " + obj);
}
image.setBitsPerComponent(bpcObj.getIntValue());
// get the color space (required)
PDFObject csObj = obj.getDictRef("ColorSpace");
if (csObj == null) {
throw new PDFParseException("No ColorSpace for image: " + obj);
}
PDFColorSpace cs = PDFColorSpace.getColorSpace(csObj, resources);
image.setColorSpace(cs);
}
// read the decode array
PDFObject decodeObj = obj.getDictRef("Decode");
if (decodeObj != null) {
PDFObject[] decodeArray = decodeObj.getArray();
float[] decode = new float[decodeArray.length];
for (int i = 0; i < decodeArray.length; i++) {
decode[i] = decodeArray[i].getFloatValue();
}
image.setDecode(decode);
}
// read the soft mask.
// If ImageMask is true, this entry must not be present.
// (See implementation note 52 in Appendix H.)
if (imageMaskObj == null) {
PDFObject sMaskObj = obj.getDictRef("SMask");
if (sMaskObj == null) {
// try the explicit mask, if there is no SoftMask
sMaskObj = obj.getDictRef("Mask");
}
if (sMaskObj != null) {
if (sMaskObj.getType() == PDFObject.STREAM) {
try {
PDFImage sMaskImage = PDFImage.createImage(sMaskObj, resources);
image.setSMask(sMaskImage);
} catch (IOException ex) {
p("ERROR: there was a problem parsing the mask for this object");
dump(obj);
ex.printStackTrace(System.out);
}
} else if (sMaskObj.getType() == PDFObject.ARRAY) {
// retrieve the range of the ColorKeyMask
// colors outside this range will not be painted.
try {
image.setColorKeyMask(sMaskObj);
} catch (IOException ex) {
p("ERROR: there was a problem parsing the color mask for this object");
dump(obj);
ex.printStackTrace(System.out);
}
}
}
}
return image;
}
/**
* Get the image that this PDFImage generates.
*
* @return a buffered image containing the decoded image data
*/
public BufferedImage getImage() {
try {
BufferedImage bi = (BufferedImage) imageObj.getCache();
if (bi == null) {
// parse the stream data into an actual image
bi = parseData(imageObj.getStream());
imageObj.setCache(bi);
}
// if(bi != null)
// ImageIO.write(bi, "png", new File("/tmp/test/" + System.identityHashCode(this) + ".png"));
return bi;
} catch (IOException ioe) {
System.out.println("Error reading image");
ioe.printStackTrace();
return null;
}
}
/**
* Parse the image stream into a buffered image. Note that this is
* guaranteed to be called after all the other setXXX methods have been
* called.
*
* NOTE: the color convolving is extremely slow on large images.
* It would be good to see if it could be moved out into the rendering
* phases, where we might be able to scale the image down first.