Search code examples
jasper-reportsjfreechart

Image as category label in iReport chart


I'm trying to create a bar chart with images for the category labels (x-axis labels). For the default bar chart, the result of the category expression is used as the label (and must be a String). How can I customize this to show an image instead? I've written a scriptlet to populate a variable with BufferedImages, now I just need a way to use them. Can I do this with a chart customizer class? Is there an easier/better way?


Solution

  • As you said, you can do that by own customizer class. Here is a simple example which can be enhanced by your needs:

    @Slf4j 
    public class CategoryAxisWithImagesCustomizer extends JRAbstractChartCustomizer {
        public class CategoryAxisWithImages extends CategoryAxis {
            public CategoryAxisWithImages(String label) {
                super(label);
            }
    
            @Override
            protected Rectangle2D getLabelEnclosure(Graphics2D g2, RectangleEdge edge) {
                // enter max width and height of your images or you can do it dynamically
                return new Rectangle2D.Double(0, 0, 32, 32);
            }
    
            @Override
            protected AxisState drawCategoryLabels(Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, AxisState state, PlotRenderingInfo plotState) {
                if (!isTickLabelsVisible()) {
                    return state;
                }
    
                List ticks = refreshTicks(g2, state, plotArea, edge);
                state.setTicks(ticks);
                for (int i = 0; i < ticks.size(); i++) {
                    double x = getCategoryMiddle(i, ticks.size(), dataArea, edge);
                    double y = state.getCursor() + getCategoryLabelPositionOffset();
    
                    int value = (int) ((CategoryPlot) getPlot()).getDataset().getColumnKey(i);
                    String imagePath = "logo_" + value + ".png";
                    try {
                        InputStream imageStream = getClass().getResourceAsStream(imagePath);
                        // you can of course load images using different way - here I'm using index value from the dataset
                        BufferedImage image = ImageIO.read(imageStream);
                        g2.drawImage(image, (int) (x - image.getWidth() / 2d), (int) (y), image.getWidth(), image.getHeight(), Color.black, null);
                    } catch (IOException e) {
                        log.error("Cannot load image {}", imagePath);
                    }
                }
                state.cursorDown(state.getMax() + getCategoryLabelPositionOffset());
                return state;
            }
        }
    
        @Override
        public void customize(JFreeChart chart, JRChart jasperChart) {
            CategoryPlot plot = chart.getCategoryPlot();
            CategoryAxis categoryAxis = new CategoryAxisWithImages(plot.getDomainAxis().getLabel());
            plot.setDomainAxis(categoryAxis);
        }
    }
    

    In JasperReport JRXML you can register that customizer class like this:

        <barChart>
            <chart customizerClass="cz.trask.experiment.jr.CategoryAxisWithImagesCustomizer"
                   isShowLegend="false">
                <reportElement x="0" y="0" width="550" height="348" uuid="a2cbb6b5-a76d-469b-8e8e-daa533260f20"/>
                <chartTitle/>
                <chartSubtitle/>
                <chartLegend/>
            </chart>
            ...
        </barChart>
    

    And the result of my example is: Example JasperReport with chart using images in category axis