From f38ed5b633c3b9d2634556c2447c992ba201275f Mon Sep 17 00:00:00 2001
From: Tim Molter <tim.molter@gmail.com>
Date: Fri, 26 Apr 2013 15:29:00 +0200
Subject: [PATCH] new feature: save as a high res png by specifying DPI

---
 .gitignore                                    |  1 +
 .../xchart/demo/charts/line/LineChart03.java  |  4 +
 .../com/xeiam/xchart/standalone/Example1.java |  1 +
 .../java/com/xeiam/xchart/BitmapEncoder.java  | 86 +++++++++++++++++++
 .../xeiam/xchart/internal/chartpart/Axis.java | 16 ++--
 .../internal/chartpart/AxisTickLabels.java    |  5 +-
 .../xchart/internal/chartpart/AxisTitle.java  | 19 +---
 .../xchart/internal/chartpart/ChartTitle.java |  7 +-
 .../xchart/internal/chartpart/Legend.java     | 64 ++++++++------
 .../xeiam/xchart/internal/markers/Circle.java |  8 +-
 .../xchart/internal/markers/Diamond.java      | 12 ++-
 .../xeiam/xchart/internal/markers/Marker.java |  6 +-
 .../xeiam/xchart/internal/markers/Square.java |  9 +-
 .../xchart/internal/markers/TriangleDown.java | 12 ++-
 .../xchart/internal/markers/TriangleUp.java   | 14 +--
 15 files changed, 192 insertions(+), 72 deletions(-)

diff --git a/.gitignore b/.gitignore
index 3a262620..ba17b3f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@ target/
 .project
 .settings/
 *.png
+*.jpg
 CSV/
\ No newline at end of file
diff --git a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart03.java b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart03.java
index 5dc34019..80f1e8e3 100644
--- a/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart03.java
+++ b/xchart-demo/src/main/java/com/xeiam/xchart/demo/charts/line/LineChart03.java
@@ -83,9 +83,13 @@ public class LineChart03 implements ExampleChart {
     chart.getStyleManager().setChartTitleBoxVisible(true);
     chart.getStyleManager().setChartTitleBoxBorderColor(Color.BLACK);
     chart.getStyleManager().setPlotGridLinesVisible(false);
+
     chart.getStyleManager().setAxisTickPadding(20);
+
     chart.getStyleManager().setAxisTickMarkLength(15);
+
     chart.getStyleManager().setPlotPadding(20);
+
     chart.getStyleManager().setChartTitleFont(new Font(Font.MONOSPACED, Font.BOLD, 24));
     chart.getStyleManager().setLegendFont(new Font(Font.SERIF, Font.PLAIN, 18));
     chart.getStyleManager().setLegendPosition(LegendPosition.InsideSE);
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 bb791b57..75dbf9e0 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
@@ -35,6 +35,7 @@ public class Example1 {
     chart.addSeries("y(x)", null, yData);
 
     BitmapEncoder.savePNG(chart, "./Sample_Chart.png");
+    BitmapEncoder.savePNGWithDPI(chart, "./Sample_Chart_300_DPI.png", 300);
     BitmapEncoder.saveJPG(chart, "./Sample_Chart.jpg", 0.95f);
 
   }
diff --git a/xchart/src/main/java/com/xeiam/xchart/BitmapEncoder.java b/xchart/src/main/java/com/xeiam/xchart/BitmapEncoder.java
index 9d148eff..964abf2f 100644
--- a/xchart/src/main/java/com/xeiam/xchart/BitmapEncoder.java
+++ b/xchart/src/main/java/com/xeiam/xchart/BitmapEncoder.java
@@ -16,6 +16,7 @@
 package com.xeiam.xchart;
 
 import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -26,8 +27,12 @@ import java.util.Iterator;
 
 import javax.imageio.IIOImage;
 import javax.imageio.ImageIO;
+import javax.imageio.ImageTypeSpecifier;
 import javax.imageio.ImageWriteParam;
 import javax.imageio.ImageWriter;
+import javax.imageio.metadata.IIOInvalidTreeException;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataNode;
 import javax.imageio.stream.FileImageOutputStream;
 
 /**
@@ -63,6 +68,79 @@ public final class BitmapEncoder {
     out.close();
   }
 
+  /**
+   * Save a chart as a PNG with a custom DPI. The default DPI is 72, which is fine for displaying charts on a computer monitor, but for printing charts, a DPI of around 300 is much better.
+   * 
+   * @param chart
+   * @param fileName
+   * @param DPI
+   * @throws IOException
+   */
+  public static void savePNGWithDPI(Chart chart, String fileName, int DPI) throws IOException {
+
+    double scaleFactor = DPI / 72.0;
+
+    BufferedImage bufferedImage = new BufferedImage((int) (chart.getWidth() * scaleFactor), (int) (chart.getHeight() * scaleFactor), BufferedImage.TYPE_INT_RGB);
+
+    Graphics2D graphics2D = bufferedImage.createGraphics();
+
+    AffineTransform at = graphics2D.getTransform();
+    at.scale(scaleFactor, scaleFactor);
+    graphics2D.setTransform(at);
+
+    chart.paint(graphics2D, chart.getWidth(), chart.getHeight());
+
+    for (Iterator<ImageWriter> iw = ImageIO.getImageWritersByFormatName("png"); iw.hasNext();) {
+      ImageWriter writer = iw.next();
+      // instantiate an ImageWriteParam object with default compression options
+      ImageWriteParam iwp = writer.getDefaultWriteParam();
+
+      ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
+      IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, iwp);
+      if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
+        continue;
+      }
+
+      setDPIforPNG(metadata, DPI);
+
+      File file = new File(fileName);
+      FileImageOutputStream output = new FileImageOutputStream(file);
+      writer.setOutput(output);
+      IIOImage image = new IIOImage(bufferedImage, null, metadata);
+      writer.write(null, image, iwp);
+      writer.dispose();
+      break;
+    }
+  }
+
+  /**
+   * Sets the metadata correctly
+   * 
+   * @param metadata
+   * @param DPI
+   * @throws IIOInvalidTreeException
+   */
+  private static void setDPIforPNG(IIOMetadata metadata, int DPI) throws IIOInvalidTreeException {
+
+    // for PNG, it's dots per millimeter
+    double dotsPerMilli = 1.0 * DPI / 10 / 2.54;
+
+    IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
+    horiz.setAttribute("value", Double.toString(dotsPerMilli));
+
+    IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
+    vert.setAttribute("value", Double.toString(dotsPerMilli));
+
+    IIOMetadataNode dim = new IIOMetadataNode("Dimension");
+    dim.appendChild(horiz);
+    dim.appendChild(vert);
+
+    IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
+    root.appendChild(dim);
+
+    metadata.mergeTree("javax_imageio_1.0", root);
+  }
+
   /**
    * Save a Chart as a JPEG file
    * 
@@ -92,4 +170,12 @@ public final class BitmapEncoder {
     writer.dispose();
   }
 
+  public static void main(String[] args) {
+
+    for (String format : ImageIO.getWriterFormatNames()) {
+      System.out.println(format);
+      // ImageIO.write(bufferedImage, format, new File("C:\\image_new." + format));
+    }
+  }
+
 }
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java
index 15b44ee2..4d156ed8 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/Axis.java
@@ -19,6 +19,7 @@ import java.awt.Graphics2D;
 import java.awt.Rectangle;
 import java.awt.font.FontRenderContext;
 import java.awt.font.TextLayout;
+import java.awt.geom.Rectangle2D;
 import java.math.BigDecimal;
 
 import com.xeiam.xchart.StyleManager.LegendPosition;
@@ -128,7 +129,7 @@ public class Axis implements ChartPart {
       double titleHeight = 0.0;
       if (axisTitle.getText() != null && !axisTitle.getText().trim().equalsIgnoreCase("") && getChartPainter().getStyleManager().isXAxisTitleVisible()) {
         TextLayout textLayout = new TextLayout(axisTitle.getText(), getChartPainter().getStyleManager().getAxisTitleFont(), new FontRenderContext(null, true, false));
-        Rectangle rectangle = textLayout.getPixelBounds(null, 0, 0);
+        Rectangle2D rectangle = textLayout.getBounds();
         titleHeight = rectangle.getHeight() + getChartPainter().getStyleManager().getAxisTitlePadding();
       }
 
@@ -136,9 +137,8 @@ public class Axis implements ChartPart {
       double axisTickLabelsHeight = 0.0;
       if (getChartPainter().getStyleManager().isXAxisTicksVisible()) {
         TextLayout textLayout = new TextLayout("0", getChartPainter().getStyleManager().getAxisTickLabelsFont(), new FontRenderContext(null, true, false));
-        Rectangle rectangle = textLayout.getPixelBounds(null, 0, 0);
-        axisTickLabelsHeight = rectangle.getHeight() + getChartPainter().getStyleManager().getAxisTickPadding() + getChartPainter().getStyleManager().getAxisTickMarkLength()
-            + getChartPainter().getStyleManager().getPlotPadding();
+        Rectangle2D rectangle = textLayout.getBounds();
+        axisTickLabelsHeight = rectangle.getHeight() + getChartPainter().getStyleManager().getAxisTickPadding() + getChartPainter().getStyleManager().getAxisTickMarkLength();
       }
       return (int) (titleHeight + axisTickLabelsHeight);
     } else { // Y-Axis
@@ -165,7 +165,9 @@ public class Axis implements ChartPart {
       int xOffset = getChartPainter().getStyleManager().getChartPadding();
       int yOffset = getChartPainter().getChartTitle().getSizeHint();
       int width = 80; // arbitrary, final width depends on Axis tick labels
-      int height = getChartPainter().getHeight() - yOffset - axisPair.getxAxis().getSizeHint() - getChartPainter().getStyleManager().getChartPadding();
+
+      int height = getChartPainter().getHeight() - yOffset - axisPair.getxAxis().getSizeHint() - getChartPainter().getStyleManager().getPlotPadding()
+          - getChartPainter().getStyleManager().getChartPadding();
       Rectangle yAxisRectangle = new Rectangle(xOffset, yOffset, width, height);
       this.paintZone = yAxisRectangle;
       // g.setColor(Color.green);
@@ -190,9 +192,9 @@ public class Axis implements ChartPart {
 
       int xOffset = (int) (axisPair.getyAxis().getBounds().getWidth() + (getChartPainter().getStyleManager().isYAxisTicksVisible() ? getChartPainter().getStyleManager().getPlotPadding() : 0) + getChartPainter()
           .getStyleManager().getChartPadding());
-      int yOffset = (int) (axisPair.getyAxis().getBounds().getY() + axisPair.getyAxis().getBounds().getHeight());
+      int yOffset = (int) (axisPair.getyAxis().getBounds().getY() + axisPair.getyAxis().getBounds().getHeight() + getChartPainter().getStyleManager().getPlotPadding());
 
-      int chartLegendWidth = 0;
+      double chartLegendWidth = 0;
       if (getChartPainter().getStyleManager().getLegendPosition() == LegendPosition.OutsideE) {
         chartLegendWidth = getChartPainter().getChartLegend().getSizeHint()[0];
       }
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 5edf008c..52285376 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
@@ -19,6 +19,7 @@ import java.awt.Graphics2D;
 import java.awt.Rectangle;
 import java.awt.font.FontRenderContext;
 import java.awt.font.TextLayout;
+import java.awt.geom.Rectangle2D;
 
 /**
  * Axis tick labels
@@ -69,7 +70,7 @@ public class AxisTickLabels implements ChartPart {
           FontRenderContext frc = g.getFontRenderContext();
           // TextLayout layout = new TextLayout(tickLabel, font, new FontRenderContext(null, true, false));
           TextLayout layout = new TextLayout(tickLabel, getChartPainter().getStyleManager().getAxisTickLabelsFont(), frc);
-          Rectangle tickLabelBounds = layout.getPixelBounds(null, 0, 0);
+          Rectangle2D tickLabelBounds = layout.getBounds();
           layout.draw(g, xOffset, (int) (yOffset + axisTick.getAxis().getPaintZone().getHeight() - tickLocation + tickLabelBounds.getHeight() / 2.0));
 
           if (tickLabelBounds.getWidth() > maxTickLabelWidth) {
@@ -96,7 +97,7 @@ 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);
-          Rectangle tickLabelBounds = layout.getPixelBounds(null, 0, 0);
+          Rectangle2D tickLabelBounds = layout.getBounds();
           layout.draw(g, (int) (xOffset + tickLocation - tickLabelBounds.getWidth() / 2.0), yOffset);
 
           if (tickLabelBounds.getHeight() > maxTickLabelHeight) {
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTitle.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTitle.java
index abf855da..5c6985fb 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTitle.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/AxisTitle.java
@@ -20,6 +20,7 @@ import java.awt.Rectangle;
 import java.awt.font.FontRenderContext;
 import java.awt.font.TextLayout;
 import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
 
 /**
  * AxisTitle
@@ -65,31 +66,19 @@ public class AxisTitle implements ChartPart {
 
         FontRenderContext frc = g.getFontRenderContext();
         TextLayout nonRotatedTextLayout = new TextLayout(text, getChartPainter().getStyleManager().getAxisTitleFont(), frc);
-        Rectangle nonRotatedRectangle = nonRotatedTextLayout.getPixelBounds(null, 0, 0);
-        // System.out.println(nonRotatedRectangle);
+        Rectangle2D nonRotatedRectangle = nonRotatedTextLayout.getBounds();
 
         // ///////////////////////////////////////////////
 
-        // AffineTransform at = new AffineTransform();
-        // // Tx.translate(anchorx, anchory); // S3: final translation
-        // double theta = Math.PI / -2.0;
-        // at.rotate(theta); // S2: rotate around anchor
-        // // Tx.translate(-anchorx, -anchory); // S1: translate anchor to origin
-        // Font derivedFont = font.deriveFont(at);
-        // TextLayout rotatedTextLayout = new TextLayout(text, derivedFont, frc);
-        // // TextLayout rotatedTextLayout = new TextLayout(text, font.deriveFont(AffineTransform.getRotateInstance(Math.PI / -2.0, 0, 0)), frc);
-        // // Rectangle rotatedRectangle = rotatedTextLayout.getPixelBounds(null, 0, 0);
-        // // System.out.println(rotatedRectangle);
-        //
         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));
         g.drawString(text, xOffset, yOffset);
-        // rotatedTextLayout.draw(g, xOffset, yOffset);
 
         // ///////////////////////////////////////////////
         g.setTransform(orig);
+        // System.out.println(nonRotatedRectangle.getHeight());
 
         // bounds
         bounds = new Rectangle((int) (xOffset - nonRotatedRectangle.getHeight()), (int) (yOffset - nonRotatedRectangle.getWidth()), (int) nonRotatedRectangle.getHeight()
@@ -106,7 +95,7 @@ public class AxisTitle implements ChartPart {
 
         FontRenderContext frc = g.getFontRenderContext();
         TextLayout textLayout = new TextLayout(text, getChartPainter().getStyleManager().getAxisTitleFont(), frc);
-        Rectangle rectangle = textLayout.getPixelBounds(null, 0, 0);
+        Rectangle2D rectangle = textLayout.getBounds();
         // System.out.println(rectangle);
 
         int xOffset = (int) (axis.getPaintZone().getX() + (axis.getPaintZone().getWidth() - rectangle.getWidth()) / 2.0);
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartTitle.java b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartTitle.java
index 395e47ae..a840a5bc 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartTitle.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/chartpart/ChartTitle.java
@@ -20,6 +20,7 @@ import java.awt.Graphics2D;
 import java.awt.Rectangle;
 import java.awt.font.FontRenderContext;
 import java.awt.font.TextLayout;
+import java.awt.geom.Rectangle2D;
 
 /**
  * Chart Title
@@ -70,7 +71,7 @@ public class ChartTitle implements ChartPart {
     if (chartPainter.getStyleManager().isChartTitleVisible()) {
 
       TextLayout textLayout = new TextLayout(text, chartPainter.getStyleManager().getChartTitleFont(), new FontRenderContext(null, true, false));
-      Rectangle rectangle = textLayout.getPixelBounds(null, 0, 0);
+      Rectangle2D rectangle = textLayout.getBounds();
       int titleHeight = (int) ((chartPainter.getStyleManager().isChartTitleVisible() ? rectangle.getHeight() : 0));
       return chartPainter.getStyleManager().getChartPadding() + 2 * chartPainter.getStyleManager().getChartTitlePadding() + titleHeight;
     } else {
@@ -89,7 +90,7 @@ public class ChartTitle implements ChartPart {
       // create rectangle first for sizing
       FontRenderContext frc = g.getFontRenderContext();
       TextLayout textLayout = new TextLayout(text, chartPainter.getStyleManager().getChartTitleFont(), frc);
-      Rectangle rectangle = textLayout.getPixelBounds(null, 0, 0);
+      Rectangle2D rectangle = textLayout.getBounds();
 
       int xOffset = (int) chartPainter.getPlot().getBounds().getX();
       int yOffset = chartPainter.getStyleManager().getChartPadding();
@@ -111,7 +112,7 @@ public class ChartTitle implements ChartPart {
       xOffset = (int) (chartPainter.getPlot().getBounds().getX() + (chartPainter.getPlot().getBounds().getWidth() - rectangle.getWidth()) / 2.0);
       yOffset = (int) (chartPainter.getStyleManager().getChartPadding() - rectangle.getY() + chartPainter.getStyleManager().getChartTitlePadding());
 
-      // bounds = new Rectangle(xOffset, yOffset + ((int) rectangle.getY()), (int) rectangle.getWidth(), (int) (rectangle.getHeight()));
+      bounds = new Rectangle(xOffset, yOffset + ((int) rectangle.getY()), (int) rectangle.getWidth(), (int) (rectangle.getHeight()));
       // g.setColor(Color.green);
       // g.draw(bounds);
 
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 fffe7f62..847f08f6 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
@@ -18,8 +18,12 @@ package com.xeiam.xchart.internal.chartpart;
 import java.awt.BasicStroke;
 import java.awt.Graphics2D;
 import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Shape;
 import java.awt.font.FontRenderContext;
 import java.awt.font.TextLayout;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
 import java.util.Map;
 
 import com.xeiam.xchart.Series;
@@ -55,51 +59,51 @@ public class Legend implements ChartPart {
    * 
    * @return
    */
-  protected int[] getSizeHint() {
+  protected double[] getSizeHint() {
 
     if (chartPainter.getStyleManager().isLegendVisible()) {
 
       Map<Integer, Series> seriesMap = chartPainter.getAxisPair().getSeriesMap();
 
       // determine legend text content max width
-      int legendTextContentMaxWidth = 0;
-      int legendTextContentMaxHeight = 0;
+      double legendTextContentMaxWidth = 0;
+      double legendTextContentMaxHeight = 0;
 
       for (Integer seriesId : seriesMap.keySet()) {
         Series series = seriesMap.get(seriesId);
         TextLayout textLayout = new TextLayout(series.getName(), chartPainter.getStyleManager().getLegendFont(), new FontRenderContext(null, true, false));
-        Rectangle rectangle = textLayout.getPixelBounds(null, 0, 0);
+        Rectangle2D rectangle = textLayout.getBounds();
         // System.out.println(rectangle);
         if (rectangle.getWidth() > legendTextContentMaxWidth) {
-          legendTextContentMaxWidth = (int) rectangle.getWidth();
+          legendTextContentMaxWidth = rectangle.getWidth();
         }
         if (rectangle.getHeight() > legendTextContentMaxHeight) {
-          legendTextContentMaxHeight = (int) rectangle.getHeight();
+          legendTextContentMaxHeight = rectangle.getHeight();
         }
       }
 
       // determine legend content height
-      int maxContentHeight = 0;
+      double maxContentHeight = 0;
       if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) {
         maxContentHeight = Math.max(legendTextContentMaxHeight, Marker.SIZE);
       } else {
         maxContentHeight = Math.max(legendTextContentMaxHeight, BOX_SIZE);
       }
-      int legendContentHeight = maxContentHeight * seriesMap.size() + chartPainter.getStyleManager().getLegendPadding() * (seriesMap.size() - 1);
+      double legendContentHeight = maxContentHeight * seriesMap.size() + chartPainter.getStyleManager().getLegendPadding() * (seriesMap.size() - 1);
 
       // determine legend content width
-      int legendContentWidth = 0;
+      double legendContentWidth = 0;
       if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) {
         legendContentWidth = (int) (3.0 * Marker.SIZE + chartPainter.getStyleManager().getLegendPadding() + legendTextContentMaxWidth);
       } else {
         legendContentWidth = BOX_SIZE + chartPainter.getStyleManager().getLegendPadding() + legendTextContentMaxWidth;
       }
       // Legend Box
-      int legendBoxWidth = legendContentWidth + 2 * chartPainter.getStyleManager().getLegendPadding();
-      int legendBoxHeight = legendContentHeight + 2 * chartPainter.getStyleManager().getLegendPadding();
-      return new int[] { legendBoxWidth, legendBoxHeight, maxContentHeight };
+      double legendBoxWidth = legendContentWidth + 2 * chartPainter.getStyleManager().getLegendPadding();
+      double legendBoxHeight = legendContentHeight + 2 * chartPainter.getStyleManager().getLegendPadding();
+      return new double[] { legendBoxWidth, legendBoxHeight, maxContentHeight };
     } else {
-      return new int[] { 0, 0, 0 };
+      return new double[] { 0, 0, 0 };
     }
   }
 
@@ -113,16 +117,19 @@ public class Legend implements ChartPart {
 
       Map<Integer, Series> seriesMap = chartPainter.getAxisPair().getSeriesMap();
 
-      int legendBoxWidth = getSizeHint()[0];
-      int legendBoxHeight = getSizeHint()[1];
-      int maxContentHeight = getSizeHint()[2];
+      double legendBoxWidth = getSizeHint()[0];
+      double legendBoxHeight = getSizeHint()[1];
+      double maxContentHeight = getSizeHint()[2];
+      // System.out.println(legendBoxWidth);
+      // System.out.println(legendBoxHeight);
+      // System.out.println(maxContentHeight);
 
       // legend draw position
       int xOffset = 0;
       int yOffset = 0;
       switch (chartPainter.getStyleManager().getLegendPosition()) {
       case OutsideE:
-        xOffset = chartPainter.getWidth() - legendBoxWidth - chartPainter.getStyleManager().getChartPadding();
+        xOffset = (int) (chartPainter.getWidth() - legendBoxWidth - chartPainter.getStyleManager().getChartPadding());
         yOffset = (int) (chartPainter.getPlot().getBounds().getY() + (chartPainter.getPlot().getBounds().getHeight() - legendBoxHeight) / 2.0);
         break;
       case InsideNW:
@@ -146,10 +153,11 @@ public class Legend implements ChartPart {
         break;
       }
       g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
-      g.setColor(chartPainter.getStyleManager().getLegendBorderColor());
-      g.drawRect(xOffset, yOffset, legendBoxWidth, legendBoxHeight);
+      Rectangle2D rect = new Rectangle2D.Double(xOffset + 1, yOffset + 1, (int) (legendBoxWidth - 2), (int) (legendBoxHeight - 2));
       g.setColor(chartPainter.getStyleManager().getLegendBackgroundColor());
-      g.fillRect(xOffset + 1, yOffset + 1, legendBoxWidth - 1, legendBoxHeight - 1);
+      g.fill(rect);
+      g.setColor(chartPainter.getStyleManager().getLegendBorderColor());
+      g.draw(rect);
 
       // Draw legend content inside legend box
       int startx = xOffset + chartPainter.getStyleManager().getLegendPadding();
@@ -161,15 +169,18 @@ public class Legend implements ChartPart {
 
           // paint line
           if (getChartPainter().getStyleManager().getChartType() != ChartType.Scatter && series.getStroke() != null) {
+            g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
             g.setColor(series.getStrokeColor());
             g.setStroke(series.getStroke());
-            g.drawLine(startx, starty + (int) (maxContentHeight / 2.0), (int) (startx + Marker.SIZE * 3.0), starty + (int) (maxContentHeight / 2.0));
+            Shape line = new Line2D.Double(startx, (int) (starty + maxContentHeight / 2.0), (startx + Marker.SIZE * 3.0), (starty + maxContentHeight / 2.0));
+            g.draw(line);
+            g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
           }
 
           // paint marker
           if (series.getMarker() != null) {
             g.setColor(series.getMarkerColor());
-            series.getMarker().paint(g, (int) (startx + (Marker.SIZE * 1.5)), starty + (int) (maxContentHeight / 2.0));
+            series.getMarker().paint(g, (int) (startx + (Marker.SIZE * 1.5)), (int) (starty + maxContentHeight / 2.0));
           }
         } else {
           // paint little box
@@ -182,19 +193,20 @@ public class Legend implements ChartPart {
         }
 
         // paint series name
+
         g.setColor(chartPainter.getStyleManager().getChartFontColor());
         TextLayout layout = new TextLayout(series.getName(), chartPainter.getStyleManager().getLegendFont(), new FontRenderContext(null, true, false));
         if (getChartPainter().getStyleManager().getChartType() != ChartType.Bar) {
           layout.draw(g, (float) (startx + Marker.SIZE + (Marker.SIZE * 1.5) + chartPainter.getStyleManager().getLegendPadding()), starty
-              + (int) ((maxContentHeight + layout.getPixelBounds(null, 0, 0).getHeight()) / 2.0));
+              + (int) ((maxContentHeight - 1 + layout.getBounds().getHeight()) / 2.0));
         } else {
-          layout.draw(g, startx + BOX_SIZE + chartPainter.getStyleManager().getLegendPadding(), starty + (int) ((maxContentHeight + layout.getPixelBounds(null, 0, 0).getHeight()) / 2.0));
+          layout.draw(g, startx + BOX_SIZE + chartPainter.getStyleManager().getLegendPadding(), starty + (int) ((maxContentHeight + layout.getBounds().getHeight()) / 2.0));
         }
-        starty = starty + maxContentHeight + chartPainter.getStyleManager().getLegendPadding();
+        starty = (int) (starty + maxContentHeight + chartPainter.getStyleManager().getLegendPadding());
       }
 
       // bounds
-      bounds = new Rectangle(xOffset, yOffset, legendBoxWidth, legendBoxHeight);
+      bounds = new Rectangle(xOffset, yOffset, (int) (legendBoxWidth), (int) (legendBoxHeight));
       // g.setColor(Color.blue);
       // g.draw(bounds);
     }
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Circle.java b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Circle.java
index b3d1950d..94df961e 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Circle.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Circle.java
@@ -16,6 +16,9 @@
 package com.xeiam.xchart.internal.markers;
 
 import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.geom.Ellipse2D;
 
 /**
  * @author timmolter
@@ -26,7 +29,10 @@ public class Circle extends Marker {
   public void paint(Graphics2D g, int xOffset, int yOffset) {
 
     g.setStroke(stroke);
-    g.fillOval(xOffset + Marker.X_OFFSET, yOffset + Marker.Y_OFFSET, Marker.SIZE, Marker.SIZE);
+    g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
+    Shape circle = new Ellipse2D.Double(xOffset + Marker.X_OFFSET, yOffset + Marker.Y_OFFSET, Marker.SIZE, Marker.SIZE);
+    g.fill(circle);
+    g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
 
   }
 
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Diamond.java b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Diamond.java
index 13f43d0b..4d2dac52 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Diamond.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Diamond.java
@@ -17,6 +17,8 @@ package com.xeiam.xchart.internal.markers;
 
 import java.awt.Graphics2D;
 import java.awt.Polygon;
+import java.awt.RenderingHints;
+import java.awt.Shape;
 
 /**
  * @author timmolter
@@ -36,16 +38,18 @@ public class Diamond extends Marker {
     int halfSize = (int) (Math.ceil((Marker.SIZE + 3) / 2.0));
     x[0] = xOffset - halfSize + 0;
     x[1] = xOffset - halfSize + halfSize;
-    x[2] = xOffset - halfSize + Marker.SIZE + 3;
+    x[2] = (int) (xOffset - halfSize + Marker.SIZE + 3);
     x[3] = xOffset - halfSize + halfSize;
 
     y[0] = 1 + yOffset - halfSize + halfSize;
-    y[1] = 1 + yOffset - halfSize + Marker.SIZE + 3;
+    y[1] = (int) (1 + yOffset - halfSize + Marker.SIZE + 3);
     y[2] = 1 + yOffset - halfSize + halfSize;
     y[3] = 1 + yOffset - halfSize + 0;
 
-    Polygon diamond = new Polygon(x, y, n);
-    g.fillPolygon(diamond);
+    Shape diamond = new Polygon(x, y, n);
+    g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
+    g.fill(diamond);
+    g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
 
   }
 
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Marker.java b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Marker.java
index da1b55f9..d4e28326 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Marker.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Marker.java
@@ -25,10 +25,10 @@ public abstract class Marker {
 
   protected BasicStroke stroke = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
 
-  public static final int SIZE = 7; // make this an odd number!
+  public static final double SIZE = 8;
 
-  public static final int X_OFFSET = (int) (-1.0 * (SIZE / 2.0));
-  public static final int Y_OFFSET = (int) (-1.0 * (SIZE / 2.0));
+  public static final double X_OFFSET = (int) (-1.0 * (SIZE / 2.0));
+  public static final double Y_OFFSET = (int) (-1.0 * (SIZE / 2.0));
 
   public abstract void paint(Graphics2D g, int xOffset, int yOffset);
 }
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Square.java b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Square.java
index 02503bca..7012ee24 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/markers/Square.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/markers/Square.java
@@ -16,7 +16,9 @@
 package com.xeiam.xchart.internal.markers;
 
 import java.awt.Graphics2D;
-import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
 
 /**
  * @author timmolter
@@ -27,7 +29,10 @@ public class Square extends Marker {
   public void paint(Graphics2D g, int xOffset, int yOffset) {
 
     g.setStroke(stroke);
-    g.fill(new Rectangle(xOffset + Marker.X_OFFSET, yOffset + Marker.Y_OFFSET, Marker.SIZE, Marker.SIZE));
+    g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
+    Shape square = new Rectangle2D.Double(xOffset + Marker.X_OFFSET, yOffset + Marker.Y_OFFSET, Marker.SIZE, Marker.SIZE);
+    g.fill(square);
+    g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
 
   }
 
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleDown.java b/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleDown.java
index 59325ad0..8730551c 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleDown.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleDown.java
@@ -17,6 +17,8 @@ package com.xeiam.xchart.internal.markers;
 
 import java.awt.Graphics2D;
 import java.awt.Polygon;
+import java.awt.RenderingHints;
+import java.awt.Shape;
 
 /**
  * @author timmolter
@@ -36,14 +38,16 @@ public class TriangleDown extends Marker {
     int halfSize = (int) (Math.ceil((Marker.SIZE + 1) / 2.0));
     x[0] = xOffset - halfSize + 0;
     x[1] = xOffset - halfSize + halfSize;
-    x[2] = xOffset - halfSize + Marker.SIZE + 1;
+    x[2] = (int) (xOffset - halfSize + Marker.SIZE + 1);
 
     y[0] = 1 + yOffset - halfSize + 0;
-    y[1] = 1 + yOffset - halfSize + Marker.SIZE + 1;
+    y[1] = (int) (1 + yOffset - halfSize + Marker.SIZE + 1);
     y[2] = 1 + yOffset - halfSize + 0;
 
-    Polygon triangle = new Polygon(x, y, n);
-    g.fillPolygon(triangle);
+    Shape triangle = new Polygon(x, y, n);
+    g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
+    g.fill(triangle);
+    g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
 
   }
 }
diff --git a/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleUp.java b/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleUp.java
index 15ded241..d8b48281 100644
--- a/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleUp.java
+++ b/xchart/src/main/java/com/xeiam/xchart/internal/markers/TriangleUp.java
@@ -17,6 +17,8 @@ package com.xeiam.xchart.internal.markers;
 
 import java.awt.Graphics2D;
 import java.awt.Polygon;
+import java.awt.RenderingHints;
+import java.awt.Shape;
 
 /**
  * @author timmolter
@@ -35,15 +37,17 @@ public class TriangleUp extends Marker {
     // Make a triangle
     int halfSize = (int) (Math.ceil((Marker.SIZE + 1) / 2.0));
     x[0] = xOffset - halfSize + 0;
-    x[1] = xOffset - halfSize + Marker.SIZE + 1;
+    x[1] = (int) (xOffset - halfSize + Marker.SIZE + 1);
     x[2] = xOffset - halfSize + halfSize;
 
-    y[0] = yOffset - halfSize + Marker.SIZE + 1;
-    y[1] = yOffset - halfSize + Marker.SIZE + 1;
+    y[0] = (int) (yOffset - halfSize + Marker.SIZE + 1);
+    y[1] = (int) (yOffset - halfSize + Marker.SIZE + 1);
     y[2] = yOffset - halfSize + 0;
 
-    Polygon triangle = new Polygon(x, y, n);
-    g.fillPolygon(triangle);
+    Shape triangle = new Polygon(x, y, n);
+    g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
+    g.fill(triangle);
+    g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
 
   }
 }
-- 
GitLab