【问题标题】:Resizing legend and axis title in a apache poi bar chart在 apache poi 条形图中调整图例和轴标题的大小
【发布时间】:2021-08-23 13:00:53
【问题描述】:

我使用 apache poi 绘制了一个 3D 堆叠条形图,但我无法处理左轴标题的字体大小和图表的图例。我查看了找到 here 的 ooxml 文档,但无法确定哪个对象对此负责。我添加了一段代码和生成的图表,感谢任何帮助我走上正确的道路。

    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

【问题讨论】:

    标签: java excel charts apache-poi bar-chart


    【解决方案1】:

    我发现哪个 xml 对象处理了条形图轴和图例的字体样式。我基本上是通过比较我的 java 代码生成的底层xl/charts/chart1.xml 和我使用 ms excel 手动完成的另一个条形图来做到这一点的。我必须这样做,因为对于值轴,apache poi 的XDDFValueAxis 对象没有实现轴标题操作方法,并且需要org.openxmlformats.schemas.drawingml.x2006 API。正如我在问题中提到的那样,可以在here 找到该文档。文档的问题在于每个对象的功能并不明显(至少对我而言),因此,比较和分析 xml 对象可以深入了解某些对象的实际功能。这是我的代码片段,它具有 apache poi 4.1.2poi-ooxml-schemas-4.1.2 依赖项。希望对遇到类似问题的人有所帮助。

        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*/
    

    【讨论】:

      【解决方案2】:

      至少对于图例的字体大小设置,使用org.apache.poi.xddf.usermodel.text.XDDFTextBody 是可能的。这样做的好处是这是一个高级的apache poi 类,可以进一步开发。所以如果你有XDDFChartLegend legend,那么从这里构造一个XDDFTextBody,并用它来设置字体。

      例子:

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

      对于轴字体设置,需要使用低级org.openxmlformats.schemas.drawingml.x2006.chart.CTValAxorg.openxmlformats.schemas.drawingml.x2006.chart.CTCatAx。但也有org.apache.poi.xddf.usermodel.chart.XDDFTitle,它是CTValAx-title 或CTCatAx-title 的高级包装。所以我们应该使用它而不是直接使用CT* 类。

      例子:

      拥有XDDFTitle getOrSetAxisTitle 方法:

      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;            
          }
      }
      

      然后就这样使用它们:

      ...
              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);
      ...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-04-13
        • 1970-01-01
        • 1970-01-01
        • 2021-07-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多