Java tutorial
/******************************************************************************* * ${licenseText} * All rights reserved. This file is made available under the terms of the * Common Development and Distribution License (CDDL) v1.0 which accompanies * this distribution, and is available at * http://www.opensource.org/licenses/cddl1.txt *******************************************************************************/ package net.sf.mcf2pdf.mcfelements.util; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.ref.WeakReference; import javax.imageio.ImageIO; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.bridge.UserAgentAdapter; import org.apache.batik.bridge.ViewBox; import org.apache.batik.dom.svg.SAXSVGDocumentFactory; import org.apache.batik.ext.awt.RenderingHintsKeyExt; import org.apache.batik.gvt.GraphicsNode; import org.apache.batik.util.XMLResourceDescriptor; import org.apache.commons.io.IOUtils; import org.w3c.dom.svg.SVGDocument; import org.w3c.dom.svg.SVGPreserveAspectRatio; import com.drew.imaging.ImageMetadataReader; import com.drew.imaging.ImageProcessingException; import com.drew.metadata.Metadata; import com.drew.metadata.MetadataException; import com.drew.metadata.exif.ExifDirectory; /** * Utility class for working with images in the context of the mcf2pdf project. */ public final class ImageUtil { /* * FIXME this uses CEWE Fotobook for MY personal pictures when they do not * have resolution information. * Don't know if this is default, or just taken from other photos in that MCF file? */ private static final float DEFAULT_RESOLUTION = 180.0f; public static final float MM_PER_INCH = 25.4f; public static final double SQRT_2 = Math.sqrt(2); private ImageUtil() { } /** * Retrieves resolution information from the given image file. As CEWE algorithm seems to have changed, always returns default * resolution for JPEG files. * * @return An array containing the x- and the y-resolution, in dots per inch, of the given file. * * @throws IOException If any I/O related problem occurs reading the file. */ public static float[] getImageResolution(File imageFile) throws IOException { return new float[] { DEFAULT_RESOLUTION, DEFAULT_RESOLUTION }; } public static BufferedImage readImage(File imageFile) throws IOException { int rotation = getImageRotation(imageFile); BufferedImage img = ImageIO.read(imageFile); if (rotation == 0) { return img; } boolean swapXY = rotation != 180; BufferedImage rotated = new BufferedImage(swapXY ? img.getHeight() : img.getWidth(), swapXY ? img.getWidth() : img.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = rotated.createGraphics(); g2d.translate((rotated.getWidth() - img.getWidth()) / 2, (rotated.getHeight() - img.getHeight()) / 2); g2d.rotate(Math.toRadians(rotation), img.getWidth() / 2, img.getHeight() / 2); g2d.drawImage(img, 0, 0, null); g2d.dispose(); return rotated; } private static int getImageRotation(File imageFile) throws IOException { try { Metadata md = ImageMetadataReader.readMetadata(imageFile); ExifDirectory ed = (ExifDirectory) md.getDirectory(ExifDirectory.class); if (ed != null) { if (ed.containsTag(ExifDirectory.TAG_ORIENTATION)) { int o = ed.getInt(ExifDirectory.TAG_ORIENTATION); switch (o) { case 3: return 180; case 6: return 90; case 8: return 270; } } } return 0; } catch (ImageProcessingException e) { throw new IOException(e); } catch (MetadataException e) { return 0; } } /** * Loads the given CLP or SVG file and creates a BufferedImage with the given dimensions. As CLP files contain Vector images, * they can be scaled to every size needed. The contents are scaled to the given width and height, <b>not</b> preserving any * ratio of the image. * * @param clpFile CLP or SVG file. * @param widthPixel The width, in pixels, the resulting image shall have. * @param heightPixel The height, in pixels, the resulting image shall have. * * @return An image displaying the contents of the loaded CLP file. * * @throws IOException If any I/O related problem occurs reading the file. */ public static BufferedImage loadClpFile(File clpFile, int widthPixel, int heightPixel) throws IOException { FileInputStream fis = new FileInputStream(clpFile); ClpInputStream cis = null; InputStream in = clpFile.getName().toLowerCase().endsWith(".clp") ? (cis = new ClpInputStream(fis)) : fis; UserAgentAdapter userAgentAdapter = new UserAgentAdapter(); BridgeContext bridgeContext = new BridgeContext(userAgentAdapter); SVGDocument svgDocument; GraphicsNode rootSvgNode; try { String parser = XMLResourceDescriptor.getXMLParserClassName(); SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(parser); svgDocument = (SVGDocument) factory.createDocument(clpFile.toURI().toString(), new InputStreamReader(in, "ISO-8859-1")); rootSvgNode = getRootNode(svgDocument, bridgeContext); } finally { IOUtils.closeQuietly(cis); IOUtils.closeQuietly(fis); } float[] vb = ViewBox.parseViewBoxAttribute(svgDocument.getRootElement(), svgDocument.getRootElement().getAttribute("viewBox"), bridgeContext); AffineTransform usr2dev = ViewBox.getPreserveAspectRatioTransform(vb, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_NONE, true, widthPixel, heightPixel); BufferedImage img = new BufferedImage(widthPixel, heightPixel, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = img.createGraphics(); g2d.setColor(new Color(0.0f, 0.0f, 0.0f, 0.0f)); g2d.fillRect(0, 0, widthPixel, heightPixel); g2d.transform(usr2dev); // fixes "Graphics2D from BufferedImage lacks BUFFERED_IMAGE hint" - part 1 final Object oldBufferedImage = g2d.getRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE); g2d.setRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE, new WeakReference<BufferedImage>(img)); rootSvgNode.paint(g2d); // fixes "Graphics2D from BufferedImage lacks BUFFERED_IMAGE hint" - part 2 if (oldBufferedImage != null) g2d.setRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE, oldBufferedImage); else g2d.getRenderingHints().remove(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE); g2d.dispose(); return img; } private static GraphicsNode getRootNode(SVGDocument document, BridgeContext bridgeContext) { // Build the tree and get the document dimensions GVTBuilder builder = new GVTBuilder(); return builder.build(bridgeContext, document); } /** * Rotates the given buffered image by the given angle, and returns a newly * created image, containing the rotated image. * * @param img Image to rotate. * @param angle Angle, in radians, by which to rotate the image. * @param drawOffset Receives the offset which is required to draw the image, * relative to the original (0,0) corner, so that the center of the image is * still on the same position. * * @return A newly created image containing the rotated image. */ public static BufferedImage rotateImage(BufferedImage img, float angle, Point drawOffset) { int w = img.getWidth(); int h = img.getHeight(); AffineTransform tf = AffineTransform.getRotateInstance(angle, w / 2.0, h / 2.0); // get coordinates for all corners to determine real image size Point2D[] ptSrc = new Point2D[4]; ptSrc[0] = new Point(0, 0); ptSrc[1] = new Point(w, 0); ptSrc[2] = new Point(w, h); ptSrc[3] = new Point(0, h); Point2D[] ptTgt = new Point2D[4]; tf.transform(ptSrc, 0, ptTgt, 0, ptSrc.length); Rectangle rc = new Rectangle(0, 0, w, h); for (Point2D p : ptTgt) { if (p.getX() < rc.x) { rc.width += rc.x - p.getX(); rc.x = (int) p.getX(); } if (p.getY() < rc.y) { rc.height += rc.y - p.getY(); rc.y = (int) p.getY(); } if (p.getX() > rc.x + rc.width) rc.width = (int) (p.getX() - rc.x); if (p.getY() > rc.y + rc.height) rc.height = (int) (p.getY() - rc.y); } BufferedImage imgTgt = new BufferedImage(rc.width, rc.height, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = imgTgt.createGraphics(); // create a NEW rotation transformation around new center tf = AffineTransform.getRotateInstance(angle, rc.getWidth() / 2, rc.getHeight() / 2); g2d.setTransform(tf); g2d.drawImage(img, -rc.x, -rc.y, null); g2d.dispose(); drawOffset.x += rc.x; drawOffset.y += rc.y; return imgTgt; } }