Search code examples
javagraphalignmentjfreechart

JFreeCharts Legend Text and Color Box Don't align


I'm using JFreeCharts and creating a stacked bar graph. I have created a legend, but when I change the font for the legend to something like Calibri size 9, the text is not vertically aligned with the colored boxes.

The legend appears as follows: enter image description here

The text is not vertically aligned with the boxes, It's like there is padding underneath or something. The following is my code for the customization of the chart

public JFreeChart createStackedChart(final CategoryDataset categorydataset, String Title) throws DocumentException, IOException {
    final JFreeChart chart = ChartFactory.createStackedBarChart(
            Title, // chart title
            "", // domain axis label
            "", // range axis label
            categorydataset, // data
            PlotOrientation.VERTICAL, // the plot orientation
            true, // legend
            true, // tooltips
            false // urls
    );


    chart.setTitle(
            new org.jfree.chart.title.TextTitle(Title,
                    new java.awt.Font("Calibri", java.awt.Font.PLAIN, 12)
            ));
    int columnCount = categorydataset.getColumnCount();
    chart.setBackgroundPaint(Color.white);
    chart.getTitle().setPadding(10, 2, 10, 2);
    chart.setBorderVisible(true);
    chart.setBorderPaint(Color.lightGray);//akash change

    final CategoryPlot plot = chart.getCategoryPlot();
    plot.setBackgroundPaint(Color.white);

    plot.setDomainGridlinePaint(Color.lightGray);
    plot.setRangeGridlinePaint(Color.lightGray);
    plot.setDomainGridlineStroke(new BasicStroke(0.5f));
    plot.setRangeGridlineStroke(new BasicStroke(0.5f));
    plot.setOutlineVisible(true);
    plot.setOutlinePaint(Color.lightGray);
    plot.setAxisOffset(new RectangleInsets(0, 0, 0, 0));

    final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
    rangeAxis.setTickLabelFont(new Font("Calibri", Font.PLAIN, 10));
    rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
    rangeAxis.setAutoRangeStickyZero(false);
    rangeAxis.setLowerMargin(0.0);

    rangeAxis.setTickLabelPaint(Color.black);
    rangeAxis.setTickMarkPaint(Color.lightGray);
    rangeAxis.setTickMarkStroke(new BasicStroke(0.3f));
    rangeAxis.setAxisLineStroke(new BasicStroke(0.3f));
    rangeAxis.setLowerBound(0.0f);

    rangeAxis.setAxisLinePaint(Color.lightGray);

    GroupedStackedBarRenderer renderer = new GroupedStackedBarRenderer();
    renderer.setDrawBarOutline(false);
    renderer.setBarPainter(new StandardBarPainter());

    Paint p1 = new GradientPaint(
            0.0f, 0.0f, new Color(0x22, 0x22, 0xFF), 0.0f, 0.0f, new Color(0x88, 0x88, 0xFF)
    );
    renderer.setSeriesPaint(0, p1);
    renderer.setSeriesPaint(4, p1);
    renderer.setSeriesPaint(8, p1);

    Paint p2 = new GradientPaint(
            0.0f, 0.0f, new Color(0x22, 0xFF, 0x22), 0.0f, 0.0f, new Color(0x88, 0xFF, 0x88)
    );
    renderer.setSeriesPaint(1, p2);
    renderer.setSeriesPaint(5, p2);
    renderer.setSeriesPaint(9, p2);

    Paint p3 = new GradientPaint(
            0.0f, 0.0f, new Color(0xFF, 0x22, 0x22), 0.0f, 0.0f, new Color(0xFF, 0x88, 0x88)
    );
    renderer.setSeriesPaint(2, p3);
    renderer.setSeriesPaint(6, p3);
    renderer.setSeriesPaint(10, p3);

    Paint p4 = new GradientPaint(
            0.0f, 0.0f, new Color(0xFF, 0xFF, 0x22), 0.0f, 0.0f, new Color(0xFF, 0xFF, 0x88)
    );
    renderer.setSeriesPaint(3, p4);
    renderer.setSeriesPaint(7, p4);
    renderer.setSeriesPaint(11, p4);
    renderer.setGradientPaintTransformer(
            new StandardGradientPaintTransformer(GradientPaintTransformType.HORIZONTAL)
    );

    final CategoryAxis domainAxis = plot.getDomainAxis();
    domainAxis.setTickLabelPaint(Color.black);
    domainAxis.setTickLabelFont(new Font("Calibri", Font.PLAIN, 10));
    Color transparent = new Color(0, 0, 0, 0);
    domainAxis.setAxisLinePaint(transparent);
    domainAxis.setTickMarkPaint(Color.darkGray);
    domainAxis.setTickMarkStroke(new BasicStroke(0.3f));
    domainAxis.setMaximumCategoryLabelWidthRatio(4f);

    if (columnCount == 2) {
        domainAxis.setCategoryMargin(.6);
        domainAxis.setLowerMargin(0.015);
        domainAxis.setUpperMargin(0.015);
    } else if (columnCount == 3) {
        domainAxis.setCategoryMargin(.35);
        domainAxis.setLowerMargin(0.15);
        domainAxis.setUpperMargin(0.15);
    } else {
        domainAxis.setCategoryMargin(.55);
        domainAxis.setLowerMargin(0.015);
        domainAxis.setUpperMargin(0.015);
    }

    LegendTitle legend = chart.getLegend();//akash change
    legend.setPosition(RectangleEdge.TOP);
    legend.setItemPaint(Color.black);
    chart.getLegend()
            .setFrame(BlockBorder.NONE);
    legend.setItemPaint(Color.GRAY);
    Font labelFont = new Font("Calibri", Font.PLAIN, 9);
    legend.setItemFont(labelFont);

    if (columnCount >= 5) {
        domainAxis.setCategoryLabelPositions(
                CategoryLabelPositions.createUpRotationLabelPositions(Math.PI / 4.0));
        chart.setPadding(new RectangleInsets(0, 5, 0, 5));
    } else {
        domainAxis.setCategoryLabelPositions(
                STANDARD);
    }
    plot.setDomainAxis(domainAxis);

    return chart;

}

The important lines include the areas in which I create the legend and change the font size. Thanks again.


Solution

  • Starting from this complete example, I see the expected result, illustrated below. Note that the relevant ChartFactory "applies the default JFree chart theme," which defaults to Tahoma, also a sans-serif font. I've exaggerated the size for convenience, and I've also used deriveFont() to change just the size.

    LegendTitle legend = chart.getLegend();
    …
    Font labelFont = legend.getItemFont().deriveFont(20f);
    legend.setItemFont(labelFont);
    

    image

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.Font;
    import javax.swing.BorderFactory;
    import javax.swing.JPanel;
    import javax.swing.JSlider;
    import javax.swing.event.ChangeEvent;
    import javax.swing.event.ChangeListener;
    import org.jfree.chart.ChartFactory;
    import org.jfree.chart.ChartPanel;
    import org.jfree.chart.JFreeChart;
    import org.jfree.chart.block.BlockBorder;
    import org.jfree.chart.plot.CategoryPlot;
    import org.jfree.chart.plot.PlotOrientation;
    import org.jfree.chart.renderer.category.GroupedStackedBarRenderer;
    import org.jfree.chart.title.LegendTitle;
    import org.jfree.data.KeyToGroupMap;
    import org.jfree.data.category.CategoryDataset;
    import org.jfree.data.category.DefaultCategoryDataset;
    import org.jfree.chart.ui.ApplicationFrame;
    import org.jfree.chart.ui.RectangleEdge;
    
    /**
     * @see https://stackoverflow.com/a/17342522/230513
     */
    public class DomainTranslateDemo extends ApplicationFrame {
    
        private static class DemoPanel extends JPanel implements ChangeListener {
    
            private static final int SLIDER_INITIAL_VALUE = -42;
            private static final double INCREMENT = 1 / 10.0;
            private final JSlider slider;
            private GroupedStackedBarRenderer renderer;
    
            public DemoPanel() {
                super(new BorderLayout());
                final CategoryDataset dataset = createDataset();
                final JFreeChart chart = createChart(dataset);
                final ChartPanel chartPanel = new ChartPanel(chart) {
    
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(640, 360);
                    }
                };
                add(chartPanel);
    
                JPanel dashboard = new JPanel(new BorderLayout());
                dashboard.setBorder(BorderFactory.createEmptyBorder(0, 4, 4, 4));
    
                this.slider = new JSlider(-50, 10, SLIDER_INITIAL_VALUE);
                this.slider.addChangeListener(this);
                dashboard.add(this.slider);
                add(dashboard, BorderLayout.SOUTH);
            }
    
            private CategoryDataset createDataset() {
                DefaultCategoryDataset result = new DefaultCategoryDataset();
                result.addValue(1.5, "5YEUR", "5Y");
                result.addValue(-1.5, "5YUSD", "5Y");
                result.addValue(0.5, "5Yx5YEUR", "5Yx5Y");
                result.addValue(-0.5, "5Yx5YUSD", "5Yx5Y");
                result.addValue(1.5, "10Yx5YEUR", "10Yx5Y");
                result.addValue(-1.5, "10Yx5YUSD", "10Yx5Y");
                result.addValue(1.5, "15Yx5YEUR", "15Yx5Y");
                result.addValue(-1.5, "15Yx5YUSD", "15Yx5Y");
                result.addValue(1.5, "20Yx5YEUR", "20Yx5Y");
                result.addValue(-1.5, "20Yx5YUSD", "20Yx5Y");
                result.addValue(1.5, "20Yx5YEUR", "25Yx5Y");
                result.addValue(-1.5, "20Yx5YUSD", "25Yx5Y");
                return result;
            }
    
            private JFreeChart createChart(final CategoryDataset dataset) {
    
                final JFreeChart chart = ChartFactory.createStackedBarChart(
                    "Max Bar Width", // chart title
                    "Category", // domain axis label
                    "Value", // range axis label
                    dataset, // data
                    PlotOrientation.VERTICAL, // the plot orientation
                    true, // legend
                    true, // tooltips
                    false // urls
                );
    
                LegendTitle legend = chart.getLegend();
                legend.setPosition(RectangleEdge.TOP);
                legend.setFrame(BlockBorder.NONE);
                Font labelFont = legend.getItemFont().deriveFont(20f);
                legend.setItemFont(labelFont);
    
                renderer = new GroupedStackedBarRenderer();
                KeyToGroupMap map = new KeyToGroupMap("G1");
                map.mapKeyToGroup("5YEUR", "G1");
                map.mapKeyToGroup("5YUSD", "G1");
                map.mapKeyToGroup("5Yx5YEUR", "G2");
                map.mapKeyToGroup("5Yx5YUSD", "G2");
                map.mapKeyToGroup("10Yx5YEUR", "G3");
                map.mapKeyToGroup("10Yx5YUSD", "G3");
                map.mapKeyToGroup("15Yx5YEUR", "G4");
                map.mapKeyToGroup("15Yx5YUSD", "G4");
                map.mapKeyToGroup("20Yx5YEUR", "G5");
                map.mapKeyToGroup("20Yx5YUSD", "G5");
                map.mapKeyToGroup("25Yx5YEUR", "G6");
                map.mapKeyToGroup("25Yx5YUSD", "G6");
                renderer.setSeriesToGroupMap(map);
                renderer.setItemMargin(SLIDER_INITIAL_VALUE * INCREMENT);
    
                CategoryPlot plot = (CategoryPlot) chart.getPlot();
                plot.setRenderer(renderer);
                return chart;
            }
    
            @Override
            public void stateChanged(ChangeEvent event) {
                int value = slider.getValue();
                renderer.setItemMargin(value * INCREMENT);
            }
        }
    
        public DomainTranslateDemo(String title) {
            super(title);
            setContentPane(new DemoPanel());
        }
    
        public static JPanel createDemoPanel() {
            return new DemoPanel();
        }
    
        public static void main(String[] args) {
            DomainTranslateDemo demo = new DomainTranslateDemo("Translate Demo");
            demo.pack();
            demo.setLocationRelativeTo(null);
            demo.setVisible(true);
        }
    }