Как добавить диаграмму XDDF в определенный прогон абзаца (т. Е. В ячейку таблицы)? POI 4.0.1 - PullRequest
0 голосов
/ 12 октября 2019

Я хочу добавить диаграмму в определенную ячейку таблицы в XWPFDocument. Мне нужна диаграмма в ячейке таблицы, чтобы можно было выровнять ее с другими элементами, которые я добавлю позже. Итак,

  • Как создать диаграмму XWPF / XDDF без добавления в нее документа (например, без использования document.createChart ())?

  • Как взять эту диаграмму и добавить ее в определенный абзац / прогон (например, тот, который создан в ячейке таблицы)?

Уже пробовал:

  1. Создание диаграммы, отражающей код, найденный в XWPFDocument.createChart ()
  2. Использование XWPFRun.addChart () с помощью R elationPart.getRelationship.getId () Я использовал с
  3. Использование document.createChart () после получения в определенном месте таблицы
  4. Попытка сделать диаграмму -> XDDFDrawing и добавить это к прогону через run.getCTR.addDrawing ... Я не думаю, что так будет?

/ пример кода

// Create a document with some initial text
XWPFDocument document = new XWPFDocument();
XWPFParagraph tmpParagraph = document.createParagraph();
XWPFRun tmpRun = tmpParagraph.createRun();
tmpRun.setText("text");
tmpRun.setFontSize(18);

// Try making the chart
// the same code as here, /9782554/kak-dobavit-vtoruy-stroku-s-vtoroi-os-v-xddfchart-v-poi-4-0-1
try{
    String[] categories = new String[]{"1","2","3","4","5","6","7","8","9"};
    Double[] values1 = new Double[]{1d,2d,3d,4d,5d,6d,7d,8d,9d};
    Double[] values2 = new Double[]{200d,300d,400d,500d,600d,700d,800d,900d,1000d};

    // create the chart
    // XWPFChart chart = document.createChart(7* Units.EMU_PER_CENTIMETER, 7*Units.EMU_PER_CENTIMETER);

    // Try to make a chart stand alone
    // using the same code as here, 
    // https://svn.apache.org/viewvc/poi/tags/REL_4_0_1/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java?view=markup

    int chartNumber = document.getCharts().size();
    POIXMLDocumentPart.RelationPart rp = document.createRelationship(XWPFRelation.CHART, XWPFFactory.getInstance(), chartNumber, false);
    XWPFChart chart = rp.getDocumentPart();
    chart.setChartIndex(chartNumber);
    chart.setChartBoundingBox(7* Units.EMU_PER_CENTIMETER, 7*Units.EMU_PER_CENTIMETER);
    document.getCharts().add(chart);


    // This stuff to make the chart is not part of the question
        // create data sources
        int numOfPoints = categories.length;
        String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
        String valuesDataRange1 = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1));
        String valuesDataRange2 = chart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2));
        XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
        XDDFNumericalDataSource<Double> valuesData1 = XDDFDataSourcesFactory.fromArray(values1, valuesDataRange1, 1);
        XDDFNumericalDataSource<Double> valuesData2 = XDDFDataSourcesFactory.fromArray(values2, valuesDataRange2, 2);

        // first line chart
        XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
        leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
        XDDFChartData data = chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
        XDDFChartData.Series series = data.addSeries(categoriesData, valuesData1);
        chart.plot(data);

        solidLineSeries(data, 0, PresetColor.BLUE);

        // second line chart
        // bottom axis must be there but must not be visible
        bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
        bottomAxis.setVisible(false);

        XDDFValueAxis rightAxis = chart.createValueAxis(AxisPosition.RIGHT);
        rightAxis.setCrosses(AxisCrosses.MAX);

        // set correct cross axis
        bottomAxis.crossAxis(rightAxis);
        rightAxis.crossAxis(bottomAxis);

        data = chart.createData(ChartTypes.LINE, bottomAxis, rightAxis);
        series = data.addSeries(categoriesData, valuesData2);
        chart.plot(data);

        // correct the id and order, must not be 0 again because there is one line series already
        chart.getCTChart().getPlotArea().getLineChartArray(1).getSerArray(0).getIdx().setVal(1);
        chart.getCTChart().getPlotArea().getLineChartArray(1).getSerArray(0).getOrder().setVal(1);

        solidLineSeries(data, 0, PresetColor.RED);


// End of extra stuff
// Back to question

    // Add the chart by relation id
    XWPFParagraph p2 = document.createParagraph();
    XWPFRun r2 = p2.createRun();
    r2.addChart(rp.getRelationship().getId());

    // Add a new run to try to add a new drawing?
    XWPFRun r3 = p2.createRun();
    CTDrawing drawing = r3.getCTR().addNewDrawing();
    ????

}catch(Exception e){}

Когда я добавляю диаграмму через r2.addChart (), ничего не появляется? Так, может быть, я не правильно создал график? Или я не добавил его в прогон правильно?

Возможно ли преобразовать диаграмму в чертеж?

Это показывает XML, который я пытаюсь имитировать (скопировано с другого изображения)

  1. Абзац
  2. Выполнить
  3. Рисование?
  4. Диаграмма?

Это ожидаемый результат

1 Ответ

1 голос
/ 12 октября 2019

Как часто apache poi действительно затрудняет расширение их кода из-за странных решений о том, какие методы защищены или закрыты. В этом случае отсутствует метод public XWPFChart createChart(int width, int height, XWPFRun run) в XWPFDocument, поскольку существующие методы всегда помещают диаграмму в новый созданный прогон в новом созданном абзаце в теле документа. Но просто расширить XWPFDocument практически невозможно, потому что необходимые методы защищены или закрыты.

Самый простой подход, который я нашел, - это сначала поместить диаграмму в первый абзац документа, используя document.createChart(). Затем удалите этот первый абзац. Часть графика остается (по крайней мере, используя apache poi 4.1.0). Затем прикрепите новую часть диаграммы к нужному текстовому прогону. Но даже это не так просто, как могло бы быть, поскольку XWPFChart.attach также защищено. Поэтому необходимо использовать java.lang.reflect.

Полный пример:

import java.io.*;

import org.apache.poi.xwpf.usermodel.*;

import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.Units;

import org.apache.poi.xddf.usermodel.*;
import org.apache.poi.xddf.usermodel.chart.*;

public class CreateWordXDDFChartTwoLinesInTable {

 public static void main(String[] args) throws Exception {
  try (XWPFDocument document = new XWPFDocument()) {

   // create the data
   String[] categories = new String[]{"1","2","3","4","5","6","7","8","9"};
   Double[] values1 = new Double[]{1d,2d,3d,4d,5d,6d,7d,8d,9d};
   Double[] values2 = new Double[]{200d,300d,400d,500d,600d,700d,800d,900d,1000d};

   // create the chart
   // this also puts the chart into a run in a new created paragraph
   XWPFChart chart = createChart(document, categories, values1, values2);
   // remove the first paragraph since we need the chart being elsewhere
   document.removeBodyElement(0);

   XWPFParagraph paragraph = document.createParagraph();
   XWPFRun run = paragraph.createRun();
   run.setText("First paragraph having first text run.");

   // create the table
   XWPFTable table = document.createTable(1,2);
   table.setWidth("100%");
   // create first run in first table cell
   paragraph = table.getRow(0).getCell(0).getParagraphArray(0);
   run = paragraph.createRun();
   // attach the chart here
   java.lang.reflect.Method attach = XWPFChart.class.getDeclaredMethod("attach", String.class, XWPFRun.class);
   attach.setAccessible(true);
   attach.invoke(chart, document.getRelationId(chart), run);
   chart.setChartBoundingBox(7*Units.EMU_PER_CENTIMETER, 7*Units.EMU_PER_CENTIMETER);

   // set text in second table cell
   paragraph = table.getRow(0).getCell(1).getParagraphArray(0);
   run = paragraph.createRun();
   run.setText("Other text goes in the 2");
   run = paragraph.createRun();
   run.setSubscript(VerticalAlign.SUPERSCRIPT);
   run.setText("nd");
   run = paragraph.createRun();
   run.setText(" cell.");

   paragraph = document.createParagraph();
   run = paragraph.createRun();
   run.setText("Lorem ipsum...");

   // Write the output to a file
   try (FileOutputStream fileOut = new FileOutputStream("CreateWordXDDFChartTwoLinesInTable.docx")) {
    document.write(fileOut);
   }
  }
 }

 private static XWPFChart createChart(XWPFDocument document, 
   String[] categories, Double[] values1, Double[] values2) throws Exception {

   // create the chart
   XWPFChart chart = document.createChart();

   // create data sources
   int numOfPoints = categories.length;
   String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
   String valuesDataRange1 = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1));
   String valuesDataRange2 = chart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2));
   XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
   XDDFNumericalDataSource<Double> valuesData1 = XDDFDataSourcesFactory.fromArray(values1, valuesDataRange1, 1);
   XDDFNumericalDataSource<Double> valuesData2 = XDDFDataSourcesFactory.fromArray(values2, valuesDataRange2, 2);

   // first line chart
   XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
   XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
   leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
   XDDFChartData data = chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
   XDDFChartData.Series series = data.addSeries(categoriesData, valuesData1);
   chart.plot(data);

   solidLineSeries(data, 0, PresetColor.BLUE);

   // second line chart
   // bottom axis must be there but must not be visible
   bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
   bottomAxis.setVisible(false);

   XDDFValueAxis rightAxis = chart.createValueAxis(AxisPosition.RIGHT);
   rightAxis.setCrosses(AxisCrosses.MAX);

   // set correct cross axis
   bottomAxis.crossAxis(rightAxis);
   rightAxis.crossAxis(bottomAxis);

   data = chart.createData(ChartTypes.LINE, bottomAxis, rightAxis);
   series = data.addSeries(categoriesData, valuesData2);
   chart.plot(data);

   // correct the id and order, must not be 0 again because there is one line series already
   chart.getCTChart().getPlotArea().getLineChartArray(1).getSerArray(0).getIdx().setVal(1);
   chart.getCTChart().getPlotArea().getLineChartArray(1).getSerArray(0).getOrder().setVal(1);

   solidLineSeries(data, 0, PresetColor.RED);

   return chart;
 }

 private static void solidLineSeries(XDDFChartData data, int index, PresetColor color) {
  XDDFSolidFillProperties fill = new XDDFSolidFillProperties(XDDFColor.from(color));
  XDDFLineProperties line = new XDDFLineProperties();
  line.setFillProperties(fill);
  XDDFChartData.Series series = data.getSeries().get(index);
  XDDFShapeProperties properties = series.getShapeProperties();
  if (properties == null) {
   properties = new XDDFShapeProperties();
  }
  properties.setLineProperties(line);
  series.setShapeProperties(properties);
 }
}
...