1

I drew a 3D stacked bar chart using apache poi and I have trouble manipulating the font size of the left axis title and the legend of the chart. I have looked at the ooxml documentation found here but couldn't identify which object is responsible for this. I have added a piece of my code and the resulting chart, I appreciate any help directing me on the right path.

    XSSFSheet sheet = wb.getSheetAt(0);

    // Bar chart coordinates
    XSSFDrawing drawing = sheet.createDrawingPatriarch();
    XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 37, 16, 47);

    XSSFChart chart = drawing.createChart(anchor);

    // configure axis properties
    XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);

    XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
    leftAxis.setTitle("Call Duration");

    // font size for left axis labels (ticks)
    leftAxis.getOrAddTextProperties().setFontSize(8d);

    leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
    leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);

    XDDFChartData data = chart.createData(ChartTypes.BAR3D, bottomAxis, leftAxis);

    // create legend
    XDDFChartLegend legend = chart.getOrAddLegend();
    legend.setPosition(LegendPosition.BOTTOM);

    /*DATA ADDITION HERE*/

output bar chart

program_k
  • 31
  • 5

2 Answers2

2

I have found which xml object addresses the font style of the the bar graph axis and legend. I did it basically by comparing the underlying xl/charts/chart1.xml generated by my java code and another bar graph that I had done with hand using ms excel. I had to do all this because, say for the value axis, apache poi's XDDFValueAxis object doesn't implement the axis title manipulation methods and org.openxmlformats.schemas.drawingml.x2006 API is needed. As I mentioned in my question the doc can be found here. The problem with the documentation is it's not apparent each objects functionality (at least for me), therefore, comparing and analyzing the xml objects gives insight to what actually some of the objects are responsible for. Here's my code piece and it has apache poi 4.1.2 and poi-ooxml-schemas-4.1.2 dependencies. Hope it'll be of help for someone with a similar problem.

    XSSFSheet sheet = wb.getSheetAt(0);

    // Bar chart coordinates
    XSSFDrawing drawing = sheet.createDrawingPatriarch();
    XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 38, 16, 47);

    XSSFChart chart = drawing.createChart(anchor);

    // configure axis properties
    XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
    bottomAxis.getOrAddTextProperties().setFontSize(6d);

    XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
    leftAxis.setTitle("Call Duration");
    leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
    leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);

    // font size for left axis labels (ticks)
    leftAxis.getOrAddTextProperties().setFontSize(6d);

    // create legend
    XDDFChartLegend legend = chart.getOrAddLegend();
    legend.setPosition(LegendPosition.BOTTOM);

    // reflect the underlying the xml objects in order to access fields that are not implemented in apache poi
    org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx ctValAx = null;
    org.openxmlformats.schemas.drawingml.x2006.chart.CTLegend ctLegend = null;
    java.lang.reflect.Field ctValRef;
    java.lang.reflect.Field chartLegendRef;
    try {
        ctValRef = XDDFValueAxis.class.getDeclaredField("ctValAx");
        ctValRef.setAccessible(true);
        ctValAx = (org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx) ctValRef.get(leftAxis);

        chartLegendRef = XDDFChartLegend.class.getDeclaredField("legend");
        chartLegendRef.setAccessible(true);
        ctLegend = (org.openxmlformats.schemas.drawingml.x2006.chart.CTLegend) chartLegendRef.get(legend);
    } catch (NoSuchFieldException | IllegalAccessException e) {
        e.printStackTrace();
    }

    // set title properties for left axis
    CTTitle ctTitle = ctValAx.getTitle();
    ctTitle.getTx().getRich().getPArray(0).getRArray(0).getRPr().setSz(600);

    // adjust the font size of the legend
    ctLegend.addNewTxPr();
    ctLegend.getTxPr().addNewBodyPr();
    ctLegend.getTxPr().addNewLstStyle(); // font size in hundreds format (6*100)
    ctLegend.getTxPr().addNewP().addNewPPr().addNewDefRPr().setSz(600);

/*DATA ADDITION HERE*/
program_k
  • 31
  • 5
1

At least for the legend's font size setting using org.apache.poi.xddf.usermodel.text.XDDFTextBody is possible. That has the advantage that this is a high level apache poi class which gets developed further. So if you have XDDFChartLegend legend, then construct a XDDFTextBody from this and use this for font settings.

Example:

...
        XDDFChartLegend legend = chart.getOrAddLegend();
        legend.setPosition(LegendPosition.BOTTOM);
        
        XDDFTextBody legendTextBody = new XDDFTextBody(legend);
        legendTextBody.getXmlObject().addNewBodyPr();
        legendTextBody.addNewParagraph().addDefaultRunProperties().setFontSize(8d);
        legend.setTextBody(legendTextBody);
...

For axis font settings using the low level org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx or org.openxmlformats.schemas.drawingml.x2006.chart.CTCatAx is necessary. But there also is org.apache.poi.xddf.usermodel.chart.XDDFTitle which is the high level wrapper for CTValAx-title or CTCatAx-title. So we should using that instead of directly using the CT* classes.

Example:

Do having XDDFTitle getOrSetAxisTitle methods:

private static XDDFTitle getOrSetAxisTitle(XDDFValueAxis axis) {
    try {
        java.lang.reflect.Field _ctValAx = XDDFValueAxis.class.getDeclaredField("ctValAx");
        _ctValAx.setAccessible(true);
        org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx ctValAx =
            (org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx)_ctValAx.get(axis);
        if (!ctValAx.isSetTitle()) {
            ctValAx.addNewTitle();
        }
        XDDFTitle title = new XDDFTitle(null, ctValAx.getTitle());
        return title;
    } catch (Exception ex) {
        ex.printStackTrace();
        return null;            
    }
}

private static XDDFTitle getOrSetAxisTitle(XDDFCategoryAxis axis) {
    try {
        java.lang.reflect.Field _ctCatAx = XDDFCategoryAxis.class.getDeclaredField("ctCatAx");
        _ctCatAx.setAccessible(true);
        org.openxmlformats.schemas.drawingml.x2006.chart.CTCatAx ctCatAx =
            (org.openxmlformats.schemas.drawingml.x2006.chart.CTCatAx)_ctCatAx.get(axis);
        if (!ctCatAx.isSetTitle()) {
            ctCatAx.addNewTitle();
        }
        XDDFTitle title = new XDDFTitle(null, ctCatAx.getTitle());
        return title;
    } catch (Exception ex) {
        ex.printStackTrace();
        return null;            
    }
}

Then do using those as so:

...
        XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
        //bottomAxis.setTitle("...");
        XDDFTitle title = getOrSetAxisTitle(bottomAxis);
        title.setOverlay(false);
        title.setText("...");
        title.getBody().getParagraph(0).addDefaultRunProperties().setFontSize(8d);
        
        bottomAxis.getOrAddTextProperties().setFontSize(8d);
    
        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
        //leftAxis.setTitle("...");
        title = getOrSetAxisTitle(leftAxis);
        title.setOverlay(false);
        title.setText("...");
        title.getBody().getParagraph(0).addDefaultRunProperties().setFontSize(8d);
        
        leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
        leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);

        leftAxis.getOrAddTextProperties().setFontSize(8d);
...
Axel Richter
  • 56,077
  • 6
  • 60
  • 87