1
0
mirror of https://github.com/octoleo/plantuml.git synced 2024-06-06 18:30:52 +00:00
plantuml/src/net/sourceforge/plantuml/cucadiagram/dot/CucaDiagramFileMaker.java
2011-04-19 18:50:40 +02:00

1154 lines
43 KiB
Java

/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML 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 for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6474 $
*
*/
package net.sourceforge.plantuml.cucadiagram.dot;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.geom.Dimension2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import net.sourceforge.plantuml.ColorParam;
import net.sourceforge.plantuml.Dimension2DDouble;
import net.sourceforge.plantuml.EmptyImageBuilder;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.FileUtils;
import net.sourceforge.plantuml.FontParam;
import net.sourceforge.plantuml.ISkinParam;
import net.sourceforge.plantuml.Log;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.Scale;
import net.sourceforge.plantuml.SkinParamBackcolored;
import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.UmlDiagramType;
import net.sourceforge.plantuml.UniqueSequence;
import net.sourceforge.plantuml.cucadiagram.CucaDiagram;
import net.sourceforge.plantuml.cucadiagram.Entity;
import net.sourceforge.plantuml.cucadiagram.EntityType;
import net.sourceforge.plantuml.cucadiagram.IEntity;
import net.sourceforge.plantuml.cucadiagram.Imaged;
import net.sourceforge.plantuml.cucadiagram.Link;
import net.sourceforge.plantuml.cucadiagram.Stereotype;
import net.sourceforge.plantuml.eps.EpsGraphics;
import net.sourceforge.plantuml.eps.EpsStrategy;
import net.sourceforge.plantuml.eps.EpsTitler;
import net.sourceforge.plantuml.eps.SvgToEpsConverter;
import net.sourceforge.plantuml.graphic.CircledCharacter;
import net.sourceforge.plantuml.graphic.GraphicStrings;
import net.sourceforge.plantuml.graphic.HorizontalAlignement;
import net.sourceforge.plantuml.graphic.HtmlColor;
import net.sourceforge.plantuml.graphic.StringBounder;
import net.sourceforge.plantuml.graphic.StringBounderUtils;
import net.sourceforge.plantuml.graphic.VerticalPosition;
import net.sourceforge.plantuml.png.PngFlashcoder;
import net.sourceforge.plantuml.png.PngIO;
import net.sourceforge.plantuml.png.PngRotation;
import net.sourceforge.plantuml.png.PngScaler;
import net.sourceforge.plantuml.png.PngSizer;
import net.sourceforge.plantuml.png.PngTitler;
import net.sourceforge.plantuml.skin.CircleInterface;
import net.sourceforge.plantuml.skin.Component;
import net.sourceforge.plantuml.skin.ComponentType;
import net.sourceforge.plantuml.skin.SimpleContext2D;
import net.sourceforge.plantuml.skin.StickMan;
import net.sourceforge.plantuml.skin.rose.Rose;
import net.sourceforge.plantuml.statediagram.StateDiagram;
import net.sourceforge.plantuml.svg.SvgTitler;
import net.sourceforge.plantuml.ugraphic.eps.UGraphicEps;
import net.sourceforge.plantuml.ugraphic.g2d.UGraphicG2d;
import net.sourceforge.plantuml.ugraphic.svg.UGraphicSvg;
public final class CucaDiagramFileMaker {
private final CucaDiagram diagram;
private final List<BufferedImage> flashcodes;
private final StaticFilesMap staticFilesMap;
private final Rose rose = new Rose();
static private final StringBounder stringBounder;
static {
final EmptyImageBuilder builder = new EmptyImageBuilder(10, 10, Color.WHITE);
stringBounder = StringBounderUtils.asStringBounder(builder.getGraphics2D());
}
public CucaDiagramFileMaker(CucaDiagram diagram, List<BufferedImage> flashcodes) throws IOException {
HtmlColor.setForceMonochrome(diagram.getSkinParam().isMonochrome());
this.diagram = diagram;
this.flashcodes = flashcodes;
if (diagram.getUmlDiagramType() == UmlDiagramType.CLASS || diagram.getUmlDiagramType() == UmlDiagramType.OBJECT) {
this.staticFilesMap = new StaticFilesMap(diagram.getSkinParam(), diagram.getDpiFactor(null));
} else {
this.staticFilesMap = null;
}
}
static private void traceDotString(String dotString) throws IOException {
final File f = new File("dottmpfile" + UniqueSequence.getValue() + ".tmp");
PrintWriter pw = null;
try {
pw = new PrintWriter(new FileWriter(f));
pw.print(dotString);
Log.info("Creating file " + f);
} finally {
if (pw != null) {
pw.close();
}
}
}
public String createFile(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
throws IOException, InterruptedException {
final FileFormat fileFormat = fileFormatOption.getFileFormat();
if (fileFormat == FileFormat.PNG) {
return createPng(os, dotStrings, fileFormatOption);
} else if (fileFormat == FileFormat.SVG) {
return createSvg(os, dotStrings, fileFormatOption);
} else if (fileFormat == FileFormat.EPS_VIA_SVG) {
return createEpsViaSvg(os, dotStrings, fileFormatOption);
} else if (fileFormat == FileFormat.EPS) {
return createEps(os, dotStrings, fileFormatOption);
} else if (fileFormat == FileFormat.DOT) {
return createDot(os, dotStrings, fileFormatOption);
} else {
throw new UnsupportedOperationException();
}
}
private String createEpsViaSvg(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
throws IOException, InterruptedException {
final File svgTmp = FileUtils.createTempFile("svgtmp", ".svg");
final FileOutputStream svgOs = new FileOutputStream(svgTmp);
final String status = createSvg(svgOs, dotStrings, fileFormatOption);
svgOs.close();
final SvgToEpsConverter converter = new SvgToEpsConverter(svgTmp);
converter.createEps(os);
return status;
}
private double deltaY;
private String createSvg(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
throws IOException, InterruptedException {
final StringBuilder cmap = new StringBuilder();
try {
deltaY = 0;
String svg = getSvgData(dotStrings, fileFormatOption, cmap);
final Dimension2D dim = getDimensionSvg(svg);
if (dim != null) {
double supH = getTitleSvgHeight();
supH += getHeaderSvgHeight();
supH += getFooterSvgHeight();
svg = removeSvgXmlHeader(svg, dim.getWidth(), dim.getHeight() + supH);
svg = addTitleSvg(svg, dim.getWidth(), dim.getHeight());
svg = addHeaderSvg(svg, dim.getWidth(), dim.getHeight());
svg = addFooterSvg(svg, dim.getWidth(), dim.getHeight());
// Image management
final Pattern pImage = Pattern.compile("(?i)<image\\W[^>]*>");
boolean changed;
do {
changed = false;
final Matcher mImage = pImage.matcher(svg);
final StringBuffer sb = new StringBuffer();
while (mImage.find()) {
final String image = mImage.group(0);
final String href = getValue(image, "href");
final double widthSvg = Double.parseDouble(getValuePx(image, "width"));
final double heightSvg = Double.parseDouble(getValuePx(image, "height"));
final double x = Double.parseDouble(getValue(image, "x"));
final double y = Double.parseDouble(getValue(image, "y"));
final DrawFile drawFile = getDrawFileFromHref(href);
if (drawFile == null) {
mImage.appendReplacement(sb, image);
} else {
final int widthPng = drawFile.getWidthPng();
final int heightPng = drawFile.getHeightPng();
String svg2 = drawFile.getSvg();
final String scale = getScale(widthSvg, heightSvg, widthPng, heightPng);
svg2 = svg2.replaceFirst("<[gG]>", "<g transform=\"translate(" + 0 + " " + 0 + ") " + scale
+ "\">");
svg2 = "<svg x=\"" + x + "\" y=\"" + y + "\">" + svg2 + "</svg>";
mImage.appendReplacement(sb, svg2);
changed = true;
}
}
mImage.appendTail(sb);
svg = sb.toString();
} while (changed);
}
os.write(svg.getBytes("UTF-8"));
// final ByteArrayInputStream bais = new
// ByteArrayInputStream(baos.toByteArray());
// BufferedImage im = ImageIO.read(bais);
// bais.close();
// if (isUnderline) {
// new UnderlineTrick(im, new Color(Integer.parseInt("FEFECF", 16)),
// Color.BLACK).process();
// }
//
// final Color background =
// diagram.getSkinParam().getBackgroundColor().getColor();
// im = addTitle(im, background);
// im = addFooter(im, background);
// im = addHeader(im, background);
//
// if (diagram.isRotation()) {
// im = PngRotation.process(im);
// }
// im = PngSizer.process(im, diagram.getMinwidth());
//
// PngIO.write(im, os, diagram.getMetadata());
} finally {
// cleanTemporaryFiles(diagram.entities().values());
// cleanTemporaryFiles(diagram.getLinks());
}
if (cmap.length() > 0) {
return translateXY(cmap.toString(), 0, (int) Math.round(deltaY));
}
return null;
}
static String translateXY(String cmap, int deltaX, int deltaY) {
if (deltaY == 0) {
return cmap;
}
final Pattern p = Pattern.compile("coords=\"(\\d+),(\\d+),(\\d+),(\\d+)\"");
final Matcher m = p.matcher(cmap);
final StringBuffer sb = new StringBuffer();
while (m.find()) {
final int x1 = Integer.parseInt(m.group(1)) + deltaX;
final int y1 = Integer.parseInt(m.group(2)) + deltaY;
final int x2 = Integer.parseInt(m.group(3)) + deltaX;
final int y2 = Integer.parseInt(m.group(4)) + deltaY;
m.appendReplacement(sb, "coords=\"" + x1 + "," + y1 + "," + x2 + "," + y2 + "\"");
}
m.appendTail(sb);
return sb.toString();
}
private String getSvgData(List<String> dotStrings, FileFormatOption fileFormatOption, StringBuilder cmap)
throws IOException, InterruptedException, UnsupportedEncodingException {
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
final String dotString = dotMaker.createDotString();
if (OptionFlags.getInstance().isKeepTmpFiles()) {
traceDotString(dotString);
}
// if (diagram.hasUrl()) {
// final Graphviz graphviz = GraphvizUtils.create(dotString, "cmapx",
// "svg");
//
// final ByteArrayOutputStream baos = new ByteArrayOutputStream();
// graphviz.createPng(baos);
// baos.close();
// dotMaker.clean();
//
// final String cmapAndSvg = new String(baos.toByteArray(),
// "UTF-8").replace('\\', '/');
// final int x = cmapAndSvg.indexOf("<?xml ");
// if (x == -1) {
// throw new IllegalStateException();
// }
// cmap.append(cmapAndSvg.substring(0, x));
// return cmapAndSvg.substring(x);
//
// }
final Graphviz graphviz = GraphvizUtils.create(dotString, "svg");
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
graphviz.createPng(baos);
baos.close();
dotMaker.clean();
return new String(baos.toByteArray(), "UTF-8").replace('\\', '/');
}
private static String removeSvgXmlHeader(String svg, double width, double height) {
final String newString = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" style=\"width:"
+ Math.round(width) + ";height:" + Math.round(height) + ";\">";
svg = svg.replaceFirst("(?i)<svg[^>]*>", newString);
return svg;
}
private Dimension2D getDimensionSvg(String svg) {
final Pattern p = Pattern.compile("(?i)<polygon\\s+[^>]*points=\"([^\"]+)\"");
final Matcher m = p.matcher(svg);
if (m.find() == false) {
return null;
}
final String points = m.group(1);
final StringTokenizer st = new StringTokenizer(points, " ");
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxX = -Double.MAX_VALUE;
double maxY = -Double.MAX_VALUE;
while (st.hasMoreTokens()) {
final String token = st.nextToken();
final StringTokenizer st2 = new StringTokenizer(token, ",");
final double x = Double.parseDouble(st2.nextToken().trim());
final double y = Double.parseDouble(st2.nextToken().trim());
if (x < minX) {
minX = x;
}
if (y < minY) {
minY = y;
}
if (x > maxX) {
maxX = x;
}
if (y > maxY) {
maxY = y;
}
}
return new Dimension2DDouble(maxX - minX, maxY - minY);
}
private DrawFile searchImageFile(File searched, Collection<? extends IEntity> entities) throws IOException {
for (IEntity ent : entities) {
final DrawFile df = ent.getImageFile(searched);
if (df != null) {
assert df.getPng().getCanonicalFile().equals(searched.getCanonicalFile());
return df;
}
}
return null;
}
private DrawFile getDrawFileFromHref(final String href) throws IOException {
if (diagram.getUmlDiagramType() == UmlDiagramType.CLASS || diagram.getUmlDiagramType() == UmlDiagramType.OBJECT) {
final DrawFile drawFile = staticFilesMap.getDrawFile(href);
if (drawFile != null) {
return drawFile;
}
}
final File searched = new File(href).getCanonicalFile();
final DrawFile result = searchImageFile(searched, diagram.entities().values());
if (result != null) {
return result;
}
for (Link ent : diagram.getLinks()) {
final DrawFile df = ent.getImageFile();
if (df == null) {
continue;
}
if (df.getPng().getCanonicalFile().equals(searched)) {
return df;
}
}
Log.error("Cannot find " + href);
return null;
}
private static String getScale(double widthSvg, double heightSvg, double widthPng, double heightPng) {
final double v1 = heightSvg / heightPng;
final double v2 = widthSvg / widthPng;
final double min = Math.min(v1, v2);
return "scale(" + min + " " + min + ")";
}
private static String getValue(String s, String param) {
final Pattern p = Pattern.compile("(?i)" + param + "=\"([^\"]+)\"");
final Matcher m = p.matcher(s);
m.find();
return m.group(1);
}
private static String getValuePx(String s, String param) {
final Pattern p = Pattern.compile("(?i)" + param + "=\"([^\"]+?)(?:px)?\"");
final Matcher m = p.matcher(s);
m.find();
return m.group(1);
}
private String createPng(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
throws IOException, InterruptedException {
final StringBuilder cmap = new StringBuilder();
double supX = 0;
double supY = 0;
try {
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
final String dotString = dotMaker.createDotString();
if (OptionFlags.getInstance().isKeepTmpFiles()) {
traceDotString(dotString);
}
final byte[] imageData = getImageData(dotString, cmap);
if (imageData.length == 0) {
createError(os, imageData.length, new FileFormatOption(FileFormat.PNG), "imageData.length == 0");
return null;
}
if (isPngHeader(imageData, 0) == false) {
createError(os, imageData.length, new FileFormatOption(FileFormat.PNG), "No PNG header found",
"Try -forcegd or -forcecairo flag");
return null;
}
final ByteArrayInputStream bais = new ByteArrayInputStream(imageData);
BufferedImage im = ImageIO.read(bais);
if (im == null) {
createError(os, imageData.length, new FileFormatOption(FileFormat.PNG), "im == null");
return null;
}
bais.close();
dotMaker.clean();
final boolean isUnderline = dotMaker.isUnderline();
if (isUnderline) {
new UnderlineTrick(im, new Color(Integer.parseInt("FEFECF", 16)), Color.BLACK).process();
}
final Color background = diagram.getSkinParam().getBackgroundColor().getColor();
supY = getTitlePngHeight(stringBounder);
supY += getHeaderPngHeight(stringBounder);
supX = getOffsetX(stringBounder, im.getWidth());
im = addTitle(im, background);
im = addFooter(im, background);
im = addHeader(im, background);
im = scaleImage(im, diagram.getScale());
if (diagram.isRotation()) {
im = PngRotation.process(im);
}
im = PngSizer.process(im, diagram.getMinwidth());
im = addFlashcode(im, background);
PngIO.write(im, os, diagram.getMetadata(), diagram.getDpi(fileFormatOption));
} finally {
cleanTemporaryFiles(diagram.entities().values());
cleanTemporaryFiles(diagram.getLinks());
}
if (cmap.length() > 0) {
return translateXY(cmap.toString(), (int) Math.round(supX), (int) Math.round(supY));
}
return null;
}
private BufferedImage addFlashcode(BufferedImage im, Color background) {
if (flashcodes == null) {
return im;
}
return new PngFlashcoder(flashcodes).processImage(im, background);
}
private String createDot(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
throws IOException, InterruptedException {
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
final String dotString = dotMaker.createDotString();
os.write(dotString.getBytes());
return null;
}
private BufferedImage scaleImage(BufferedImage im, Scale scale) {
if (scale == null) {
return im;
}
return PngScaler.scale(im, scale.getScale(im.getWidth(), im.getHeight()));
}
private byte[] getImageDataCmap(final String dotString, StringBuilder cmap2) throws IOException,
InterruptedException {
final Graphviz graphviz = GraphvizUtils.create(dotString, "cmapx", getPngType());
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
graphviz.createPng(baos);
baos.close();
final byte[] allData = baos.toByteArray();
Log.info("Reading " + allData.length + " bytes from dot");
final int pngStart = getPngStart(allData);
final String cmap = new String(allData, 0, pngStart, "UTF8");
Log.info("CMAP is " + cmap.length() + " long");
cmap2.append(cmap);
final byte[] imageData = new byte[allData.length - pngStart];
System.arraycopy(allData, pngStart, imageData, 0, imageData.length);
Log.info("PNG is " + imageData.length + " bytes from dot");
return imageData;
}
private int getPngStart(byte[] allData) throws IOException {
for (int i = 0; i < allData.length - 8; i++) {
if (isPngHeader(allData, i)) {
return i;
}
}
throw new IOException("Cannot find PNG header");
}
private boolean isPngHeader(byte[] allData, int i) {
if (i + 7 >= allData.length) {
return false;
}
return allData[i] == (byte) 0x89 && allData[i + 1] == (byte) 0x50 && allData[i + 2] == (byte) 0x4E
&& allData[i + 3] == (byte) 0x47 && allData[i + 4] == (byte) 0x0D && allData[i + 5] == (byte) 0x0A
&& allData[i + 6] == (byte) 0x1A && allData[i + 7] == (byte) 0x0A;
}
private byte[] getImageData(final String dotString, StringBuilder cmap) throws IOException, InterruptedException {
if (diagram.hasUrl()) {
return getImageDataCmap(dotString, cmap);
}
final Graphviz graphviz = GraphvizUtils.create(dotString, getPngType());
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
graphviz.createPng(baos);
baos.close();
final byte[] imageData = baos.toByteArray();
Log.info("Reading " + imageData.length + " bytes from dot");
// File f = new File("dummy.png");
// FileOutputStream fos = new FileOutputStream(f);
// fos.write(imageData);
// fos.close();
return imageData;
}
private String getPngType() {
if (OptionFlags.getInstance().isForceCairo()) {
return "png:cairo";
}
if (OptionFlags.getInstance().isForceGd()) {
return "png:gd";
}
return "png";
}
void createError(OutputStream os, int length, FileFormatOption fileFormat, String... supp) throws IOException {
final List<String> msg = new ArrayList<String>();
msg.add("Error: Reading " + length + " byte(s) from dot");
msg.add("Error reading the generated image");
for (String s : supp) {
msg.add(s);
}
final GraphicStrings errorResult = new GraphicStrings(msg);
errorResult.writeImage(os, fileFormat);
}
private BufferedImage addTitle(BufferedImage im, final Color background) {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
final int fontSize = getSkinParam().getFontSize(FontParam.TITLE, null);
final PngTitler pngTitler = new PngTitler(titleColor, diagram.getTitle(), fontSize, fontFamily,
HorizontalAlignement.CENTER, VerticalPosition.TOP);
return pngTitler.processImage(im, background, 3);
}
private double getTitlePngHeight(StringBounder stringBounder) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
final int fontSize = getSkinParam().getFontSize(FontParam.TITLE, null);
final PngTitler pngTitler = new PngTitler(titleColor, diagram.getTitle(), fontSize, fontFamily,
HorizontalAlignement.CENTER, VerticalPosition.TOP);
return pngTitler.getOffsetY(stringBounder);
}
private double getOffsetX(StringBounder stringBounder, double imWidth) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
final int fontSize = getSkinParam().getFontSize(FontParam.TITLE, null);
final PngTitler pngTitler = new PngTitler(titleColor, diagram.getTitle(), fontSize, fontFamily,
HorizontalAlignement.CENTER, VerticalPosition.TOP);
return pngTitler.getOffsetX(imWidth, stringBounder);
}
private String addTitleSvg(String svg, double width, double height) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
final int fontSize = getSkinParam().getFontSize(FontParam.TITLE, null);
final SvgTitler svgTitler = new SvgTitler(titleColor, diagram.getTitle(), fontSize, fontFamily,
HorizontalAlignement.CENTER, VerticalPosition.TOP, 3);
this.deltaY += svgTitler.getHeight();
return svgTitler.addTitleSvg(svg, width, height);
}
private double getTitleSvgHeight() throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
final int fontSize = getSkinParam().getFontSize(FontParam.TITLE, null);
final SvgTitler svgTitler = new SvgTitler(titleColor, diagram.getTitle(), fontSize, fontFamily,
HorizontalAlignement.CENTER, VerticalPosition.TOP, 3);
return svgTitler.getHeight();
}
private String addTitleEps(String eps) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
final int fontSize = getSkinParam().getFontSize(FontParam.TITLE, null);
final EpsTitler epsTitler = new EpsTitler(titleColor, diagram.getTitle(), fontSize, fontFamily,
HorizontalAlignement.CENTER, VerticalPosition.TOP, 3);
this.deltaY += epsTitler.getHeight();
return epsTitler.addTitleEps(eps);
}
private String addFooterEps(String eps) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.FOOTER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.FOOTER, null);
final int fontSize = getSkinParam().getFontSize(FontParam.FOOTER, null);
final EpsTitler epsTitler = new EpsTitler(titleColor, diagram.getFooter(), fontSize, fontFamily, diagram
.getFooterAlignement(), VerticalPosition.BOTTOM, 3);
return epsTitler.addTitleEps(eps);
}
private String addHeaderEps(String eps) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.HEADER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.HEADER, null);
final int fontSize = getSkinParam().getFontSize(FontParam.HEADER, null);
final EpsTitler epsTitler = new EpsTitler(titleColor, diagram.getHeader(), fontSize, fontFamily, diagram
.getHeaderAlignement(), VerticalPosition.TOP, 3);
this.deltaY += epsTitler.getHeight();
return epsTitler.addTitleEps(eps);
}
private String addHeaderSvg(String svg, double width, double height) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.HEADER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.HEADER, null);
final int fontSize = getSkinParam().getFontSize(FontParam.HEADER, null);
final SvgTitler svgTitler = new SvgTitler(titleColor, diagram.getHeader(), fontSize, fontFamily, diagram
.getHeaderAlignement(), VerticalPosition.TOP, 3);
this.deltaY += svgTitler.getHeight();
return svgTitler.addTitleSvg(svg, width, height);
}
private double getHeaderSvgHeight() throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.HEADER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.HEADER, null);
final int fontSize = getSkinParam().getFontSize(FontParam.HEADER, null);
final SvgTitler svgTitler = new SvgTitler(titleColor, diagram.getHeader(), fontSize, fontFamily, diagram
.getHeaderAlignement(), VerticalPosition.TOP, 3);
return svgTitler.getHeight();
}
private String addFooterSvg(String svg, double width, double height) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.FOOTER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.FOOTER, null);
final int fontSize = getSkinParam().getFontSize(FontParam.FOOTER, null);
final SvgTitler svgTitler = new SvgTitler(titleColor, diagram.getFooter(), fontSize, fontFamily, diagram
.getFooterAlignement(), VerticalPosition.BOTTOM, 3);
return svgTitler.addTitleSvg(svg, width, height + deltaY);
}
private double getFooterSvgHeight() throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.FOOTER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.FOOTER, null);
final int fontSize = getSkinParam().getFontSize(FontParam.FOOTER, null);
final SvgTitler svgTitler = new SvgTitler(titleColor, diagram.getFooter(), fontSize, fontFamily, diagram
.getFooterAlignement(), VerticalPosition.BOTTOM, 3);
return svgTitler.getHeight();
}
private BufferedImage addFooter(BufferedImage im, final Color background) {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.FOOTER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.FOOTER, null);
final int fontSize = getSkinParam().getFontSize(FontParam.FOOTER, null);
final PngTitler pngTitler = new PngTitler(titleColor, diagram.getFooter(), fontSize, fontFamily, diagram
.getFooterAlignement(), VerticalPosition.BOTTOM);
return pngTitler.processImage(im, background, 3);
}
private BufferedImage addHeader(BufferedImage im, final Color background) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.HEADER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.HEADER, null);
final int fontSize = getSkinParam().getFontSize(FontParam.HEADER, null);
final PngTitler pngTitler = new PngTitler(titleColor, diagram.getHeader(), fontSize, fontFamily, diagram
.getHeaderAlignement(), VerticalPosition.TOP);
return pngTitler.processImage(im, background, 3);
}
private double getHeaderPngHeight(StringBounder stringBounder) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.HEADER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.HEADER, null);
final int fontSize = getSkinParam().getFontSize(FontParam.HEADER, null);
final PngTitler pngTitler = new PngTitler(titleColor, diagram.getHeader(), fontSize, fontFamily, diagram
.getHeaderAlignement(), VerticalPosition.TOP);
return pngTitler.getOffsetY(stringBounder);
}
private void cleanTemporaryFiles(final Collection<? extends Imaged> imageFiles) throws IOException {
if (OptionFlags.getInstance().isKeepTmpFiles() == false) {
for (Imaged entity : imageFiles) {
if (entity.getImageFile() != null) {
entity.getImageFile().delete();
}
}
}
}
DrawFile createImage(Entity entity, double dpiFactor, int dpi) throws IOException {
if (entity.getType() == EntityType.NOTE) {
return createImageForNote(entity.getDisplay(), entity.getSpecificBackColor(), dpiFactor, dpi);
}
if (entity.getType() == EntityType.ACTOR) {
return createImageForActor(entity, dpiFactor);
}
if (entity.getType() == EntityType.CIRCLE_INTERFACE) {
return createImageForCircleInterface(entity, dpiFactor);
}
if (entity.getType() == EntityType.ABSTRACT_CLASS || entity.getType() == EntityType.CLASS
|| entity.getType() == EntityType.ENUM || entity.getType() == EntityType.INTERFACE) {
return createImageForCircleCharacter(entity, dpiFactor);
}
return null;
}
private DrawFile createImageForNoteOld2(String display, HtmlColor backColor, double dpiFactor, int dpi)
throws IOException {
final File fPng = FileUtils.createTempFile("plantumlB", ".png");
final Rose skin = new Rose();
final ISkinParam skinParam = new SkinParamBackcolored(getSkinParam(), backColor);
final Component comp = skin
.createComponent(ComponentType.NOTE, skinParam, StringUtils.getWithNewlines(display));
final int width = (int) (comp.getPreferredWidth(stringBounder) * dpiFactor);
final int height = (int) (comp.getPreferredHeight(stringBounder) * dpiFactor);
final Color background = diagram.getSkinParam().getBackgroundColor().getColor();
final EmptyImageBuilder builder = new EmptyImageBuilder(width, height, background);
final BufferedImage im = builder.getBufferedImage();
final Graphics2D g2d = builder.getGraphics2D();
comp.drawU(new UGraphicG2d(g2d, null, dpiFactor), new Dimension(width, height), new SimpleContext2D(false));
PngIO.write(im, fPng, dpi);
g2d.dispose();
final UGraphicSvg ug = new UGraphicSvg(true);
comp.drawU(ug, new Dimension(width, height), new SimpleContext2D(false));
final File fEps = FileUtils.createTempFile("plantumlB", ".eps");
final PrintWriter pw = new PrintWriter(fEps);
final UGraphicEps uEps = new UGraphicEps(EpsStrategy.getDefault());
comp.drawU(uEps, new Dimension(width, height), new SimpleContext2D(false));
pw.print(uEps.getEPSCode());
pw.close();
return DrawFile.createFromFile(fPng, getSvg(ug), fEps);
}
private DrawFile createImageForNote(String display, HtmlColor backColor, final double dpiFactor, final int dpi)
throws IOException {
final Rose skin = new Rose();
final ISkinParam skinParam = new SkinParamBackcolored(getSkinParam(), backColor);
final Component comp = skin
.createComponent(ComponentType.NOTE, skinParam, StringUtils.getWithNewlines(display));
final int width = (int) (comp.getPreferredWidth(stringBounder) * dpiFactor);
final int height = (int) (comp.getPreferredHeight(stringBounder) * dpiFactor);
final Color background = diagram.getSkinParam().getBackgroundColor().getColor();
final Lazy<File> lpng = new Lazy<File>() {
public File getNow() throws IOException {
final File fPng = FileUtils.createTempFile("plantumlB", ".png");
final EmptyImageBuilder builder = new EmptyImageBuilder(width, height, background);
final BufferedImage im = builder.getBufferedImage();
final Graphics2D g2d = builder.getGraphics2D();
comp.drawU(new UGraphicG2d(g2d, null, dpiFactor), new Dimension(width, height), new SimpleContext2D(
false));
PngIO.write(im, fPng, dpi);
g2d.dispose();
return fPng;
}
};
final Lazy<String> lsvg = new Lazy<String>() {
public String getNow() throws IOException {
final UGraphicSvg ug = new UGraphicSvg(true);
comp.drawU(ug, new Dimension(width, height), new SimpleContext2D(false));
return getSvg(ug);
}
};
final Lazy<File> leps = new Lazy<File>() {
public File getNow() throws IOException {
final File fEps = FileUtils.createTempFile("plantumlB", ".eps");
final PrintWriter pw = new PrintWriter(fEps);
final UGraphicEps uEps = new UGraphicEps(EpsStrategy.getDefault());
comp.drawU(uEps, new Dimension(width, height), new SimpleContext2D(false));
pw.print(uEps.getEPSCode());
pw.close();
return fEps;
}
};
return DrawFile.create(lpng, lsvg, leps, null);
}
static public String getSvg(UGraphicSvg ug) throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
ug.createXml(baos);
baos.close();
final String s = new String(baos.toByteArray(), "UTF-8");
final Pattern p = Pattern.compile("(?i)<g\\W.*</g>");
final Matcher m = p.matcher(s);
m.find();
return m.group(0);
}
private DrawFile createImageForActivity(Entity entity) throws IOException {
return null;
}
private DrawFile createImageForCircleInterface(Entity entity, final double dpiFactor) throws IOException {
final String stereo = entity.getStereotype() == null ? null : entity.getStereotype().getLabel();
final Color interfaceBackground = rose.getHtmlColor(getSkinParam(), ColorParam.componentInterfaceBackground,
stereo).getColor();
final Color interfaceBorder = rose.getHtmlColor(getSkinParam(), ColorParam.componentInterfaceBorder, stereo)
.getColor();
final Color background = rose.getHtmlColor(getSkinParam(), ColorParam.background, stereo).getColor();
final CircleInterface circleInterface = new CircleInterface(interfaceBackground, interfaceBorder);
final Lazy<File> lpng = new Lazy<File>() {
public File getNow() throws IOException {
final EmptyImageBuilder builder = new EmptyImageBuilder(circleInterface.getPreferredWidth(null)
* dpiFactor, circleInterface.getPreferredHeight(null) * dpiFactor, background);
final BufferedImage im = builder.getBufferedImage();
final Graphics2D g2d = builder.getGraphics2D();
circleInterface.drawU(new UGraphicG2d(g2d, null, 1.0));
final File png = FileUtils.createTempFile("circleinterface", ".png");
ImageIO.write(im, "png", png);
return png;
}
};
final Lazy<File> leps = new Lazy<File>() {
public File getNow() throws IOException {
final File epsFile = FileUtils.createTempFile("circleinterface", ".eps");
UGraphicEps.copyEpsToFile(circleInterface, epsFile);
return epsFile;
}
};
final Lazy<String> lsvg = new Lazy<String>() {
public String getNow() throws IOException {
return UGraphicG2d.getSvgString(circleInterface);
}
};
final Object signature = Arrays.asList("circleinterface", interfaceBackground, interfaceBorder, background,
dpiFactor);
return DrawFile.create(lpng, lsvg, leps, signature);
}
private DrawFile createImageForActor(Entity entity, final double dpiFactor) throws IOException {
final String stereo = entity.getStereotype() == null ? null : entity.getStereotype().getLabel();
final Color actorBackground = rose.getHtmlColor(getSkinParam(), ColorParam.usecaseActorBackground, stereo)
.getColor();
final Color actorBorder = rose.getHtmlColor(getSkinParam(), ColorParam.usecaseActorBorder, stereo).getColor();
final Color background = rose.getHtmlColor(getSkinParam(), ColorParam.background, stereo).getColor();
final StickMan stickMan = new StickMan(actorBackground, actorBorder);
final Lazy<File> lpng = new Lazy<File>() {
public File getNow() throws IOException {
final EmptyImageBuilder builder = new EmptyImageBuilder(stickMan.getPreferredWidth(null) * dpiFactor,
stickMan.getPreferredHeight(null) * dpiFactor, background);
final BufferedImage im = builder.getBufferedImage();
final Graphics2D g2d = builder.getGraphics2D();
stickMan.drawU(new UGraphicG2d(g2d, null, dpiFactor));
final File png = FileUtils.createTempFile("actor", ".png");
ImageIO.write(im, "png", png);
return png;
}
};
final Lazy<File> leps = new Lazy<File>() {
public File getNow() throws IOException {
final File epsFile = FileUtils.createTempFile("actor", ".eps");
UGraphicEps.copyEpsToFile(stickMan, epsFile);
return epsFile;
}
};
final Lazy<String> lsvg = new Lazy<String>() {
public String getNow() throws IOException {
return UGraphicG2d.getSvgString(stickMan);
}
};
final Object signature = Arrays.asList("actor", actorBackground, actorBorder, background, dpiFactor);
return DrawFile.create(lpng, lsvg, leps, signature);
}
private DrawFile createImageForCircleCharacter(Entity entity, double dpiFactor) throws IOException {
final Stereotype stereotype = entity.getStereotype();
if (stereotype == null || stereotype.getColor() == null) {
return null;
}
final String stereo = stereotype.getLabel();
final Color classBorder = rose.getHtmlColor(getSkinParam(), ColorParam.classBorder, stereo).getColor();
final Color classBackground = rose.getHtmlColor(getSkinParam(), ColorParam.classBackground, stereo).getColor();
final Font font = diagram.getSkinParam().getFont(FontParam.CIRCLED_CHARACTER, stereo);
final CircledCharacter circledCharacter = new CircledCharacter(stereotype.getCharacter(), getSkinParam()
.getCircledCharacterRadius(), font, stereotype.getColor(), classBorder, Color.BLACK);
return circledCharacter.generateCircleCharacter(classBackground, dpiFactor);
}
private ISkinParam getSkinParam() {
return diagram.getSkinParam();
}
private String createEps(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
throws IOException, InterruptedException {
try {
deltaY = 0;
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
final String dotString = dotMaker.createDotString();
if (OptionFlags.getInstance().isKeepTmpFiles()) {
traceDotString(dotString);
}
// final boolean isUnderline = dotMaker.isUnderline();
final Graphviz graphviz = GraphvizUtils.create(dotString, "eps");
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
graphviz.createPng(baos);
baos.close();
dotMaker.clean();
final boolean isUnderline = dotMaker.isUnderline();
String eps = new String(baos.toByteArray(), "UTF-8");
eps = cleanStrangeCharacter(eps);
if (isUnderline) {
eps = new UnderlineTrickEps(eps).getString();
}
if (diagram.getTitle() != null) {
eps = addTitleEps(eps);
}
if (diagram.getFooter() != null) {
eps = addFooterEps(eps);
}
if (diagram.getHeader() != null) {
eps = addHeaderEps(eps);
}
os.write(eps.getBytes("UTF-8"));
// final Dimension2D dim = getDimensionSvg(svg);
//
// svg = svg
// .replaceFirst(
// "(?i)<svg[^>]*>",
// "<svg xmlns=\"http://www.w3.org/2000/svg\"
// xmlns:xlink=\"http://www.w3.org/1999/xlink\"
// style=\"width:100%;height:100%;position:absolute;top:0;left:0;\">");
//
// svg = addTitleSvg(svg, dim.getWidth(), dim.getHeight());
// svg = addHeaderSvg(svg, dim.getWidth(), dim.getHeight());
// svg = addFooterSvg(svg, dim.getWidth(), dim.getHeight());
//
// // Image management
// final Pattern pImage = Pattern.compile("(?i)<image\\W[^>]*>");
// final Matcher mImage = pImage.matcher(svg);
// final StringBuffer sb = new StringBuffer();
// while (mImage.find()) {
// final String image = mImage.group(0);
// final String href = getValue(image, "href");
// final double widthSvg = Double.parseDouble(getValuePx(image,
// "width"));
// final double heightSvg = Double.parseDouble(getValuePx(image,
// "height"));
// final double x = Double.parseDouble(getValue(image, "x"));
// final double y = Double.parseDouble(getValue(image, "y"));
// final DrawFile drawFile = getDrawFileFromHref(href);
// final int widthPng = drawFile.getWidthPng();
// final int heightPng = drawFile.getHeightPng();
// String svg2 = drawFile.getSvg();
// final String scale = getScale(widthSvg, heightSvg, widthPng,
// heightPng);
// svg2 = svg2.replaceFirst("<[gG]>", "<g transform=\"translate(" +
// x + " " + y + ") " + scale + "\">");
// mImage.appendReplacement(sb, svg2);
// }
// mImage.appendTail(sb);
// svg = sb.toString();
} finally {
// cleanTemporaryFiles(diagram.entities().values());
// cleanTemporaryFiles(diagram.getLinks());
}
return null;
}
private GraphvizMaker populateImagesAndCreateGraphvizMaker(List<String> dotStrings,
FileFormatOption fileFormatOption) throws IOException, InterruptedException {
populateImages(diagram.getDpiFactor(fileFormatOption), diagram.getDpi(fileFormatOption));
populateImagesLink(diagram.getDpiFactor(fileFormatOption), diagram.getDpi(fileFormatOption));
final GraphvizMaker dotMaker = createDotMaker(dotStrings, fileFormatOption);
return dotMaker;
}
private GraphvizMaker createDotMaker(List<String> dotStrings, FileFormatOption fileFormatOption)
throws IOException, InterruptedException {
final FileFormat fileFormat = fileFormatOption.getFileFormat();
if (diagram.getUmlDiagramType() == UmlDiagramType.STATE
|| diagram.getUmlDiagramType() == UmlDiagramType.ACTIVITY) {
new CucaDiagramSimplifier(diagram, dotStrings, fileFormat);
}
final DotData dotData = new DotData(null, diagram.getLinks(), diagram.entities(), diagram.getUmlDiagramType(),
diagram.getSkinParam(), diagram.getRankdir(), diagram, diagram);
dotData.setDpi(diagram.getDpi(fileFormatOption));
if (diagram.getUmlDiagramType() == UmlDiagramType.CLASS || diagram.getUmlDiagramType() == UmlDiagramType.OBJECT) {
dotData.setStaticImagesMap(staticFilesMap);
if (diagram.isVisibilityModifierPresent()) {
dotData.setVisibilityModifierPresent(true);
}
}
if (diagram.getUmlDiagramType() == UmlDiagramType.STATE) {
dotData.setHideEmptyDescription(((StateDiagram) diagram).isHideEmptyDescription());
}
return diagram.getSkinParam().getStrategy().getGraphvizMaker(dotData, dotStrings, fileFormat);
// return new DotMaker(dotData, dotStrings, fileFormat);
}
private void populateImages(double dpiFactor, int dpi) throws IOException {
for (Entity entity : diagram.entities().values()) {
final DrawFile f = createImage(entity, dpiFactor, dpi);
if (f != null) {
entity.setImageFile(f);
}
}
}
private void populateImagesLink(double dpiFactor, int dpi) throws IOException {
for (Link link : diagram.getLinks()) {
final String note = link.getNote();
if (note == null) {
continue;
}
final DrawFile f = createImageForNote(note, null, dpiFactor, dpi);
if (f != null) {
link.setImageFile(f);
}
}
}
static String cleanStrangeCharacter(String eps) {
final StringBuilder sb = new StringBuilder();
final StringTokenizer st = new StringTokenizer(eps, "\r\n");
while (st.hasMoreTokens()) {
String s = st.nextToken();
if (s.equals(EpsGraphics.END_OF_FILE)) {
sb.append(s);
sb.append("\n");
s = st.nextToken();
if (s.equalsIgnoreCase("grestore") == false) {
s = st.nextToken();
if (s.equalsIgnoreCase("grestore") == false) {
throw new IllegalStateException();
}
}
}
sb.append(s);
sb.append("\n");
}
return sb.toString();
}
}