/*******************************************************************************
* Copyright (c) 2017 Original authors and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Thanh Liem PHAN (ALL4TEC) <thanhliem.phan@all4tec.net> - Bug 509361
******************************************************************************/
package org.eclipse.nebula.widgets.nattable.export.image;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.export.FileOutputStreamProvider;
import org.eclipse.nebula.widgets.nattable.export.IOutputStreamProvider;
import org.eclipse.nebula.widgets.nattable.export.ITableExporter;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
/**
* This class is used to export a NatTable to different types of image.
* Currently, 4 types of image are supported: BMP, JPEG, JPG, PNG. GIF is not
* supported due to its limitation on image depth described in the bug 38232.
*
* <p>
* <b>Warning:</b> Using this class is risky as it could cause severe damage to
* NatTables that show huge data sets (for example: a table with more than 20k
* rows).
* </p>
*
* @since 1.5
*/
public class ImageExporter implements ITableExporter {
private static final Log LOG = LogFactory.getLog(ImageExporter.class);
private static final String DEFAULT_IMAGE_NAME = "table_export.bmp"; //$NON-NLS-1$
private static final String BMP_FILTER_NAME = "BMP files (*.bmp)"; //$NON-NLS-1$
private static final String BMP_FILTER_EXT = "*.bmp"; //$NON-NLS-1$
private static final String JPEG_FILTER_NAME = "JPEG files (*.jpeg)"; //$NON-NLS-1$
private static final String JPEG_FILTER_EXT = "*.jpeg"; //$NON-NLS-1$
private static final String JPG_FILTER_NAME = "JPG files (*.jpg)"; //$NON-NLS-1$
private static final String JPG_FILTER_EXT = "*.jpg"; //$NON-NLS-1$
private static final String PNG_FILTER_NAME = "PNG files (*.png)"; //$NON-NLS-1$
private static final String PNG_FILTER_EXT = "*.png"; //$NON-NLS-1$
private static final String[] DEFAULT_FILTER_NAMES =
new String[] { BMP_FILTER_NAME, JPEG_FILTER_NAME, JPG_FILTER_NAME, PNG_FILTER_NAME };
private static final String[] DEFAULT_FILTER_EXTENSIONS =
new String[] { BMP_FILTER_EXT, JPEG_FILTER_EXT, JPG_FILTER_EXT, PNG_FILTER_EXT };
/**
* The {@link IOutputStreamProvider} used to create new output stream on
* beginning new export operation.
*/
private final IOutputStreamProvider outputStreamProvider;
/**
* Default constructor to create a new image exporter.
*/
public ImageExporter() {
this(new FileOutputStreamProvider(DEFAULT_IMAGE_NAME, DEFAULT_FILTER_NAMES, DEFAULT_FILTER_EXTENSIONS));
}
/**
* Create a new ImageExporter that uses the given IOutputStreamProvider for
* retrieving the OutputStream.
*
* @param outputStreamProvider
* The IOutputStreamProvider used to retrieve the OutputStream to
* write the export to.
*/
public ImageExporter(IOutputStreamProvider outputStreamProvider) {
this.outputStreamProvider = outputStreamProvider;
}
@Override
public Object getResult() {
return this.outputStreamProvider.getResult();
}
/**
* Export the given layer of the nattable to image. This method must be
* called after the execution of the
* {@link ImageExporter#getOutputStream(Shell)} method.
*
* @param shell
* The parent shell
* @param layer
* The layer to be exported
* @param configRegistry
* The configure registry of the nattable
*/
@Override
public void exportTable(Shell shell,
ProgressBar progressBar,
OutputStream outputStream,
ILayer layer,
IConfigRegistry configRegistry) throws IOException {
if (null == shell || null == layer || null == configRegistry) {
throw new IllegalArgumentException("Shell, layer or configure registry must not be null"); //$NON-NLS-1$
}
int width = layer.getWidth();
int height = layer.getHeight();
final Image image = new Image(shell.getDisplay(), width, height);
GC gc = new GC(image);
Rectangle layerBounds = new Rectangle(0, 0, width, height);
layer.getLayerPainter().paintLayer(layer, gc, 0, 0, layerBounds, configRegistry);
ImageLoader imageLoader = new ImageLoader();
imageLoader.data = new ImageData[] { image.getImageData() };
try {
if (this.outputStreamProvider instanceof FileOutputStreamProvider) {
final FileOutputStreamProvider fileOutputStreamProvider = (FileOutputStreamProvider) this.outputStreamProvider;
final String selectedFilterExt = DEFAULT_FILTER_EXTENSIONS[fileOutputStreamProvider.getExtensionFilterIndex()];
final int imageFormat = getImageFormatIndex(selectedFilterExt);
if (imageFormat >= 0) {
try {
imageLoader.save(outputStream, imageFormat);
} catch (SWTException e) {
LOG.error("Error while saving the result image", e); //$NON-NLS-1$
}
}
}
} finally {
gc.dispose();
image.dispose();
}
}
/**
* Get the image format index by the filter extension.
*
* @param selectedFilterExt
* The selected filter extension
* @return The image index or -1 if not found
*/
protected int getImageFormatIndex(String selectedFilterExt) {
int imageFormat = -1;
if (BMP_FILTER_EXT.equals(selectedFilterExt)) {
imageFormat = SWT.IMAGE_BMP;
} else if (JPEG_FILTER_EXT.equals(selectedFilterExt) || JPG_FILTER_EXT.equals(selectedFilterExt)) {
imageFormat = SWT.IMAGE_JPEG;
} else if (PNG_FILTER_EXT.equals(selectedFilterExt)) {
imageFormat = SWT.IMAGE_PNG;
}
return imageFormat;
}
@Override
public OutputStream getOutputStream(Shell shell) {
if (shell == null) {
throw new IllegalArgumentException("Shell must not be null"); //$NON-NLS-1$
}
return this.outputStreamProvider.getOutputStream(shell);
}
}