/** * 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. * * <p>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.</p */ protected BufferedImage parseData(byte[] data) { // create the data buffer DataBuffer db = new DataBufferByte(data, data.length); // pick a color model, based on the number of components and // bits per component ColorModel cm = getColorModel(); // create a compatible raster SampleModel sm = cm.createCompatibleSampleModel(getWidth(), getHeight()); WritableRaster raster; try { raster = Raster.createWritableRaster(sm, db, new Point(0, 0)); } catch (RasterFormatException e) { int tempExpectedSize = getWidth() * getHeight() * getColorSpace().getNumComponents() * Math.max(8, getBitsPerComponent()) / 8; if (tempExpectedSize < 3) { tempExpectedSize = 3; } if (tempExpectedSize > data.length) { byte[] tempLargerData = new byte[tempExpectedSize]; System.arraycopy(data, 0, tempLargerData, 0, data.length); db = new DataBufferByte(tempLargerData, tempExpectedSize); raster = Raster.createWritableRaster(sm, db, new Point(0, 0)); } else { throw e; } } /* * Workaround for a bug on the Mac -- a class cast exception in * drawImage() due to the wrong data buffer type (?) */ BufferedImage bi = null; if (cm instanceof IndexColorModel) { IndexColorModel icm = (IndexColorModel) cm; // choose the image type based on the size int type = BufferedImage.TYPE_BYTE_BINARY; if (getBitsPerComponent() == 8) { type = BufferedImage.TYPE_BYTE_INDEXED; } // create the image with an explicit indexed color model. bi = new BufferedImage(getWidth(), getHeight(), type, icm); // set the data explicitly as well bi.setData(raster); } else { // Raster is already in a format which is supported by Java2D, // such as RGB or Gray. bi = new BufferedImage(cm, raster, true, null); } // hack to avoid *very* slow conversion ColorSpace cs = cm.getColorSpace(); ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB); if (!isImageMask() && cs instanceof ICC_ColorSpace && !cs.equals(rgbCS)) { ColorConvertOp op = new ColorConvertOp(cs, rgbCS, null); BufferedImage converted = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); bi = op.filter(bi, converted); } // add in the alpha data supplied by the SMask, if any PDFImage sMaskImage = getSMask(); if (sMaskImage != null) { BufferedImage si = sMaskImage.getImage(); BufferedImage outImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); int[] srcArray = new int[this.width]; int[] maskArray = new int[this.width]; for (int i = 0; i < this.height; i++) { bi.getRGB(0, i, this.width, 1, srcArray, 0, this.width); si.getRGB(0, i, this.width, 1, maskArray, 0, this.width); for (int j = 0; j < this.width; j++) { int ac = 0xff000000; maskArray[j] = ((maskArray[j] & 0xff) << 24) | (srcArray[j] & ~ac); } outImage.setRGB(0, i, this.width, 1, maskArray, 0, this.width); } bi = outImage; } return (bi); }
/** * 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 * @param useAsSMask - flag for switching colors in case image is used as sMask internally this is * needed for handling transparency in smask images. */ public static PDFImage createImage(PDFObject obj, Map resources, boolean useAsSMask) 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: // [PATCHED by XOND] - switched colors in case the image is used as SMask for another image, // otherwise transparency isn't // handled correctly. Color[] colors = useAsSMask ? new Color[] {Color.WHITE, Color.BLACK} : new Color[] {Color.BLACK, Color.WHITE}; PDFObject imageMaskDecode = obj.getDictRef("Decode"); if (imageMaskDecode != null) { PDFObject[] decodeArray = imageMaskDecode.getArray(); float decode0 = decodeArray[0].getFloatValue(); if (decode0 == 1.0f) { colors = useAsSMask ? new Color[] {Color.BLACK, Color.WHITE} : new Color[] {Color.WHITE, Color.BLACK}; } float[] decode = new float[decodeArray.length]; for (int i = 0; i < decodeArray.length; i++) { decode[i] = decodeArray[i].getFloatValue(); } image.setDecode(decode); } 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.) 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, true); 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; }