package com.baselet.diagram.io;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Collection;
import javax.imageio.ImageIO;
import javax.swing.JLayeredPane;
import org.apache.batik.dom.GenericDOMImplementation;
import org.apache.batik.svggen.SVGGraphics2D;
import org.sourceforge.jlibeps.epsgraphics.EpsGraphics2D;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Element;
import com.baselet.control.basics.Converter;
import com.baselet.control.basics.geom.Dimension;
import com.baselet.control.basics.geom.Rectangle;
import com.baselet.control.config.Config;
import com.baselet.control.constants.Constants;
import com.baselet.control.enums.Program;
import com.baselet.control.util.Utils;
import com.baselet.diagram.DiagramHandler;
import com.baselet.diagram.DrawPanel;
import com.baselet.diagram.FontHandler;
import com.baselet.element.ElementFactorySwing;
import com.baselet.element.interfaces.GridElement;
import com.itextpdf.awt.FontMapper;
import com.itextpdf.awt.PdfGraphics2D;
import com.itextpdf.text.pdf.PdfWriter;
public class OutputHandler {
private OutputHandler() {} // private constructor to avoid instantiation
public static void createAndOutputToFile(String extension, File file, DiagramHandler handler) throws Exception {
OutputStream ostream = new FileOutputStream(file);
createToStream(extension, ostream, handler);
ostream.close();
}
public static void createToStream(String extension, OutputStream ostream, DiagramHandler handler) throws Exception {
int oldZoom = handler.getGridSize();
handler.setGridAndZoom(Constants.DEFAULTGRIDSIZE, false); // Zoom to the defaultGridsize before execution
// if some GridElements are selected, only export them
Collection<GridElement> elementsToDraw = handler.getDrawPanel().getSelector().getSelectedElements();
// if nothing is selected, draw everything
if (elementsToDraw.isEmpty()) {
elementsToDraw = handler.getDrawPanel().getGridElements();
}
OutputHandler.exportToOutputStream(extension, ostream, elementsToDraw, handler.getFontHandler());
handler.setGridAndZoom(oldZoom, false); // Zoom back to the oldGridsize after execution
}
private static void exportToOutputStream(String extension, OutputStream ostream, Collection<GridElement> entities, FontHandler diagramFont) throws IOException {
for (GridElement ge : entities) {
ge.getDeprecatedAddons().doBeforeExport();
}
if (extension.equals("eps")) {
exportEps(ostream, entities, diagramFont);
}
else if (extension.equals("pdf")) {
exportPdf(ostream, entities, diagramFont);
}
else if (extension.equals("svg")) {
exportSvg(ostream, entities, diagramFont);
}
else if (isImageExtension(extension)) {
exportImg(extension, ostream, entities, diagramFont);
}
else {
throw new IllegalArgumentException(extension + " is an invalid format");
}
}
private static void exportEps(OutputStream ostream, Collection<GridElement> entities, FontHandler diagramFont) throws IOException {
Rectangle bounds = DrawPanel.getContentBounds(Config.getInstance().getPrintPadding(), entities);
EpsGraphics2D graphics2d = new EpsGraphics2D(Program.getInstance().getProgramName() + " Diagram", ostream, 0, 0, bounds.width, bounds.height);
setGraphicsBorders(bounds, graphics2d);
paintEntitiesIntoGraphics2D(graphics2d, entities, diagramFont);
graphics2d.flush();
graphics2d.close();
}
private static void exportPdf(OutputStream ostream, Collection<GridElement> entities, FontHandler diagramFont) throws IOException {
try {
FontMapper mapper = new PdfFontMapper();
Rectangle bounds = DrawPanel.getContentBounds(Config.getInstance().getPrintPadding(), entities);
com.itextpdf.text.Document document = new com.itextpdf.text.Document(new com.itextpdf.text.Rectangle(bounds.getWidth(), bounds.getHeight()));
PdfWriter writer = PdfWriter.getInstance(document, ostream);
document.open();
Graphics2D graphics2d = new PdfGraphics2D(writer.getDirectContent(), bounds.getWidth(), bounds.getHeight(), mapper);
// We shift the diagram to the upper left corner, so we shift it by (minX,minY) of the contextBounds
Dimension trans = new Dimension(bounds.getX(), bounds.getY());
graphics2d.translate(-trans.getWidth(), -trans.getHeight());
paintEntitiesIntoGraphics2D(graphics2d, entities, diagramFont);
graphics2d.dispose();
document.close();
} catch (com.itextpdf.text.DocumentException e) {
throw new IOException(e.getMessage());
}
}
private static void exportSvg(OutputStream ostream, Collection<GridElement> entities, FontHandler diagramFont) throws IOException {
Rectangle bounds = DrawPanel.getContentBounds(Config.getInstance().getPrintPadding(), entities);
DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();
org.w3c.dom.Document document = domImpl.createDocument(null, "svg", null);
SVGGraphics2D graphics2d = new SVGGraphics2D(document);
graphics2d.setSVGCanvasSize(Converter.convert(bounds.getSize()));
paintEntitiesIntoGraphics2D(graphics2d, entities, diagramFont);
Element root = graphics2d.getRoot();
root.setAttributeNS(null, "viewBox", String.format("%d %d %d %d", bounds.x, bounds.y, bounds.width, bounds.height));
Writer out = new OutputStreamWriter(ostream, "UTF-8"); // Stream out SVG to the standard output using UTF-8 character to byte encoding
graphics2d.stream(root, out, false, false);
graphics2d.dispose();
}
private static void exportImg(String imgType, OutputStream ostream, Collection<GridElement> entities, FontHandler diagramFont) throws IOException {
ImageIO.write(createImageForGridElements(entities, diagramFont), imgType, ostream);
ostream.flush();
ostream.close();
}
public static BufferedImage createImageForGridElements(Collection<GridElement> entities, FontHandler diagramFont) {
Rectangle bounds = DrawPanel.getContentBounds(Config.getInstance().getPrintPadding(), entities);
BufferedImage im = new BufferedImage(bounds.width == 0 ? 1 : bounds.width, bounds.height == 0 ? 1 : bounds.height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2d = im.createGraphics();
graphics2d.setRenderingHints(Utils.getUxRenderingQualityHigh(true));
setGraphicsBorders(bounds, graphics2d);
paintEntitiesIntoGraphics2D(graphics2d, entities, diagramFont);
graphics2d.dispose();
return im;
}
private static void setGraphicsBorders(Rectangle bounds, Graphics2D graphics2d) {
graphics2d.translate(-bounds.x, -bounds.y);
graphics2d.clipRect(bounds.x, bounds.y, bounds.width, bounds.height);
graphics2d.setColor(Color.white);
graphics2d.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
private static boolean isImageExtension(String ext) {
return ImageIO.getImageWritersBySuffix(ext).hasNext();
}
public static void paintEntitiesIntoGraphics2D(Graphics2D g2d, Collection<GridElement> entities, FontHandler diagramFont) {
DiagramHandler handler = DiagramHandler.forExport(diagramFont); // #290: pass fontHandler from original diagramHandler to let the export use diagram specific fontsize+family
JLayeredPane tempPanel = new JLayeredPane();
for (GridElement entity : entities) {
GridElement clone = ElementFactorySwing.createCopy(entity, handler);
com.baselet.element.interfaces.Component component = clone.getComponent();
// Issue 138: when PDF and Swing Export draw on (0,0) a part of the drawn image is cut, therefore it's displaced by 0.5px in that case.
// also Issue 270: makes arrow ending placement better
component.translateForExport();
tempPanel.add((Component) component, clone.getLayer());
}
tempPanel.validate();
tempPanel.setBackground(Color.WHITE);
tempPanel.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
tempPanel.update(g2d);
}
}