Skip to content
Snippets Groups Projects
Commit 168aedd2 authored by Tim Molter's avatar Tim Molter
Browse files

roughed in vector export

parent b182cee6
Branches
No related tags found
No related merge requests found
......@@ -8,4 +8,7 @@ bin/
*.png
*.jpg
*.bmp
*.gif
\ No newline at end of file
*.gif
*.svg
*.pdf
*.eps
\ No newline at end of file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
......@@ -66,6 +67,24 @@
<downloadUrl>https://oss.sonatype.org/content/groups/public/com/xeiam/xchart</downloadUrl>
</distributionManagement>
<repositories>
<repository>
<id>erichseifert.de</id>
<url>http://mvn.erichseifert.de/maven2</url>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.erichseifert.vectorgraphics2d</groupId>
<artifactId>VectorGraphics2D</artifactId>
<version>0.9.1</version>
<optional>true</optional>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
......@@ -128,7 +147,7 @@
<autoversionsubmodules>true</autoversionsubmodules>
</configuration>
</plugin>
<!-- for header in all .java files -->
<plugin>
<groupId>com.mycila.maven-license-plugin</groupId>
......
/**
* Copyright 2013 Xeiam LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.xeiam.xchart.standalone;
import java.io.IOException;
import com.xeiam.xchart.Chart;
import com.xeiam.xchart.QuickChart;
import com.xeiam.xchart.VectorGraphicsEncoder;
import com.xeiam.xchart.VectorGraphicsEncoder.VectorGraphicsFormat;
/**
* @author timmolter
*/
public class VectorGraphicsTest {
public static void main(String[] args) throws IOException {
double[] xData = new double[] { 0.0, 1.0, 2.0 };
double[] yData = new double[] { 2.0, 1.0, 0.0 };
// Create Chart
Chart chart = QuickChart.getChart("Sample Chart", "X", "Y", "y(x)", xData, yData);
VectorGraphicsEncoder.saveVectorGraphic(chart, "", VectorGraphicsFormat.PDF);
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.xeiam.xchart</groupId>
<artifactId>xchart-parent</artifactId>
......@@ -13,4 +14,11 @@
<name>XChart</name>
<description>The core XChart library</description>
<dependencies>
<dependency>
<groupId>de.erichseifert.vectorgraphics2d</groupId>
<artifactId>VectorGraphics2D</artifactId>
</dependency>
</dependencies>
</project>
/**
* Copyright 2011 - 2014 Xeiam LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.xeiam.xchart;
import java.io.FileOutputStream;
import java.io.IOException;
import de.erichseifert.vectorgraphics2d.SVGGraphics2D;
/**
* A helper class with static methods for saving Charts as bitmaps
*
* @author timmolter
*/
public final class VectorGraphicsEncoder {
/**
* Constructor - Private constructor to prevent instantiation
*/
private VectorGraphicsEncoder() {
}
public enum VectorGraphicsFormat {
EPS, PDF, SVG;
}
public static void saveVectorGraphic(Chart chart, String fileName, VectorGraphicsFormat vectorGraphicsFormat) throws IOException {
SVGGraphics2D g = new SVGGraphics2D(0.0, 0.0, chart.getWidth(), chart.getHeight());
chart.paint(g, chart.getWidth(), chart.getHeight());
// Write the vector graphic output to a file
FileOutputStream file = new FileOutputStream("./ellipse.svg");
try {
file.write(g.getBytes());
} finally {
file.close();
}
}
}
......@@ -16,8 +16,10 @@
package com.xeiam.xchart.internal.chartpart;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
/**
......@@ -76,12 +78,20 @@ public class AxisTickLabels implements ChartPart {
// TextLayout layout = new TextLayout(tickLabel, font, new FontRenderContext(null, true, false));
TextLayout layout = new TextLayout(tickLabel, getChartPainter().getStyleManager().getAxisTickLabelsFont(), frc);
Rectangle2D tickLabelBounds = layout.getBounds();
layout.draw(g, (float) xOffset, (float) (yOffset + axisTick.getAxis().getPaintZone().getHeight() - tickLocation + tickLabelBounds.getHeight() / 2.0));
// layout.draw(g, (float) xOffset, (float) (yOffset + axisTick.getAxis().getPaintZone().getHeight() - tickLocation + tickLabelBounds.getHeight() / 2.0));
Shape shape = layout.getOutline(null);
AffineTransform orig = g.getTransform();
AffineTransform at = new AffineTransform();
at.translate((float) xOffset, (float) (yOffset + axisTick.getAxis().getPaintZone().getHeight() - tickLocation + tickLabelBounds.getHeight() / 2.0));
g.transform(at);
g.fill(shape);
g.setTransform(orig);
if (tickLabelBounds.getWidth() > maxTickLabelWidth) {
maxTickLabelWidth = tickLabelBounds.getWidth();
}
// g.setTransform(orig);
}
}
......@@ -106,7 +116,19 @@ public class AxisTickLabels implements ChartPart {
FontRenderContext frc = g.getFontRenderContext();
TextLayout layout = new TextLayout(tickLabel, getChartPainter().getStyleManager().getAxisTickLabelsFont(), frc);
Rectangle2D tickLabelBounds = layout.getBounds();
layout.draw(g, (float) (xOffset + tickLocation - tickLabelBounds.getWidth() / 2.0), (float) yOffset);
// layout.draw(g, (float) (xOffset + tickLocation - tickLabelBounds.getWidth() / 2.0), (float) yOffset);
Shape shape = layout.getOutline(null);
// Graphics2D gTemp = (Graphics2D) g.create();
// gTemp.setTransform(g.getTransform());
// gTemp.translate((float) (xOffset + tickLocation - tickLabelBounds.getWidth() / 2.0), (float) yOffset);
AffineTransform orig = g.getTransform();
AffineTransform at = new AffineTransform();
at.translate((float) (xOffset + tickLocation - tickLabelBounds.getWidth() / 2.0), (float) yOffset);
g.transform(at);
g.fill(shape);
g.setTransform(orig);
if (tickLabelBounds.getHeight() > maxTickLabelHeight) {
maxTickLabelHeight = tickLabelBounds.getHeight();
......
......@@ -16,6 +16,7 @@
package com.xeiam.xchart.internal.chartpart;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
......@@ -72,11 +73,13 @@ public class AxisTitle implements ChartPart {
int xOffset = (int) (axis.getPaintZone().getX() + nonRotatedRectangle.getHeight());
int yOffset = (int) ((axis.getPaintZone().getHeight() + nonRotatedRectangle.getWidth()) / 2.0 + axis.getPaintZone().getY());
AffineTransform orig = g.getTransform();
g.transform(AffineTransform.getRotateInstance(Math.PI / -2.0, xOffset, yOffset));
AffineTransform at = new AffineTransform();
at.rotate(Math.PI / -2.0, xOffset, yOffset);
g.transform(at);
g.drawString(text, xOffset, yOffset);
g.setTransform(orig);
// ///////////////////////////////////////////////
g.setTransform(orig);
// System.out.println(nonRotatedRectangle.getHeight());
// bounds
......@@ -103,7 +106,14 @@ public class AxisTitle implements ChartPart {
double xOffset = axis.getPaintZone().getX() + (axis.getPaintZone().getWidth() - rectangle.getWidth()) / 2.0;
double yOffset = axis.getPaintZone().getY() + axis.getPaintZone().getHeight() - rectangle.getHeight();
textLayout.draw(g, (float) xOffset, (float) (yOffset - rectangle.getY()));
// textLayout.draw(g, (float) xOffset, (float) (yOffset - rectangle.getY()));
Shape shape = textLayout.getOutline(null);
AffineTransform orig = g.getTransform();
AffineTransform at = new AffineTransform();
at.translate((float) xOffset, (float) (yOffset - rectangle.getY()));
g.transform(at);
g.fill(shape);
g.setTransform(orig);
bounds =
new Rectangle2D.Double(xOffset, yOffset - getChartPainter().getStyleManager().getAxisTitlePadding(), rectangle.getWidth(), rectangle.getHeight()
......
......@@ -20,6 +20,7 @@ import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
/**
......@@ -120,7 +121,15 @@ public class ChartTitle implements ChartPart {
// g.draw(bounds);
g.setColor(chartPainter.getStyleManager().getChartFontColor());
textLayout.draw(g, xOffset, yOffset);
// textLayout.draw(g, xOffset, yOffset);
Shape shape = textLayout.getOutline(null);
AffineTransform orig = g.getTransform();
AffineTransform at = new AffineTransform();
at.translate(xOffset, yOffset);
g.transform(at);
g.fill(shape);
g.setTransform(orig);
}
}
......
......@@ -16,9 +16,11 @@
package com.xeiam.xchart.internal.chartpart;
import java.awt.BasicStroke;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.AbstractMap;
......@@ -37,6 +39,7 @@ public class Legend implements ChartPart {
private static final int LEGEND_MARGIN = 6;
private static final int BOX_SIZE = 20;
private static final int MULTI_LINE_SPACE = 3;
/**
* parent
......@@ -71,26 +74,25 @@ public class Legend implements ChartPart {
}
StyleManager styleManager = getChartPainter().getStyleManager();
FontMetrics fontMetrics = g.getFontMetrics(chartPainter.getStyleManager().getLegendFont());
boolean isBar = styleManager.getChartType() == ChartType.Bar;
// determine legend text content max width
double legendTextContentMaxWidth = 0;
// double legendTextContentMaxHeight = 0;
// determine legend content height
double legendContentHeight = 0;
for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) {
List<Map.Entry<String, Rectangle2D>> seriesBounds = getSeriesNameBounds(series, g);
List<Map.Entry<String, Rectangle2D>> seriesBounds = getSeriesTextBounds(series, g);
double blockHeight = 0;
for (Map.Entry<String, Rectangle2D> entry : seriesBounds) {
blockHeight += entry.getValue().getHeight();
blockHeight += entry.getValue().getHeight() + MULTI_LINE_SPACE;
legendTextContentMaxWidth = Math.max(legendTextContentMaxWidth, entry.getValue().getWidth());
}
blockHeight -= MULTI_LINE_SPACE;
blockHeight = Math.max(blockHeight, isBar ? BOX_SIZE : getChartPainter().getStyleManager().getMarkerSize());
legendContentHeight += blockHeight + styleManager.getLegendPadding();
......@@ -104,28 +106,18 @@ public class Legend implements ChartPart {
else {
legendContentWidth = BOX_SIZE + styleManager.getLegendPadding() + legendTextContentMaxWidth;
}
// Legend Box
double legendBoxWidth = legendContentWidth + 2 * styleManager.getLegendPadding();
double legendBoxHeight = legendContentHeight + 1 * styleManager.getLegendPadding();
return new double[] { legendBoxWidth, legendBoxHeight };
}
private List<Map.Entry<String, Rectangle2D>> getSeriesNameBounds(Series series, Graphics2D g) {
String lines[] = series.getName().split("\\n");
List<Map.Entry<String, Rectangle2D>> stringBounds = new ArrayList<Map.Entry<String, Rectangle2D>>(lines.length);
for (String line : lines) {
Rectangle2D bounds = g.getFontMetrics().getStringBounds(line, g);
stringBounds.add(new AbstractMap.SimpleEntry<String, Rectangle2D>(line, bounds));
}
return stringBounds;
}
@Override
public void paint(Graphics2D g) {
bounds = new Rectangle2D.Double();
g.setFont(chartPainter.getStyleManager().getLegendFont());
// g.setFont(chartPainter.getStyleManager().getLegendFont());
StyleManager styleManager = getChartPainter().getStyleManager();
......@@ -138,7 +130,7 @@ public class Legend implements ChartPart {
double legendBoxWidth = sizeHint[0];
double legendBoxHeight = sizeHint[1];
FontMetrics fontMetrics = g.getFontMetrics(styleManager.getLegendFont());
// FontMetrics fontMetrics = g.getFontMetrics(styleManager.getLegendFont());
// legend draw position
double xOffset = 0;
......@@ -186,16 +178,20 @@ public class Legend implements ChartPart {
for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) {
List<Map.Entry<String, Rectangle2D>> seriesNameBounds = getSeriesNameBounds(series, g);
List<Map.Entry<String, Rectangle2D>> seriesTextBounds = getSeriesTextBounds(series, g);
float blockHeight = 0;
for (Map.Entry<String, Rectangle2D> entry : seriesNameBounds) {
blockHeight += entry.getValue().getHeight();
double legendTextContentMaxWidth = 0;
for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds) {
blockHeight += entry.getValue().getHeight() + MULTI_LINE_SPACE;
legendTextContentMaxWidth = Math.max(legendTextContentMaxWidth, entry.getValue().getWidth());
}
blockHeight -= MULTI_LINE_SPACE;
blockHeight = Math.max(blockHeight, styleManager.getChartType() == ChartType.Bar ? BOX_SIZE : getChartPainter().getStyleManager().getMarkerSize());
if (styleManager.getChartType() != ChartType.Bar) {
// paint line
if (styleManager.getChartType() != ChartType.Scatter && series.getStroke() != null) {
g.setColor(series.getStrokeColor());
......@@ -204,10 +200,17 @@ public class Legend implements ChartPart {
g.draw(line);
}
// // debug box
// Rectangle2D boundsTemp = new Rectangle2D.Double(startx, starty, styleManager.getLegendSeriesLineLength(), blockHeight);
// g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
// g.setColor(Color.red);
// g.draw(boundsTemp);
// paint marker
if (series.getMarker() != null) {
g.setColor(series.getMarkerColor());
series.getMarker().paint(g, startx + styleManager.getLegendSeriesLineLength() / 2.0, starty + blockHeight / 2.0, getChartPainter().getStyleManager().getMarkerSize());
}
}
else {
......@@ -217,27 +220,73 @@ public class Legend implements ChartPart {
Shape rectSmall = new Rectangle2D.Double(startx, starty, BOX_SIZE, BOX_SIZE);
g.fill(rectSmall);
}
// // debug box
// Rectangle2D boundsTemp = new Rectangle2D.Double(startx, starty, BOX_SIZE, BOX_SIZE);
// g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
// g.setColor(Color.red);
// g.draw(boundsTemp);
}
// paint series name
g.setColor(chartPainter.getStyleManager().getChartFontColor());
float itemOffsetY = -fontMetrics.getDescent();
double multiLineOffset = 0.0;
if (styleManager.getChartType() != ChartType.Bar) {
final float x = (float) (startx + styleManager.getLegendSeriesLineLength() + styleManager.getLegendPadding());
for (Map.Entry<String, Rectangle2D> entry : seriesNameBounds) {
g.drawString(entry.getKey(), x, (float) (starty + entry.getValue().getHeight()) + itemOffsetY);
itemOffsetY += entry.getValue().getHeight();
double x = startx + styleManager.getLegendSeriesLineLength() + styleManager.getLegendPadding();
for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds) {
// float itemOffsetY = -fontMetrics.getDescent();
// System.out.println(itemOffsetY);
double height = entry.getValue().getHeight();
double centerOffsetY = (Math.max(getChartPainter().getStyleManager().getMarkerSize(), height) - height) / 2.0;
FontRenderContext frc = g.getFontRenderContext();
TextLayout tl = new TextLayout(entry.getKey(), styleManager.getLegendFont(), frc);
Shape shape = tl.getOutline(null);
AffineTransform orig = g.getTransform();
AffineTransform at = new AffineTransform();
at.translate(x, starty + height + centerOffsetY + multiLineOffset);
g.transform(at);
g.fill(shape);
g.setTransform(orig);
// // debug box
// Rectangle2D boundsTemp = new Rectangle2D.Double(x, starty + centerOffsetY + multiLineOffset, entry.getValue().getWidth(), height);
// g.setColor(Color.blue);
// g.draw(boundsTemp);
multiLineOffset += height + MULTI_LINE_SPACE;
}
starty += blockHeight + styleManager.getLegendPadding();
}
else {
final float x = (float) (startx + BOX_SIZE + styleManager.getLegendPadding());
for (Map.Entry<String, Rectangle2D> entry : seriesNameBounds) {
final double x = startx + BOX_SIZE + styleManager.getLegendPadding();
for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds) {
double height = entry.getValue().getHeight();
double centerOffsetY = (Math.max(BOX_SIZE, height) - height) / 2.0;
g.drawString(entry.getKey(), x, (float) (starty + height + itemOffsetY + centerOffsetY));
itemOffsetY += height;
FontRenderContext frc = g.getFontRenderContext();
TextLayout tl = new TextLayout(entry.getKey(), styleManager.getLegendFont(), frc);
Shape shape = tl.getOutline(null);
AffineTransform orig = g.getTransform();
AffineTransform at = new AffineTransform();
at.translate(x, starty + height + centerOffsetY + multiLineOffset);
g.transform(at);
g.fill(shape);
g.setTransform(orig);
// // debug box
// Rectangle2D boundsTemp = new Rectangle2D.Double(x, starty + centerOffsetY, entry.getValue().getWidth(), height);
// g.setColor(Color.blue);
// g.draw(boundsTemp);
multiLineOffset += height + MULTI_LINE_SPACE;
}
starty += blockHeight + styleManager.getLegendPadding();
}
......@@ -252,6 +301,20 @@ public class Legend implements ChartPart {
}
private List<Map.Entry<String, Rectangle2D>> getSeriesTextBounds(Series series, Graphics2D g) {
String lines[] = series.getName().split("\\n");
List<Map.Entry<String, Rectangle2D>> seriesTextBounds = new ArrayList<Map.Entry<String, Rectangle2D>>(lines.length);
for (String line : lines) {
FontRenderContext frc = g.getFontRenderContext();
TextLayout tl = new TextLayout(line, getChartPainter().getStyleManager().getLegendFont(), frc);
Shape shape = tl.getOutline(null);
Rectangle2D bounds = shape.getBounds2D();
seriesTextBounds.add(new AbstractMap.SimpleEntry<String, Rectangle2D>(line, bounds));
}
return seriesTextBounds;
}
@Override
public Rectangle2D getBounds() {
......
......@@ -62,7 +62,7 @@ public class PlotContentBarChart extends PlotContent {
for (Series series : getChartPainter().getAxisPair().getSeriesMap().values()) {
// data points
Collection<?> xData = series.getXData();
// Collection<?> xData = series.getXData();
Collection<? extends Number> yData = series.getYData();
double yMin = getChartPainter().getAxisPair().getYAxis().getMin();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment