package com.griddynamics.jagger.webclient.client.components;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.event.dom.client.LoadEvent;
import com.google.gwt.event.dom.client.LoadHandler;
import com.google.gwt.user.client.ui.*;
import com.googlecode.gflot.client.Series;
import com.googlecode.gflot.client.SimplePlot;
/**
* Class that enables to save plots */
public class PlotSaver {
/**
* Frame to fire download */
private Frame hiddenFrame;
{
hiddenFrame = new Frame();
hiddenFrame.setVisible(false);
hiddenFrame.setPixelSize(0, 0);
RootPanel.get().add(hiddenFrame);
}
/**
* Saves plot as png
* @param plot plot to be saved
* @param plotHeader plot header
* @param xAxisLabel X axis label
*/
public void saveAsPng (final SimplePlot plot, final String plotHeader, final String xAxisLabel) {
if (plot.isExportAsImageEnabled()) {
Image im = plot.getImage();
im.addLoadHandler(new LoadHandler() {
@Override
public void onLoad(LoadEvent event) {
Image imageOfPlot = (Image) event.getSource();
drawCanvas(imageOfPlot, plot, plotHeader, xAxisLabel);
RootPanel.get().remove(imageOfPlot);
}
});
im.setVisible(false);
RootPanel.get().add(im);
} else {
alert("Can not save image in your browser.");
}
}
private void drawCanvas(Image imageOfPlot, SimplePlot plot, String plotHeader, String xAxisLabel) {
Canvas canvasTmp = Canvas.createIfSupported();
if (canvasTmp == null) {
alert("Can not create canvas element in your browser.");
return;
}
Context2d context = canvasTmp.getContext2d();
ImageElement imageElement = ImageElement.as(imageOfPlot.getElement());
// Width of plot image
int plotWidth = plot.getOffsetWidth();
// Height of plot image
int plotHeight = plot.getOffsetHeight();
// Font size of text in pixels
int fontSize = 13;
// Indent size in pixels. Used to separate all elements on final image and defines left indent of header or legend
int indent = 5;
JsArray<Series> series = plot.getModel().getSeries();
// Calculate max width of legend label to avoid overlay
int maxLegendLabelWidth = calculateLabelMaxWidth(series, context, fontSize);
int legendRectangleWidth = (int) (fontSize * 1.5);
int legendRectangleHeight = fontSize;
// Total width of one series legend including rectangle with color, and indents.
int oneSeriesLegendSize = legendRectangleWidth + 3 * indent + maxLegendLabelWidth;
// Number of columns in legend
int numberOfColumns = plotWidth / oneSeriesLegendSize;
if (numberOfColumns == 0)
numberOfColumns = 1;
int legendHeight = calculateLegendHeight(series, numberOfColumns, fontSize, indent);
// 2 * fontSize as we have Header of plot and X axis label
// 3 * indent as we have indent from top to header,
// indent between plot and header,
// indent between plot and X axis label.
int totalHeight = plotHeight + 2 * fontSize + 3 * indent + legendHeight;
canvasTmp.setWidth(plotWidth + "px");
canvasTmp.setHeight(totalHeight + "px");
canvasTmp.setCoordinateSpaceWidth(plotWidth);
canvasTmp.setCoordinateSpaceHeight(totalHeight);
int currentY = 0;
context.setFillStyle("black");
context.setFont("bold " + fontSize + "px sans-serif");
context.setTextAlign(Context2d.TextAlign.START);
currentY += fontSize + indent;
// add plot header
context.fillText(plotHeader, indent, currentY);
currentY += indent;
// add image of plot
context.drawImage(imageElement, 0.0, currentY);//, imageElement.getWidth(), imageElement.getHeight());
currentY += plotHeight + indent;
context.setFont(fontSize + "px sans-serif");
context.setTextAlign(Context2d.TextAlign.CENTER);
// add x axis label
context.fillText(xAxisLabel, plotWidth / 2, currentY + fontSize / 2);
currentY += fontSize + indent;
int currentX = 0;
for (int i = 0; i < series.length(); i++) {
Series s = series.get(i);
int curRecW = legendRectangleWidth;
int curRecH = legendRectangleHeight;
if (i % numberOfColumns == 0) {
currentX = indent;
} else {
currentX += indent + oneSeriesLegendSize;
}
// add rectangle with color
context.setFillStyle("gray");
context.fillRect(currentX, currentY, curRecW, curRecH);
context.setFillStyle("white");
curRecW -= 2;
curRecH -= 2;
context.fillRect(currentX + 1, currentY + 1, curRecW, curRecH);
context.setFillStyle(s.getColor());
curRecW -= 2;
curRecH -= 2;
context.fillRect(currentX + 2, currentY + 2, curRecW, curRecH);
context.setFillStyle("black");
context.setTextAlign(Context2d.TextAlign.START);
// add label of current series
context.fillText(s.getLabel(), currentX + legendRectangleWidth + indent, currentY + fontSize - 2);
if (i % numberOfColumns == numberOfColumns - 1) {
currentY += fontSize + indent;
}
}
// get url of png data created from canvas
String url = canvasTmp.toDataUrl("image/png");
// fire browser event to download png
hiddenFrame.setUrl(url.replace("image/png", "image/octet-stream"));
}
private void alert(String message) {
new ExceptionPanel(message);
}
private int calculateLegendHeight(JsArray<Series> series, int numberOfColumns, int fontSize, int delta) {
int legendHeight = 0;
for (int i = 0; i < series.length(); i++) {
if (i % numberOfColumns == 0)
legendHeight += fontSize + delta;
}
return legendHeight;
}
private int calculateLabelMaxWidth(JsArray<Series> series, Context2d context, int fontSize) {
context.setFont(fontSize + "px sans-serif");
int maxWidth = 0;
for (int i = 0; i < series.length(); i++) {
Series s = series.get(i);
double width = context.measureText(s.getLabel()).getWidth();
if (maxWidth < width)
maxWidth = (int)width;
}
return maxWidth;
}
}