diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/standalone/Example1.java b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/Example1.java index e04feb8306fbf529bf098db1d342c0a5be3c67b0..17c5095fb2f63a770d0a00e466808d3c6d100b30 100644 --- a/xchart-demo/src/main/java/com/xeiam/xchart/standalone/Example1.java +++ b/xchart-demo/src/main/java/com/xeiam/xchart/standalone/Example1.java @@ -20,6 +20,8 @@ import com.xeiam.xchart.BitmapEncoder.BitmapFormat; import com.xeiam.xchart.Chart; import com.xeiam.xchart.Series; import com.xeiam.xchart.SeriesMarker; +import com.xeiam.xchart.VectorGraphicsEncoder; +import com.xeiam.xchart.VectorGraphicsEncoder.VectorGraphicsFormat; /** * Creates a simple Chart and saves it as a PNG and JPEG image file. @@ -48,5 +50,9 @@ public class Example1 { BitmapEncoder.saveBitmapWithDPI(chart, "./Sample_Chart_300_DPI", BitmapFormat.JPG, 300); BitmapEncoder.saveBitmapWithDPI(chart, "./Sample_Chart_300_DPI", BitmapFormat.GIF, 300); + VectorGraphicsEncoder.saveVectorGraphic(chart, "./Sample_Chart", VectorGraphicsFormat.EPS); + VectorGraphicsEncoder.saveVectorGraphic(chart, "./Sample_Chart", VectorGraphicsFormat.PDF); + VectorGraphicsEncoder.saveVectorGraphic(chart, "./Sample_Chart", VectorGraphicsFormat.SVG); + } } diff --git a/xchart/src/main/java/com/xeiam/xchart/VectorGraphicsEncoder.java b/xchart/src/main/java/com/xeiam/xchart/VectorGraphicsEncoder.java index 5eba3583cc9e092cf402c3d87c090a87b80ad4ef..fc00d7de4f4a7160648cfb8eeafdcb8f931a02b7 100644 --- a/xchart/src/main/java/com/xeiam/xchart/VectorGraphicsEncoder.java +++ b/xchart/src/main/java/com/xeiam/xchart/VectorGraphicsEncoder.java @@ -18,7 +18,10 @@ package com.xeiam.xchart; import java.io.FileOutputStream; import java.io.IOException; +import de.erichseifert.vectorgraphics2d.EPSGraphics2D; +import de.erichseifert.vectorgraphics2d.PDFGraphics2D; import de.erichseifert.vectorgraphics2d.SVGGraphics2D; +import de.erichseifert.vectorgraphics2d.VectorGraphics2D; /** * A helper class with static methods for saving Charts as bitmaps @@ -40,10 +43,28 @@ public final class VectorGraphicsEncoder { public static void saveVectorGraphic(Chart chart, String fileName, VectorGraphicsFormat vectorGraphicsFormat) throws IOException { - SVGGraphics2D g = new SVGGraphics2D(0.0, 0.0, chart.getWidth(), chart.getHeight()); + VectorGraphics2D g = null; + + switch (vectorGraphicsFormat) { + case EPS: + g = new EPSGraphics2D(0.0, 0.0, chart.getWidth(), chart.getHeight()); + break; + case PDF: + g = new PDFGraphics2D(0.0, 0.0, chart.getWidth(), chart.getHeight()); + break; + case SVG: + g = new SVGGraphics2D(0.0, 0.0, chart.getWidth(), chart.getHeight()); + break; + + default: + break; + } + chart.paint(g, chart.getWidth(), chart.getHeight()); + // Write the vector graphic output to a file - FileOutputStream file = new FileOutputStream("./ellipse.svg"); + FileOutputStream file = new FileOutputStream(fileName + "." + vectorGraphicsFormat.toString().toLowerCase()); + try { file.write(g.getBytes()); } finally { diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickLabels.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickLabels.java index 4f26213ed33faebeb4a097c21e90d8d298a584a6..51ceb6fab1ba54c007ef13cc88d4a8384fb7e777 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickLabels.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTickLabels.java @@ -84,7 +84,7 @@ public class AxisTickLabels implements ChartPart { AffineTransform orig = g.getTransform(); AffineTransform at = new AffineTransform(); - at.translate((float) xOffset, (float) (yOffset + axisTick.getAxis().getPaintZone().getHeight() - tickLocation + tickLabelBounds.getHeight() / 2.0)); + at.translate(xOffset, yOffset + axisTick.getAxis().getPaintZone().getHeight() - tickLocation + tickLabelBounds.getHeight() / 2.0); g.transform(at); g.fill(shape); g.setTransform(orig); @@ -114,22 +114,24 @@ public class AxisTickLabels implements ChartPart { if (tickLabel != null) { // some are null for logarithmic axes 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); + TextLayout textLayout = new TextLayout(tickLabel, getChartPainter().getStyleManager().getAxisTickLabelsFont(), frc); + // GlyphVector v = getChartPainter().getStyleManager().getAxisTickLabelsFont().createGlyphVector(frc, tickLabel); - Shape shape = layout.getOutline(null); + // Shape shape = v.getOutline(); + Shape shape = textLayout.getOutline(null); + Rectangle2D tickLabelBounds = shape.getBounds2D(); - // 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); + at.translate(xOffset + tickLocation - tickLabelBounds.getWidth() / 2.0, yOffset); g.transform(at); g.fill(shape); g.setTransform(orig); + // // debug box + // g.setColor(Color.blue); + // g.draw(new Rectangle2D.Double(xOffset + tickLocation - tickLabelBounds.getWidth() / 2.0, yOffset - tickLabelBounds.getHeight(), tickLabelBounds.getWidth(), tickLabelBounds.getHeight())); + if (tickLabelBounds.getHeight() > maxTickLabelHeight) { maxTickLabelHeight = tickLabelBounds.getHeight(); } diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java index 3244fd8da867dddf0d328f79a3514db2d690daec..d07fc1b46bcc5334cf385f03bef81fe175bebd1e 100644 --- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java +++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Legend.java @@ -23,9 +23,7 @@ import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.List; +import java.util.LinkedHashMap; import java.util.Map; import com.xeiam.xchart.Series; @@ -84,10 +82,10 @@ public class Legend implements ChartPart { for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) { - List<Map.Entry<String, Rectangle2D>> seriesBounds = getSeriesTextBounds(series, g); + Map<String, Rectangle2D> seriesBounds = getSeriesTextBounds(series, g); double blockHeight = 0; - for (Map.Entry<String, Rectangle2D> entry : seriesBounds) { + for (Map.Entry<String, Rectangle2D> entry : seriesBounds.entrySet()) { blockHeight += entry.getValue().getHeight() + MULTI_LINE_SPACE; legendTextContentMaxWidth = Math.max(legendTextContentMaxWidth, entry.getValue().getWidth()); } @@ -130,8 +128,6 @@ public class Legend implements ChartPart { double legendBoxWidth = sizeHint[0]; double legendBoxHeight = sizeHint[1]; - // FontMetrics fontMetrics = g.getFontMetrics(styleManager.getLegendFont()); - // legend draw position double xOffset = 0; double yOffset = 0; @@ -178,11 +174,11 @@ public class Legend implements ChartPart { for (Series series : chartPainter.getAxisPair().getSeriesMap().values()) { - List<Map.Entry<String, Rectangle2D>> seriesTextBounds = getSeriesTextBounds(series, g); + Map<String, Rectangle2D> seriesTextBounds = getSeriesTextBounds(series, g); float blockHeight = 0; double legendTextContentMaxWidth = 0; - for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds) { + for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds.entrySet()) { blockHeight += entry.getValue().getHeight() + MULTI_LINE_SPACE; legendTextContentMaxWidth = Math.max(legendTextContentMaxWidth, entry.getValue().getWidth()); } @@ -227,7 +223,7 @@ public class Legend implements ChartPart { // g.draw(boundsTemp); } - // paint series name + // paint series text g.setColor(chartPainter.getStyleManager().getChartFontColor()); double multiLineOffset = 0.0; @@ -235,10 +231,7 @@ public class Legend implements ChartPart { if (styleManager.getChartType() != ChartType.Bar) { double x = startx + styleManager.getLegendSeriesLineLength() + styleManager.getLegendPadding(); - for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds) { - - // float itemOffsetY = -fontMetrics.getDescent(); - // System.out.println(itemOffsetY); + for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds.entrySet()) { double height = entry.getValue().getHeight(); double centerOffsetY = (Math.max(getChartPainter().getStyleManager().getMarkerSize(), height) - height) / 2.0; @@ -266,7 +259,7 @@ public class Legend implements ChartPart { else { final double x = startx + BOX_SIZE + styleManager.getLegendPadding(); - for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds) { + for (Map.Entry<String, Rectangle2D> entry : seriesTextBounds.entrySet()) { double height = entry.getValue().getHeight(); double centerOffsetY = (Math.max(BOX_SIZE, height) - height) / 2.0; @@ -301,16 +294,24 @@ public class Legend implements ChartPart { } - private List<Map.Entry<String, Rectangle2D>> getSeriesTextBounds(Series series, Graphics2D g) { + private Map<String, Rectangle2D> getSeriesTextBounds(Series series, Graphics2D g) { + + // FontMetrics fontMetrics = g.getFontMetrics(getChartPainter().getStyleManager().getLegendFont()); + // float fontDescent = fontMetrics.getDescent(); String lines[] = series.getName().split("\\n"); - List<Map.Entry<String, Rectangle2D>> seriesTextBounds = new ArrayList<Map.Entry<String, Rectangle2D>>(lines.length); + Map<String, Rectangle2D> seriesTextBounds = new LinkedHashMap<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)); + // System.out.println(tl.getAscent()); + // System.out.println(tl.getDescent()); + // System.out.println(tl.getBounds()); + // seriesTextBounds.put(line, new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() - tl.getDescent())); + // seriesTextBounds.put(line, new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), tl.getAscent())); + seriesTextBounds.put(line, bounds); } return seriesTextBounds; }