/* * $Id: Image.java,v 1.115 2006/02/16 16:17:59 psoares33 Exp $ * $Name: $ * * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie. * * The contents of this file are subject to the Mozilla Public License Version 1.1 * (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the License. * * The Original Code is 'iText, a free JAVA-PDF library'. * * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. * All Rights Reserved. * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. * * Contributor(s): all the names of the contributors are added in the source code * where applicable. * * Alternatively, the contents of this file may be used under the terms of the * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the * provisions of LGPL are applicable instead of those above. If you wish to * allow use of your version of this file only under the terms of the LGPL * License and not to allow others to use your version of this file under * the MPL, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the LGPL. * If you do not delete the provisions above, a recipient may use your version * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. * * This library is free software; you can redistribute it and/or modify it * under the terms of the MPL as stated above or under the terms of the GNU * Library General Public License as published by the Free Software Foundation; * either version 2 of the License, or 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 Library general Public License for more * details. * * If you didn't download this code from the following link, you should check if * you aren't using an obsolete version: * http://www.lowagie.com/iText/ */ package com.lowagie.text; import java.awt.Graphics2D; import java.awt.color.ICC_Profile; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Properties; import java.util.Set; import com.lowagie.text.pdf.PRIndirectReference; import com.lowagie.text.pdf.PRTokeniser; import com.lowagie.text.pdf.PdfArray; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfDictionary; import com.lowagie.text.pdf.PdfIndirectReference; import com.lowagie.text.pdf.PdfName; import com.lowagie.text.pdf.PdfNumber; import com.lowagie.text.pdf.PdfOCG; import com.lowagie.text.pdf.PdfObject; import com.lowagie.text.pdf.PdfReader; import com.lowagie.text.pdf.PdfTemplate; import com.lowagie.text.pdf.PdfWriter; import com.lowagie.text.pdf.RandomAccessFileOrArray; import com.lowagie.text.pdf.codec.BmpImage; import com.lowagie.text.pdf.codec.CCITTG4Encoder; import com.lowagie.text.pdf.codec.GifImage; import com.lowagie.text.pdf.codec.PngImage; import com.lowagie.text.pdf.codec.TiffImage; /** * An Image is the representation of a graphic element (JPEG, PNG * or GIF) that has to be inserted into the document * * @see Element * @see Rectangle */ public abstract class Image extends Rectangle implements Element, MarkupAttributes { // static membervariables /** this is a kind of image alignment. */ public static final int DEFAULT = 0; /** this is a kind of image alignment. */ public static final int RIGHT = 2; /** this is a kind of image alignment. */ public static final int LEFT = 0; /** this is a kind of image alignment. */ public static final int MIDDLE = 1; /** this is a kind of image alignment. */ public static final int TEXTWRAP = 4; /** this is a kind of image alignment. */ public static final int UNDERLYING = 8; /** This represents a coordinate in the transformation matrix. */ public static final int AX = 0; /** This represents a coordinate in the transformation matrix. */ public static final int AY = 1; /** This represents a coordinate in the transformation matrix. */ public static final int BX = 2; /** This represents a coordinate in the transformation matrix. */ public static final int BY = 3; /** This represents a coordinate in the transformation matrix. */ public static final int CX = 4; /** This represents a coordinate in the transformation matrix. */ public static final int CY = 5; /** This represents a coordinate in the transformation matrix. */ public static final int DX = 6; /** This represents a coordinate in the transformation matrix. */ public static final int DY = 7; /** type of image */ public static final int ORIGINAL_NONE = 0; /** type of image */ public static final int ORIGINAL_JPEG = 1; /** type of image */ public static final int ORIGINAL_PNG = 2; /** type of image */ public static final int ORIGINAL_GIF = 3; /** type of image */ public static final int ORIGINAL_BMP = 4; /** type of image */ public static final int ORIGINAL_TIFF = 5; /** type of image */ public static final int ORIGINAL_WMF = 6; /** type of image */ public static final int ORIGINAL_PS = 7; /** Image color inversion */ protected boolean invert = false; /** The imagetype. */ protected int type; /** The URL of the image. */ protected URL url; /** The raw data of the image. */ protected byte rawData[]; /** The template to be treated as an image. */ protected PdfTemplate template[] = new PdfTemplate[1]; /** The alignment of the Image. */ protected int alignment; /** Text that can be shown instead of the image. */ protected String alt; /** This is the absolute X-position of the image. */ protected float absoluteX = Float.NaN; /** This is the absolute Y-position of the image. */ protected float absoluteY = Float.NaN; /** This is the width of the image without rotation. */ protected float plainWidth; /** This is the width of the image without rotation. */ protected float plainHeight; /** This is the scaled width of the image taking rotation into account. */ protected float scaledWidth; /** This is the original height of the image taking rotation into account. */ protected float scaledHeight; /** This is the rotation of the image. */ protected float rotation; /** this is the colorspace of a jpeg-image. */ protected int colorspace = -1; /** * this is the bits per component of the raw image. It also flags a CCITT * image. */ protected int bpc = 1; /** this is the transparency information of the raw image */ protected int transparency[]; // for the moment these variables are only used for Images in class Table // code contributed by Pelikan Stephan /** the indentation to the left. */ protected float indentationLeft = 0; /** the indentation to the right. */ protected float indentationRight = 0; // serial stamping protected Long mySerialId = getSerialId(); static long serialId = 0; /** Holds value of property dpiX. */ protected int dpiX = 0; /** Holds value of property dpiY. */ protected int dpiY = 0; protected boolean mask = false; protected Image imageMask; /** Holds value of property interpolation. */ protected boolean interpolation; /** if the annotation is not null the image will be clickable. */ protected Annotation annotation = null; /** Contains extra markupAttributes */ protected Properties markupAttributes; /** ICC Profile attached */ protected ICC_Profile profile = null; /** Holds value of property deflated. */ protected boolean deflated = false; private PdfDictionary additional = null; /** Holds value of property smask. */ private boolean smask; /** Holds value of property XYRatio. */ private float XYRatio = 0; /** Holds value of property originalType. */ protected int originalType = ORIGINAL_NONE; /** Holds value of property originalData. */ protected byte[] originalData; /** The spacing before the image. */ protected float spacingBefore; /** The spacing after the image. */ protected float spacingAfter; /** * Holds value of property widthPercentage. */ private float widthPercentage = 100; protected PdfOCG layer; /** * Holds value of property initialRotation. */ private float initialRotation; // constructors /** * Constructs an Image -object, using an url . * * @param url * the URL where the image can be found. */ public Image(URL url) { super(0, 0); this.url = url; this.alignment = DEFAULT; rotation = 0; } /** * Constructs an Image -object, using an url . * * @param image * another Image object. */ protected Image(Image image) { super(image); this.type = image.type; this.url = image.url; this.alignment = image.alignment; this.alt = image.alt; this.absoluteX = image.absoluteX; this.absoluteY = image.absoluteY; this.plainWidth = image.plainWidth; this.plainHeight = image.plainHeight; this.scaledWidth = image.scaledWidth; this.scaledHeight = image.scaledHeight; this.rotation = image.rotation; this.colorspace = image.colorspace; this.rawData = image.rawData; this.template = image.template; this.bpc = image.bpc; this.transparency = image.transparency; this.mySerialId = image.mySerialId; this.invert = image.invert; this.dpiX = image.dpiX; this.dpiY = image.dpiY; this.mask = image.mask; this.imageMask = image.imageMask; this.interpolation = image.interpolation; this.annotation = image.annotation; this.markupAttributes = image.markupAttributes; this.profile = image.profile; this.deflated = image.deflated; this.additional = image.additional; this.smask = image.smask; this.XYRatio = image.XYRatio; this.originalData = image.originalData; this.originalType = image.originalType; this.spacingAfter = image.spacingAfter; this.spacingBefore = image.spacingBefore; this.widthPercentage = image.widthPercentage; this.layer = image.layer; this.initialRotation = image.initialRotation; this.directReference = image.directReference; } /** * gets an instance of an Image * * @param image * an Image object * @return a new Image object */ public static Image getInstance(Image image) { if (image == null) return null; try { Class cs = image.getClass(); Constructor constructor = cs .getDeclaredConstructor(new Class[] { Image.class }); return (Image) constructor.newInstance(new Object[] { image }); } catch (Exception e) { throw new ExceptionConverter(e); } } /** * Gets an instance of an Image. * * @param url * an URL * @return an Image * @throws BadElementException * @throws MalformedURLException * @throws IOException */ public static Image getInstance(URL url) throws BadElementException, MalformedURLException, IOException { InputStream is = null; try { is = url.openStream(); int c1 = is.read(); int c2 = is.read(); int c3 = is.read(); int c4 = is.read(); is.close(); is = null; if (c1 == 'G' && c2 == 'I' && c3 == 'F') { GifImage gif = new GifImage(url); Image img = gif.getImage(1); return img; } if (c1 == 0xFF && c2 == 0xD8) { return new Jpeg(url); } if (c1 == PngImage.PNGID[0] && c2 == PngImage.PNGID[1] && c3 == PngImage.PNGID[2] && c4 == PngImage.PNGID[3]) { return PngImage.getImage(url); } if (c1 == '%' && c2 == '!' && c3 == 'P' && c4 == 'S') { return new ImgPostscript(url); } if (c1 == 0xD7 && c2 == 0xCD) { return new ImgWMF(url); } if (c1 == 'B' && c2 == 'M') { return BmpImage.getImage(url); } if ((c1 == 'M' && c2 == 'M' && c3 == 0 && c4 == 42) || (c1 == 'I' && c2 == 'I' && c3 == 42 && c4 == 0)) { RandomAccessFileOrArray ra = null; try { if (url.getProtocol().equals("file")) { String file = url.getFile(); file = unEscapeURL(file); ra = new RandomAccessFileOrArray(file); } else ra = new RandomAccessFileOrArray(url); Image img = TiffImage.getTiffImage(ra, 1); img.url = url; return img; } finally { if (ra != null) ra.close(); } } throw new IOException(url.toString() + " is not a recognized imageformat."); } finally { if (is != null) { is.close(); } } } /** * gets an instance of an Image * * @param imgb * raw image date * @return an Image object * @throws BadElementException * @throws MalformedURLException * @throws IOException */ public static Image getInstance(byte imgb[]) throws BadElementException, MalformedURLException, IOException { InputStream is = null; try { is = new java.io.ByteArrayInputStream(imgb); int c1 = is.read(); int c2 = is.read(); int c3 = is.read(); int c4 = is.read(); is.close(); is = null; if (c1 == 'G' && c2 == 'I' && c3 == 'F') { GifImage gif = new GifImage(imgb); return gif.getImage(1); } if (c1 == 0xFF && c2 == 0xD8) { return new Jpeg(imgb); } if (c1 == PngImage.PNGID[0] && c2 == PngImage.PNGID[1] && c3 == PngImage.PNGID[2] && c4 == PngImage.PNGID[3]) { return PngImage.getImage(imgb); } if (c1 == '%' && c2 == '!' && c3 == 'P' && c4 == 'S') { return new ImgPostscript(imgb); } if (c1 == 0xD7 && c2 == 0xCD) { return new ImgWMF(imgb); } if (c1 == 'B' && c2 == 'M') { return BmpImage.getImage(imgb); } if ((c1 == 'M' && c2 == 'M' && c3 == 0 && c4 == 42) || (c1 == 'I' && c2 == 'I' && c3 == 42 && c4 == 0)) { RandomAccessFileOrArray ra = null; try { ra = new RandomAccessFileOrArray(imgb); Image img = TiffImage.getTiffImage(ra, 1); if (img.getOriginalData() == null) img.setOriginalData(imgb); return img; } finally { if (ra != null) ra.close(); } } throw new IOException( "The byte array is not a recognized imageformat."); } finally { if (is != null) { is.close(); } } } /** * Gets an instance of an Image from a java.awt.Image. * * @param image * the java.awt.Image to convert * @param color * if different from null the transparency pixels * are replaced by this color * @param forceBW * if true the image is treated as black and white * @return an object of type ImgRaw * @throws BadElementException * on error * @throws IOException * on error */ public static Image getInstance(java.awt.Image image, java.awt.Color color, boolean forceBW) throws BadElementException, IOException { java.awt.image.PixelGrabber pg = new java.awt.image.PixelGrabber(image, 0, 0, -1, -1, true); try { pg.grabPixels(); } catch (InterruptedException e) { throw new IOException( "java.awt.Image Interrupted waiting for pixels!"); } if ((pg.getStatus() & java.awt.image.ImageObserver.ABORT) != 0) { throw new IOException("java.awt.Image fetch aborted or errored"); } int w = pg.getWidth(); int h = pg.getHeight(); int[] pixels = (int[]) pg.getPixels(); if (forceBW) { int byteWidth = (w / 8) + ((w & 7) != 0 ? 1 : 0); byte[] pixelsByte = new byte[byteWidth * h]; int index = 0; int size = h * w; int transColor = 1; if (color != null) { transColor = (color.getRed() + color.getGreen() + color.getBlue() < 384) ? 0 : 1; } int transparency[] = null; int cbyte = 0x80; int wMarker = 0; int currByte = 0; if (color != null) { for (int j = 0; j < size; j++) { int alpha = (pixels[j] >> 24) & 0xff; if (alpha < 250) { if (transColor == 1) currByte |= cbyte; } else { if ((pixels[j] & 0x888) != 0) currByte |= cbyte; } cbyte >>= 1; if (cbyte == 0 || wMarker + 1 >= w) { pixelsByte[index++] = (byte) currByte; cbyte = 0x80; currByte = 0; } ++wMarker; if (wMarker >= w) wMarker = 0; } } else { for (int j = 0; j < size; j++) { if (transparency == null) { int alpha = (pixels[j] >> 24) & 0xff; if (alpha == 0) { transparency = new int[2]; transparency[0] = transparency[1] = ((pixels[j] & 0x888) != 0) ? 1 : 0; } } if ((pixels[j] & 0x888) != 0) currByte |= cbyte; cbyte >>= 1; if (cbyte == 0 || wMarker + 1 >= w) { pixelsByte[index++] = (byte) currByte; cbyte = 0x80; currByte = 0; } ++wMarker; if (wMarker >= w) wMarker = 0; } } return Image.getInstance(w, h, 1, 1, pixelsByte, transparency); } else { byte[] pixelsByte = new byte[w * h * 3]; byte[] smask = null; int index = 0; int size = h * w; int red = 255; int green = 255; int blue = 255; if (color != null) { red = color.getRed(); green = color.getGreen(); blue = color.getBlue(); } int transparency[] = null; if (color != null) { for (int j = 0; j < size; j++) { int alpha = (pixels[j] >> 24) & 0xff; if (alpha < 250) { pixelsByte[index++] = (byte) red; pixelsByte[index++] = (byte) green; pixelsByte[index++] = (byte) blue; } else { pixelsByte[index++] = (byte) ((pixels[j] >> 16) & 0xff); pixelsByte[index++] = (byte) ((pixels[j] >> 8) & 0xff); pixelsByte[index++] = (byte) ((pixels[j]) & 0xff); } } } else { int transparentPixel = 0; smask = new byte[w * h]; boolean shades = false; for (int j = 0; j < size; j++) { byte alpha = smask[j] = (byte) ((pixels[j] >> 24) & 0xff); /* bugfix by Chris Nokleberg */ if (!shades) { if (alpha != 0 && alpha != -1) { shades = true; } else if (transparency == null) { if (alpha == 0) { transparentPixel = pixels[j] & 0xffffff; transparency = new int[6]; transparency[0] = transparency[1] = (transparentPixel >> 16) & 0xff; transparency[2] = transparency[3] = (transparentPixel >> 8) & 0xff; transparency[4] = transparency[5] = transparentPixel & 0xff; } } else if ((pixels[j] & 0xffffff) != transparentPixel) { shades = true; } } pixelsByte[index++] = (byte) ((pixels[j] >> 16) & 0xff); pixelsByte[index++] = (byte) ((pixels[j] >> 8) & 0xff); pixelsByte[index++] = (byte) ((pixels[j]) & 0xff); } if (shades) transparency = null; else smask = null; } Image img = Image.getInstance(w, h, 3, 8, pixelsByte, transparency); if (smask != null) { Image sm = Image.getInstance(w, h, 1, 8, smask); try { sm.makeMask(); img.setImageMask(sm); } catch (DocumentException de) { throw new ExceptionConverter(de); } } return img; } } /** * Gets an instance of an Image from a java.awt.Image. * * @param image * the java.awt.Image to convert * @param color * if different from null the transparency pixels * are replaced by this color * @return an object of type ImgRaw * @throws BadElementException * on error * @throws IOException * on error */ public static Image getInstance(java.awt.Image image, java.awt.Color color) throws BadElementException, IOException { return Image.getInstance(image, color, false); } /** * Gets an instance of a Image from a java.awt.Image. * The image is added as a JPEG with a userdefined quality. * * @param writer * the PdfWriter object to which the image will be added * @param awtImage * the java.awt.Image to convert * @param quality * a float value between 0 and 1 * @return an object of type PdfTemplate * @throws BadElementException * on error * @throws IOException */ public static Image getInstance(PdfWriter writer, java.awt.Image awtImage, float quality) throws BadElementException, IOException { return getInstance(new PdfContentByte(writer), awtImage, quality); } /** * Gets an instance of a Image from a java.awt.Image. * The image is added as a JPEG with a userdefined quality. * * @param cb * the PdfContentByte object to which the image will be added * @param awtImage * the java.awt.Image to convert * @param quality * a float value between 0 and 1 * @return an object of type PdfTemplate * @throws BadElementException * on error * @throws IOException */ public static Image getInstance(PdfContentByte cb, java.awt.Image awtImage, float quality) throws BadElementException, IOException { java.awt.image.PixelGrabber pg = new java.awt.image.PixelGrabber(awtImage, 0, 0, -1, -1, true); try { pg.grabPixels(); } catch (InterruptedException e) { throw new IOException( "java.awt.Image Interrupted waiting for pixels!"); } if ((pg.getStatus() & java.awt.image.ImageObserver.ABORT) != 0) { throw new IOException("java.awt.Image fetch aborted or errored"); } int w = pg.getWidth(); int h = pg.getHeight(); PdfTemplate tp = cb.createTemplate(w, h); Graphics2D g2d = tp.createGraphics(w, h, true, quality); g2d.drawImage(awtImage, 0, 0, null); g2d.dispose(); return getInstance(tp); } /** * Gets an instance of an Image. * * @param filename * a filename * @return an object of type Gif,Jpeg or * Png * @throws BadElementException * @throws MalformedURLException * @throws IOException */ public static Image getInstance(String filename) throws BadElementException, MalformedURLException, IOException { return getInstance(toURL(filename)); } /** * Gets an instance of an Image in raw mode. * * @param width * the width of the image in pixels * @param height * the height of the image in pixels * @param components * 1,3 or 4 for GrayScale, RGB and CMYK * @param data * the image data * @param bpc * bits per component * @return an object of type ImgRaw * @throws BadElementException * on error */ public static Image getInstance(int width, int height, int components, int bpc, byte data[]) throws BadElementException { return Image.getInstance(width, height, components, bpc, data, null); } /** * Reuses an existing image. * @param ref the reference to the image dictionary * @throws BadElementException on error * @return the image */ public static Image getInstance(PRIndirectReference ref) throws BadElementException { PdfDictionary dic = (PdfDictionary)PdfReader.getPdfObjectRelease(ref); int width = ((PdfNumber)PdfReader.getPdfObjectRelease(dic.get(PdfName.WIDTH))).intValue(); int height = ((PdfNumber)PdfReader.getPdfObjectRelease(dic.get(PdfName.HEIGHT))).intValue(); Image imask = null; PdfObject obj = dic.get(PdfName.SMASK); if (obj != null && obj.isIndirect()) { imask = getInstance((PRIndirectReference)obj); } else { obj = dic.get(PdfName.MASK); if (obj != null && obj.isIndirect()) { PdfObject obj2 = PdfReader.getPdfObjectRelease(obj); if (obj2 instanceof PdfDictionary) imask = getInstance((PRIndirectReference)obj); } } Image img = new ImgRaw(width, height, 1, 1, null); img.imageMask = imask; img.directReference = ref; return img; } /** * gets an instance of an Image * * @param template * a PdfTemplate that has to be wrapped in an Image object * @return an Image object * @throws BadElementException */ public static Image getInstance(PdfTemplate template) throws BadElementException { return new ImgTemplate(template); } /** * Creates an Image with CCITT G3 or G4 compression. It assumes that the * data bytes are already compressed. * * @param width * the exact width of the image * @param height * the exact height of the image * @param reverseBits * reverses the bits in data. Bit 0 is swapped * with bit 7 and so on * @param typeCCITT * the type of compression in data. It can be * CCITTG4, CCITTG31D, CCITTG32D * @param parameters * parameters associated with this stream. Possible values are * CCITT_BLACKIS1, CCITT_ENCODEDBYTEALIGN, CCITT_ENDOFLINE and * CCITT_ENDOFBLOCK or a combination of them * @param data * the image data * @return an Image object * @throws BadElementException * on error */ public static Image getInstance(int width, int height, boolean reverseBits, int typeCCITT, int parameters, byte[] data) throws BadElementException { return Image.getInstance(width, height, reverseBits, typeCCITT, parameters, data, null); } /** * Creates an Image with CCITT G3 or G4 compression. It assumes that the * data bytes are already compressed. * * @param width * the exact width of the image * @param height * the exact height of the image * @param reverseBits * reverses the bits in data. Bit 0 is swapped * with bit 7 and so on * @param typeCCITT * the type of compression in data. It can be * CCITTG4, CCITTG31D, CCITTG32D * @param parameters * parameters associated with this stream. Possible values are * CCITT_BLACKIS1, CCITT_ENCODEDBYTEALIGN, CCITT_ENDOFLINE and * CCITT_ENDOFBLOCK or a combination of them * @param data * the image data * @param transparency * transparency information in the Mask format of the image * dictionary * @return an Image object * @throws BadElementException * on error */ public static Image getInstance(int width, int height, boolean reverseBits, int typeCCITT, int parameters, byte[] data, int transparency[]) throws BadElementException { if (transparency != null && transparency.length != 2) throw new BadElementException( "Transparency length must be equal to 2 with CCITT images"); Image img = new ImgCCITT(width, height, reverseBits, typeCCITT, parameters, data); img.transparency = transparency; return img; } /** * Gets an instance of an Image in raw mode. * * @param width * the width of the image in pixels * @param height * the height of the image in pixels * @param components * 1,3 or 4 for GrayScale, RGB and CMYK * @param data * the image data * @param bpc * bits per component * @param transparency * transparency information in the Mask format of the image * dictionary * @return an object of type ImgRaw * @throws BadElementException * on error */ public static Image getInstance(int width, int height, int components, int bpc, byte data[], int transparency[]) throws BadElementException { if (transparency != null && transparency.length != components * 2) throw new BadElementException( "Transparency length must be equal to (componentes * 2)"); if (components == 1 && bpc == 1) { byte g4[] = CCITTG4Encoder.compress(data, width, height); return Image.getInstance(width, height, false, Image.CCITTG4, Image.CCITT_BLACKIS1, g4, transparency); } Image img = new ImgRaw(width, height, components, bpc, data); img.transparency = transparency; return img; } /** * Returns an Image that has been constructed taking in * account the value of some attributes . * * @param attributes * Some attributes * @return an Image * @throws BadElementException * @throws MalformedURLException * @throws IOException */ public static Image getInstance(Properties attributes) throws BadElementException, MalformedURLException, IOException { String value = (String) attributes.remove(ElementTags.URL); if (value == null) throw new MalformedURLException("The URL of the image is missing."); Image image = Image.getInstance(value); int align = 0; if ((value = (String) attributes.remove(ElementTags.ALIGN)) != null) { if (ElementTags.ALIGN_LEFT.equalsIgnoreCase(value)) align |= Image.LEFT; else if (ElementTags.ALIGN_RIGHT.equalsIgnoreCase(value)) align |= Image.RIGHT; else if (ElementTags.ALIGN_MIDDLE.equalsIgnoreCase(value)) align |= Image.MIDDLE; } if ((value = (String) attributes.remove(ElementTags.UNDERLYING)) != null) { if (new Boolean(value).booleanValue()) align |= Image.UNDERLYING; } if ((value = (String) attributes.remove(ElementTags.TEXTWRAP)) != null) { if (new Boolean(value).booleanValue()) align |= Image.TEXTWRAP; } image.setAlignment(align); if ((value = (String) attributes.remove(ElementTags.ALT)) != null) { image.setAlt(value); } String x; String y; if (((x = (String) attributes.remove(ElementTags.ABSOLUTEX)) != null) && ((y = (String) attributes.remove(ElementTags.ABSOLUTEY)) != null)) { image.setAbsolutePosition(Float.valueOf(x + "f").floatValue(), Float.valueOf(y + "f").floatValue()); } if ((value = (String) attributes.remove(ElementTags.PLAINWIDTH)) != null) { image.scaleAbsoluteWidth(Float.valueOf(value + "f").floatValue()); } if ((value = (String) attributes.remove(ElementTags.PLAINHEIGHT)) != null) { image.scaleAbsoluteHeight(Float.valueOf(value + "f").floatValue()); } if ((value = (String) attributes.remove(ElementTags.ROTATION)) != null) { image.setRotation(Float.valueOf(value + "f").floatValue()); } if (attributes.size() > 0) image.setMarkupAttributes(attributes); return image; } // methods to set information /** * Sets the alignment for the image. * * @param alignment * the alignment */ public void setAlignment(int alignment) { this.alignment = alignment; } /** * Sets the alternative information for the image. * * @param alt * the alternative information */ public void setAlt(String alt) { this.alt = alt; } /** * Sets the absolute position of the Image. * * @param absoluteX * @param absoluteY */ public void setAbsolutePosition(float absoluteX, float absoluteY) { this.absoluteX = absoluteX; this.absoluteY = absoluteY; } /** * Scale the image to an absolute width and an absolute height. * * @param newWidth * the new width * @param newHeight * the new height */ public void scaleAbsolute(float newWidth, float newHeight) { plainWidth = newWidth; plainHeight = newHeight; float[] matrix = matrix(); scaledWidth = matrix[DX] - matrix[CX]; scaledHeight = matrix[DY] - matrix[CY]; } /** * Scale the image to an absolute width. * * @param newWidth * the new width */ public void scaleAbsoluteWidth(float newWidth) { plainWidth = newWidth; float[] matrix = matrix(); scaledWidth = matrix[DX] - matrix[CX]; scaledHeight = matrix[DY] - matrix[CY]; } /** * Scale the image to an absolute height. * * @param newHeight * the new height */ public void scaleAbsoluteHeight(float newHeight) { plainHeight = newHeight; float[] matrix = matrix(); scaledWidth = matrix[DX] - matrix[CX]; scaledHeight = matrix[DY] - matrix[CY]; } /** * Scale the image to a certain percentage. * * @param percent * the scaling percentage */ public void scalePercent(float percent) { scalePercent(percent, percent); } /** * Scale the width and height of an image to a certain percentage. * * @param percentX * the scaling percentage of the width * @param percentY * the scaling percentage of the height */ public void scalePercent(float percentX, float percentY) { plainWidth = (width() * percentX) / 100f; plainHeight = (height() * percentY) / 100f; float[] matrix = matrix(); scaledWidth = matrix[DX] - matrix[CX]; scaledHeight = matrix[DY] - matrix[CY]; } /** * Scales the image so that it fits a certain width and height. * * @param fitWidth * the width to fit * @param fitHeight * the height to fit */ public void scaleToFit(float fitWidth, float fitHeight) { scalePercent(100); float percentX = (fitWidth * 100) / scaledWidth(); float percentY = (fitHeight * 100) / scaledHeight(); scalePercent(percentX < percentY ? percentX : percentY); } /** * Gets the current image rotation in radians. * @return the current image rotation in radians */ public float getImageRotation() { float rot = (float) ((rotation - initialRotation) % (2.0 * Math.PI)); if (rot < 0) { rot += 2.0 * Math.PI; //__IDS__ } return rot; } /** * Sets the rotation of the image in radians. * * @param r * rotation in radians */ public void setRotation(float r) { double d = Math.PI; //__IDS__ rotation = (float) ((r + initialRotation) % (2.0 * d)); //__IDS__ if (rotation < 0) { rotation += 2.0 * d; //__IDS__ } float[] matrix = matrix(); scaledWidth = matrix[DX] - matrix[CX]; scaledHeight = matrix[DY] - matrix[CY]; } /** * Sets the rotation of the image in degrees. * * @param deg * rotation in degrees */ public void setRotationDegrees(float deg) { double d = Math.PI; //__IDS__ setRotation(deg / 180 * (float) d); //__IDS__ } /** * Sets the annotation of this Image. * * @param annotation * the annotation */ public void setAnnotation(Annotation annotation) { this.annotation = annotation; } /** * Gets the annotation. * * @return the annotation that is linked to this image */ public Annotation annotation() { return annotation; } // methods to retrieve information /** * Gets the bpc for the image. *

* Remark: this only makes sense for Images of the type RawImage * . * * @return a bpc value */ public int bpc() { return bpc; } /** * Gets the raw data for the image. *

* Remark: this only makes sense for Images of the type RawImage * . * * @return the raw data */ public byte[] rawData() { return rawData; } /** * Gets the template to be used as an image. *

* Remark: this only makes sense for Images of the type ImgTemplate * . * * @return the template */ public PdfTemplate templateData() { return template[0]; } /** * Sets data from a PdfTemplate * * @param template * the template with the content */ public void setTemplateData(PdfTemplate template) { this.template[0] = template; } /** * Checks if the Images has to be added at an absolute * position. * * @return a boolean */ public boolean hasAbsolutePosition() { return !Float.isNaN(absoluteY); } /** * Checks if the Images has to be added at an absolute X * position. * * @return a boolean */ public boolean hasAbsoluteX() { return !Float.isNaN(absoluteX); } /** * Returns the absolute X position. * * @return a position */ public float absoluteX() { return absoluteX; } /** * Returns the absolute Y position. * * @return a position */ public float absoluteY() { return absoluteY; } /** * Returns the type. * * @return a type */ public int type() { return type; } /** * Returns true if the image is a Jpeg * -object. * * @return a boolean */ public boolean isJpeg() { return type == JPEG; } /** * Returns true if the image is a ImgRaw * -object. * * @return a boolean */ public boolean isImgRaw() { return type == IMGRAW; } /** * Returns true if the image is an ImgTemplate * -object. * * @return a boolean */ public boolean isImgTemplate() { return type == IMGTEMPLATE; } /** * Gets the String -representation of the reference to the * image. * * @return a String */ public URL url() { return url; } /** * Gets the alignment for the image. * * @return a value */ public int alignment() { return alignment; } /** * Gets the alternative text for the image. * * @return a String */ public String alt() { return alt; } /** * Gets the scaled width of the image. * * @return a value */ public float scaledWidth() { return scaledWidth; } /** * Gets the scaled height of the image. * * @return a value */ public float scaledHeight() { return scaledHeight; } /** * Gets the colorspace for the image. *

* Remark: this only makes sense for Images of the type Jpeg. * * @return a colorspace value */ public int colorspace() { return colorspace; } /** * Returns the transformation matrix of the image. * * @return an array [AX, AY, BX, BY, CX, CY, DX, DY] */ public float[] matrix() { float[] matrix = new float[8]; float cosX = (float) Math.cos(rotation); float sinX = (float) Math.sin(rotation); matrix[AX] = plainWidth * cosX; matrix[AY] = plainWidth * sinX; matrix[BX] = (-plainHeight) * sinX; matrix[BY] = plainHeight * cosX; if (rotation < Math.PI / 2f) { matrix[CX] = matrix[BX]; matrix[CY] = 0; matrix[DX] = matrix[AX]; matrix[DY] = matrix[AY] + matrix[BY]; } else if (rotation < Math.PI) { matrix[CX] = matrix[AX] + matrix[BX]; matrix[CY] = matrix[BY]; matrix[DX] = 0; matrix[DY] = matrix[AY]; } else if (rotation < Math.PI * 1.5f) { matrix[CX] = matrix[AX]; matrix[CY] = matrix[AY] + matrix[BY]; matrix[DX] = matrix[BX]; matrix[DY] = 0; } else { matrix[CX] = 0; matrix[CY] = matrix[AY]; matrix[DX] = matrix[AX] + matrix[BX]; matrix[DY] = matrix[BY]; } return matrix; } /** * This method is an alternative for the InputStream.skip() * -method that doesn't seem to work properly for big values of size * . * * @param is * the InputStream * @param size * the number of bytes to skip * @throws IOException */ static public void skip(InputStream is, int size) throws IOException { long n; while (size > 0) { n = is.skip(size); if (n <= 0) break; size -= n; } } private static String excUri = " <>#%\"{}[]|\\\u005E\u0060"; private static String[] excUriEsc = {"%20", "%3C", "%3E", "%23", "%25", "%22", "%7B", "%7D", "%5B", "%5D", "%7C", "%5C", "%5E", "%60"}; /** * Holds value of property directReference. */ private PdfIndirectReference directReference; /** * This method makes a valid URL from a given filename. *

* This method makes the conversion of this library from the JAVA 2 platform * to a JDK1.1.x-version easier. * * @param filename * a given filename * @return a valid URL * @throws MalformedURLException */ public static URL toURL(String filename) throws MalformedURLException { if (filename.startsWith("file:/") || filename.startsWith("http://") || filename.startsWith("https://") || filename.startsWith("jar:")) { return new URL(filename); } File f = new File(filename); String path = f.getAbsolutePath(); if (File.separatorChar != '/') { path = path.replace(File.separatorChar, '/'); } if (!path.startsWith("/")) { path = "/" + path; } if (!path.endsWith("/") && f.isDirectory()) { path = path + "/"; } char[] t = path.toCharArray(); StringBuffer sb = new StringBuffer(); for (int k = 0; k < t.length; ++k) { char c = t[k]; int a = excUri.indexOf(c); if (a >= 0) sb.append(excUriEsc[a]); else sb.append(c); } return new URL("file", "", sb.toString()); } /** * Unescapes an URL. All the "%xx" are replaced by the 'xx' hex char value. * @param src the url to unescape * @return the eunescaped value */ public static String unEscapeURL(String src) { StringBuffer bf = new StringBuffer(); char[] s = src.toCharArray(); for (int k = 0; k < s.length; ++k) { char c = s[k]; if (c == '%') { if (k + 2 >= s.length) { bf.append(c); continue; } int a0 = PRTokeniser.getHex((int)s[k + 1]); int a1 = PRTokeniser.getHex((int)s[k + 2]); if (a0 < 0 || a1 < 0) { bf.append(c); continue; } bf.append((char)(a0 * 16 + a1)); k += 2; } else bf.append(c); } return bf.toString(); } /** * Returns the transparency. * * @return the transparency values */ public int[] getTransparency() { return transparency; } /** * Sets the transparency values * * @param transparency * the transparency values */ public void setTransparency(int transparency[]) { this.transparency = transparency; } /** * Checks if a given tag corresponds with this object. * * @param tag * the given tag * @return true if the tag corresponds */ public static boolean isTag(String tag) { return ElementTags.IMAGE.equals(tag); } /** * Gets the plain width of the image. * * @return a value */ public float plainWidth() { return plainWidth; } /** * Gets the plain height of the image. * * @return a value */ public float plainHeight() { return plainHeight; } static protected synchronized Long getSerialId() { ++serialId; return new Long(serialId); } /** * Returns a serial id for the Image (reuse the same image more than once) * * @return a serialId */ public Long getMySerialId() { return mySerialId; } /** * Gets the dots-per-inch in the X direction. Returns 0 if not available. * * @return the dots-per-inch in the X direction */ public int getDpiX() { return dpiX; } /** * Gets the dots-per-inch in the Y direction. Returns 0 if not available. * * @return the dots-per-inch in the Y direction */ public int getDpiY() { return dpiY; } /** * Sets the dots per inch value * * @param dpiX * dpi for x coordinates * @param dpiY * dpi for y coordinates */ public void setDpi(int dpiX, int dpiY) { this.dpiX = dpiX; this.dpiY = dpiY; } /** * Returns true if this Image has the * requisites to be a mask. * * @return true if this Image can be a mask */ public boolean isMaskCandidate() { if (type == IMGRAW) { if (bpc > 0xff) return true; } return colorspace == 1; } /** * Make this Image a mask. * * @throws DocumentException * if this Image can not be a mask */ public void makeMask() throws DocumentException { if (!isMaskCandidate()) throw new DocumentException("This image can not be an image mask."); mask = true; } /** * Sets the explicit masking. * * @param mask * the mask to be applied * @throws DocumentException * on error */ public void setImageMask(Image mask) throws DocumentException { if (this.mask) throw new DocumentException( "An image mask cannot contain another image mask."); if (!mask.mask) throw new DocumentException( "The image mask is not a mask. Did you do makeMask()?"); imageMask = mask; smask = (mask.bpc > 1 && mask.bpc <= 8); } /** * Gets the explicit masking. * * @return the explicit masking */ public Image getImageMask() { return imageMask; } /** * Returns true if this Image is a mask. * * @return true if this Image is a mask */ public boolean isMask() { return mask; } /** * Inverts the meaning of the bits of a mask. * * @param invert * true to invert the meaning of the bits of a * mask */ public void setInvertMask(boolean invert) { this.invert = invert; } /** * Returns true if the bits are to be inverted in the mask. * * @return true if the bits are to be inverted in the mask */ public boolean isInvertMask() { return invert; } /** * Getter for the inverted value * * @return true if the image is inverted */ public boolean isInverted() { return invert; } /** * Sets inverted true or false * * @param invert * true or false */ public void setInverted(boolean invert) { this.invert = invert; } /** * Getter for property interpolation. * * @return Value of property interpolation. */ public boolean isInterpolation() { return interpolation; } /** * Sets the image interpolation. Image interpolation attempts to produce a * smooth transition between adjacent sample values. * * @param interpolation * New value of property interpolation. */ public void setInterpolation(boolean interpolation) { this.interpolation = interpolation; } /** * @see com.lowagie.text.MarkupAttributes#setMarkupAttribute(java.lang.String, * java.lang.String) */ public void setMarkupAttribute(String name, String value) { if (markupAttributes == null) markupAttributes = new Properties(); markupAttributes.put(name, value); } /** * @see com.lowagie.text.MarkupAttributes#setMarkupAttributes(java.util.Properties) */ public void setMarkupAttributes(Properties markupAttributes) { this.markupAttributes = markupAttributes; } /** * @see com.lowagie.text.MarkupAttributes#getMarkupAttribute(java.lang.String) */ public String getMarkupAttribute(String name) { return (markupAttributes == null) ? null : String .valueOf(markupAttributes.get(name)); } /** * @see com.lowagie.text.MarkupAttributes#getMarkupAttributeNames() */ public Set getMarkupAttributeNames() { return Chunk.getKeySet(markupAttributes); } /** * @see com.lowagie.text.MarkupAttributes#getMarkupAttributes() */ public Properties getMarkupAttributes() { return markupAttributes; } /** * Tags this image with an ICC profile. * * @param profile * the profile */ public void tagICC(ICC_Profile profile) { this.profile = profile; } /** * Checks is the image has an ICC profile. * * @return the ICC profile or null */ public boolean hasICCProfile() { return (this.profile != null); } /** * Gets the images ICC profile. * * @return the ICC profile */ public ICC_Profile getICCProfile() { return profile; } /** * Getter for property deflated. * * @return Value of property deflated. * */ public boolean isDeflated() { return this.deflated; } /** * Setter for property deflated. * * @param deflated * New value of property deflated. * */ public void setDeflated(boolean deflated) { this.deflated = deflated; } /** * Getter for property indexed. * * @return Value of property indexed. * */ public PdfDictionary getAdditional() { return this.additional; } /** * Sets the /Colorspace key. * * @param additional * New value of property indexed. */ public void setAdditional(PdfDictionary additional) { this.additional = additional; } /** * Getter for property smask. * * @return Value of property smask. * */ public boolean isSmask() { return this.smask; } /** * Setter for property smask. * * @param smask * New value of property smask. * */ public void setSmask(boolean smask) { this.smask = smask; } /** * Gets the X/Y pixel dimensionless aspect ratio. * * @return the X/Y pixel dimensionless aspect ratio */ public float getXYRatio() { return this.XYRatio; } /** * Sets the X/Y pixel dimensionless aspect ratio. * * @param XYRatio * the X/Y pixel dimensionless aspect ratio */ public void setXYRatio(float XYRatio) { this.XYRatio = XYRatio; } /** * Gets the left indentation. * * @return the left indentation */ public float indentationLeft() { return indentationLeft; } /** * Gets the right indentation. * * @return the right indentation */ public float indentationRight() { return indentationRight; } /** * Sets the left indentation. * * @param f */ public void setIndentationLeft(float f) { indentationLeft = f; } /** * Sets the right indentation. * * @param f */ public void setIndentationRight(float f) { indentationRight = f; } /** * Getter for property originalType. * * @return Value of property originalType. * */ public int getOriginalType() { return this.originalType; } /** * Setter for property originalType. * * @param originalType * New value of property originalType. * */ public void setOriginalType(int originalType) { this.originalType = originalType; } /** * Getter for property originalData. * * @return Value of property originalData. * */ public byte[] getOriginalData() { return this.originalData; } /** * Setter for property originalData. * * @param originalData * New value of property originalData. * */ public void setOriginalData(byte[] originalData) { this.originalData = originalData; } /** * Sets the url of the image * * @param url * the url of the image */ public void setUrl(URL url) { this.url = url; } /** * Sets the spacing before this image. * * @param spacing * the new spacing */ public void setSpacingBefore(float spacing) { this.spacingBefore = spacing; } /** * Sets the spacing after this image. * * @param spacing * the new spacing */ public void setSpacingAfter(float spacing) { this.spacingAfter = spacing; } /** * Gets the spacing before this image. * * @return the spacing */ public float spacingBefore() { return spacingBefore; } /** * Gets the spacing before this image. * * @return the spacing */ public float spacingAfter() { return spacingAfter; } /** * Getter for property widthPercentage. * * @return Value of property widthPercentage. */ public float getWidthPercentage() { return this.widthPercentage; } /** * Setter for property widthPercentage. * * @param widthPercentage * New value of property widthPercentage. */ public void setWidthPercentage(float widthPercentage) { this.widthPercentage = widthPercentage; } /** * Gets the layer this image belongs to. * * @return the layer this image belongs to or null for no * layer defined */ public PdfOCG getLayer() { return layer; } /** * Sets the layer this image belongs to. * * @param layer * the layer this image belongs to */ public void setLayer(PdfOCG layer) { this.layer = layer; } private PdfObject simplifyColorspace(PdfObject obj) { if (obj == null || !obj.isArray()) return obj; PdfObject first = (PdfObject)(((PdfArray)obj).getArrayList().get(0)); if (PdfName.CALGRAY.equals(first)) return PdfName.DEVICEGRAY; else if (PdfName.CALRGB.equals(first)) return PdfName.DEVICERGB; else return obj; } /** * Replaces CalRGB and CalGray colorspaces with DeviceRGB and DeviceGray. */ public void simplifyColorspace() { if (additional == null) return; PdfObject value = additional.get(PdfName.COLORSPACE); if (value == null || !value.isArray()) return; PdfObject cs = simplifyColorspace(value); if (cs.isName()) value = cs; else { PdfObject first = (PdfObject)(((PdfArray)value).getArrayList().get(0)); if (PdfName.INDEXED.equals(first)) { ArrayList array = ((PdfArray)value).getArrayList(); if (array.size() >= 2 && ((PdfObject)array.get(1)).isArray()) { array.set(1, simplifyColorspace((PdfObject)array.get(1))); } } } additional.put(PdfName.COLORSPACE, value); } /** * Getter for property initialRotation. * @return Value of property initialRotation. */ public float getInitialRotation() { return this.initialRotation; } /** * Some image formats, like TIFF may present the images rotated that have * to be compensated. * @param initialRotation New value of property initialRotation. */ public void setInitialRotation(float initialRotation) { float old_rot = rotation - this.initialRotation; this.initialRotation = initialRotation; setRotation(old_rot); } /** * Getter for property directReference. * @return Value of property directReference. */ public PdfIndirectReference getDirectReference() { return this.directReference; } /** * Setter for property directReference. * @param directReference New value of property directReference. */ public void setDirectReference(PdfIndirectReference directReference) { this.directReference = directReference; } }