diff options
Diffstat (limited to 'reportbuilder/java/com/sun/star/report/pentaho/output/ImageProducer.java')
-rw-r--r-- | reportbuilder/java/com/sun/star/report/pentaho/output/ImageProducer.java | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/reportbuilder/java/com/sun/star/report/pentaho/output/ImageProducer.java b/reportbuilder/java/com/sun/star/report/pentaho/output/ImageProducer.java new file mode 100644 index 000000000000..b3d6485d9195 --- /dev/null +++ b/reportbuilder/java/com/sun/star/report/pentaho/output/ImageProducer.java @@ -0,0 +1,498 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +package com.sun.star.report.pentaho.output; + +import com.sun.star.report.ImageService; +import com.sun.star.report.InputRepository; +import com.sun.star.report.OutputRepository; +import com.sun.star.report.ReportExecutionException; +import com.sun.star.report.pentaho.DefaultNameGenerator; + +import java.awt.Dimension; +import java.awt.Image; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; + +import java.sql.Blob; +import java.sql.SQLException; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.jfree.layouting.input.style.values.CSSNumericType; +import org.jfree.layouting.input.style.values.CSSNumericValue; + +import org.pentaho.reporting.libraries.base.util.IOUtils; +import org.pentaho.reporting.libraries.base.util.PngEncoder; +import org.pentaho.reporting.libraries.base.util.WaitingImageObserver; + + +/** + * This class manages the images embedded in a report. + * + * @author Thomas Morgner + * @since 31.03.2007 + */ +public class ImageProducer +{ + + private static final Log LOGGER = LogFactory.getLog(ImageProducer.class); + + public static class OfficeImage + { + + private final CSSNumericValue width; + private final CSSNumericValue height; + private final String embeddableLink; + + public OfficeImage(final String embeddableLink, final CSSNumericValue width, final CSSNumericValue height) + { + this.embeddableLink = embeddableLink; + this.width = width; + this.height = height; + } + + public CSSNumericValue getWidth() + { + return width; + } + + public CSSNumericValue getHeight() + { + return height; + } + + public String getEmbeddableLink() + { + return embeddableLink; + } + } + + private static class ByteDataImageKey + { + + private final byte[] keyData; + private Integer hashCode; + + protected ByteDataImageKey(final byte[] keyData) + { + if (keyData == null) + { + throw new NullPointerException(); + } + this.keyData = keyData; + } + + public boolean equals(final Object o) + { + if (this != o) + { + if (o == null || getClass() != o.getClass()) + { + return false; + } + + final ByteDataImageKey key = (ByteDataImageKey) o; + if (!Arrays.equals(keyData, key.keyData)) + { + return false; + } + } + + return true; + } + + public int hashCode() + { + if (hashCode != null) + { + return hashCode; + } + + final int length = Math.min(keyData.length, 512); + int hashValue = 0; + for (int i = 0; i < length; i++) + { + final byte b = keyData[i]; + hashValue = b + hashValue * 23; + } + this.hashCode = hashValue; + return hashValue; + } + } + private final Map imageCache; + private final InputRepository inputRepository; + private final OutputRepository outputRepository; + private final ImageService imageService; + + public ImageProducer(final InputRepository inputRepository, + final OutputRepository outputRepository, + final ImageService imageService) + { + if (inputRepository == null) + { + throw new NullPointerException(); + } + if (outputRepository == null) + { + throw new NullPointerException(); + } + if (imageService == null) + { + throw new NullPointerException(); + } + + this.inputRepository = inputRepository; + this.outputRepository = outputRepository; + this.imageService = imageService; + this.imageCache = new HashMap(); + } + + /** + * Image-Data can be one of the following types: String, URL, URI, byte-array, blob. + * + * @param imageData + * @param preserveIRI + * @return + */ + public OfficeImage produceImage(final Object imageData, + final boolean preserveIRI) + { + + LOGGER.debug("Want to produce image " + imageData); + if (imageData instanceof String) + { + return produceFromString((String) imageData, preserveIRI); + } + + if (imageData instanceof URL) + { + return produceFromURL((URL) imageData, preserveIRI); + } + + if (imageData instanceof Blob) + { + return produceFromBlob((Blob) imageData); + } + + if (imageData instanceof byte[]) + { + return produceFromByteArray((byte[]) imageData); + } + + if (imageData instanceof Image) + { + return produceFromImage((Image) imageData); + } + // not usable .. + return null; + } + + private OfficeImage produceFromImage(final Image image) + { + // quick caching ... use a weak list ... + final WaitingImageObserver obs = new WaitingImageObserver(image); + obs.waitImageLoaded(); + + final PngEncoder encoder = new PngEncoder(image, PngEncoder.ENCODE_ALPHA, PngEncoder.FILTER_NONE, 5); + final byte[] data = encoder.pngEncode(); + return produceFromByteArray(data); + } + + private OfficeImage produceFromBlob(final Blob blob) + { + try + { + final InputStream inputStream = blob.getBinaryStream(); + final int length = (int) blob.length(); + + final ByteArrayOutputStream bout = new ByteArrayOutputStream(length); + try + { + IOUtils.getInstance().copyStreams(inputStream, bout); + } finally + { + inputStream.close(); + } + return produceFromByteArray(bout.toByteArray()); + } + catch (IOException e) + { + LOGGER.warn("Failed to produce image from Blob", e); + } + catch (SQLException e) + { + LOGGER.warn("Failed to produce image from Blob", e); + } + return null; + } + + private OfficeImage produceFromByteArray(final byte[] data) + { + final ByteDataImageKey imageKey = new ByteDataImageKey(data); + final OfficeImage o = (OfficeImage) imageCache.get(imageKey); + if (o != null) + { + return o; + } + + try + { + final String mimeType = imageService.getMimeType(data); + final Dimension dims = imageService.getImageSize(data); + + // copy the image into the local output-storage + // todo: Implement data-fingerprinting so that we can detect the mime-type + final OutputRepository storage = outputRepository.openOutputRepository("Pictures", null); + final DefaultNameGenerator nameGenerator = new DefaultNameGenerator(storage); + final String name = nameGenerator.generateName("image", mimeType); + final OutputStream outputStream = storage.createOutputStream(name, mimeType); + final ByteArrayInputStream bin = new ByteArrayInputStream(data); + + try + { + IOUtils.getInstance().copyStreams(bin, outputStream); + } finally + { + outputStream.close(); + storage.closeOutputRepository(); + } + + final CSSNumericValue widthVal = CSSNumericValue.createValue(CSSNumericType.MM, dims.getWidth() / 100.0); + final CSSNumericValue heightVal = CSSNumericValue.createValue(CSSNumericType.MM, dims.getHeight() / 100.0); + final OfficeImage officeImage = new OfficeImage("Pictures/" + name, widthVal, heightVal); + imageCache.put(imageKey, officeImage); + return officeImage; + } + catch (IOException e) + { + LOGGER.warn("Failed to load image from local input-repository", e); + } + catch (ReportExecutionException e) + { + LOGGER.warn("Failed to create image from local input-repository", e); + } + return null; + } + + private OfficeImage produceFromString(final String source, + final boolean preserveIRI) + { + + try + { + final URL url = new URL(source); + return produceFromURL(url, preserveIRI); + } + catch (MalformedURLException e) + { + // ignore .. but we had to try this .. + } + + final OfficeImage o = (OfficeImage) imageCache.get(source); + if (o != null) + { + return o; + } + + // Next, check whether this is a local path. + if (inputRepository.isReadable(source)) + { + // cool, the file exists. Let's try to read it. + try + { + final ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); + final InputStream inputStream = inputRepository.createInputStream(source); + try + { + IOUtils.getInstance().copyStreams(inputStream, bout); + } finally + { + inputStream.close(); + } + final byte[] data = bout.toByteArray(); + final Dimension dims = imageService.getImageSize(data); + final String mimeType = imageService.getMimeType(data); + + final CSSNumericValue widthVal = CSSNumericValue.createValue(CSSNumericType.MM, dims.getWidth() / 100.0); + final CSSNumericValue heightVal = CSSNumericValue.createValue(CSSNumericType.MM, dims.getHeight() / 100.0); + + final String filename = copyToOutputRepository(mimeType, data); + final OfficeImage officeImage = new OfficeImage(filename, widthVal, heightVal); + imageCache.put(source, officeImage); + return officeImage; + } + catch (IOException e) + { + LOGGER.warn("Failed to load image from local input-repository", e); + } + catch (ReportExecutionException e) + { + LOGGER.warn("Failed to create image from local input-repository", e); + } + } + else + { + try + { + URI rootURI = new URI(inputRepository.getRootURL()); + final URI uri = rootURI.resolve(source); + return produceFromURL(uri.toURL(), preserveIRI); + } + catch (URISyntaxException ex) + { + } + catch (MalformedURLException e) + { + // ignore .. but we had to try this .. + } + } + + // Return the image as broken image instead .. + final OfficeImage officeImage = new OfficeImage(source, null, null); + imageCache.put(source, officeImage); + return officeImage; + } + + private OfficeImage produceFromURL(final URL url, + final boolean preserveIRI) + { + final String urlString = url.toString(); + URI uri = null; + try + { + uri = new URI(urlString); + } + catch (URISyntaxException ex) + { + Logger.getLogger(ImageProducer.class.getName()).log(Level.SEVERE, null, ex); + } + final OfficeImage o = (OfficeImage) imageCache.get(uri); + if (o != null) + { + return o; + } + + try + { + final ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); + final URLConnection urlConnection = url.openConnection(); + final InputStream inputStream = new BufferedInputStream(urlConnection.getInputStream()); + try + { + IOUtils.getInstance().copyStreams(inputStream, bout); + } finally + { + inputStream.close(); + } + final byte[] data = bout.toByteArray(); + + final Dimension dims = imageService.getImageSize(data); + final String mimeType = imageService.getMimeType(data); + final CSSNumericValue widthVal = CSSNumericValue.createValue(CSSNumericType.MM, dims.getWidth() / 100.0); + final CSSNumericValue heightVal = CSSNumericValue.createValue(CSSNumericType.MM, dims.getHeight() / 100.0); + + if (preserveIRI) + { + final OfficeImage retval = new OfficeImage(urlString, widthVal, heightVal); + imageCache.put(uri, retval); + return retval; + } + + final String name = copyToOutputRepository(mimeType, data); + final OfficeImage officeImage = new OfficeImage(name, widthVal, heightVal); + imageCache.put(uri, officeImage); + return officeImage; + } + catch (IOException e) + { + LOGGER.warn("Failed to load image from local input-repository" + e); + } + catch (ReportExecutionException e) + { + LOGGER.warn("Failed to create image from local input-repository" + e); + } + + if (!preserveIRI) + { + final OfficeImage image = new OfficeImage(urlString, null, null); + imageCache.put(uri, image); + return image; + } + + // OK, everything failed; the image is not - repeat it - not usable. + return null; + } + + private String copyToOutputRepository(final String urlMimeType, final byte[] data) + throws IOException, ReportExecutionException + { + final String mimeType; + if (urlMimeType == null) + { + mimeType = imageService.getMimeType(data); + } + else + { + mimeType = urlMimeType; + } + + // copy the image into the local output-storage + final OutputRepository storage = outputRepository.openOutputRepository("Pictures", null); + final DefaultNameGenerator nameGenerator = new DefaultNameGenerator(storage); + final String name = nameGenerator.generateName("image", mimeType); + final OutputStream outputStream = storage.createOutputStream(name, mimeType); + final ByteArrayInputStream bin = new ByteArrayInputStream(data); + + try + { + IOUtils.getInstance().copyStreams(bin, outputStream); + } finally + { + outputStream.close(); + storage.closeOutputRepository(); + } + return "Pictures/" + name; + } +} |