From 2202bae2e207fac0259f12949c6ef3db9ed745de Mon Sep 17 00:00:00 2001 From: emmanueldufour Date: Wed, 17 Dec 2025 14:21:50 +0700 Subject: [PATCH 1/2] Update XWPFChart.java Word keeps cached values that need be refreshed after the embedded workbook has been updated. This is just the first step refreshing categories and series labels (tested for bar charts). TODO ultimately a function should be provided to refresh the entire cache --- .../apache/poi/xwpf/usermodel/XWPFChart.java | 110 +++++++++++++++--- 1 file changed, 93 insertions(+), 17 deletions(-) diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFChart.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFChart.java index 65070777712..21d96f911f2 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFChart.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFChart.java @@ -19,15 +19,27 @@ Licensed to the Apache Software Foundation (ASF) under one or more import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; import org.apache.poi.ooxml.POIXMLException; import org.apache.poi.ooxml.POIXMLFactory; import org.apache.poi.ooxml.POIXMLRelation; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellAddress; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.Beta; import org.apache.poi.util.IOUtils; import org.apache.poi.xddf.usermodel.chart.XDDFChart; +import org.apache.poi.xddf.usermodel.chart.XDDFChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; import org.apache.xmlbeans.XmlException; import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline; @@ -58,7 +70,7 @@ public class XWPFChart extends XDDFChart { * constructor to * Create a new chart in document * - * @since 4.0.0 + * @since POI 4.0.0 */ protected XWPFChart() { super(); @@ -69,7 +81,7 @@ protected XWPFChart() { * * @param part the package part holding the chart data, * the content type must be {@code application/vnd.openxmlformats-officedocument.drawingml.chart+xml} - * @since 4.0.0 + * @since POI 4.0.0 */ protected XWPFChart(PackagePart part) throws IOException, XmlException { super(part); @@ -116,7 +128,7 @@ public int hashCode() { * * @param chartRelId the relation id of this chart in its parent document. * @param run the text run to which this chart will be inlined. - * @since 4.0.0 + * @since POI 4.0.0 */ protected void attach(String chartRelId, XWPFRun run) throws InvalidFormatException, IOException { @@ -129,7 +141,7 @@ protected void attach(String chartRelId, XWPFRun run) * set chart height * * @param height height of chart - * @since 4.0.0 + * @since POI 4.0.0 */ public void setChartHeight(long height) { ctInline.getExtent().setCy(height); @@ -139,7 +151,7 @@ public void setChartHeight(long height) { * set chart width * * @param width width of chart - * @since 4.0.0 + * @since POI 4.0.0 */ public void setChartWidth(long width) { ctInline.getExtent().setCx(width); @@ -148,7 +160,7 @@ public void setChartWidth(long width) { /** * get chart height * - * @since 4.0.0 + * @since POI 4.0.0 */ public long getChartHeight() { return ctInline.getExtent().getCy(); @@ -157,7 +169,7 @@ public long getChartHeight() { /** * get chart width * - * @since 4.0.0 + * @since POI 4.0.0 */ public long getChartWidth() { return ctInline.getExtent().getCx(); @@ -168,7 +180,7 @@ public long getChartWidth() { * * @param width width of chart * @param height height of chart - * @since 4.0.0 + * @since POI 4.0.0 */ public void setChartBoundingBox(long width, long height) { this.setChartWidth(width); @@ -179,7 +191,7 @@ public void setChartBoundingBox(long width, long height) { * set margin from top * * @param margin margin from top - * @since 4.0.0 + * @since POI 4.0.0 */ public void setChartTopMargin(long margin) { ctInline.setDistT(margin); @@ -188,7 +200,7 @@ public void setChartTopMargin(long margin) { /** * get margin from Top * - * @since 4.0.0 + * @since POI 4.0.0 */ public long getChartTopMargin(long margin) { return ctInline.getDistT(); @@ -198,7 +210,7 @@ public long getChartTopMargin(long margin) { * set margin from bottom * * @param margin margin from Bottom - * @since 4.0.0 + * @since POI 4.0.0 */ public void setChartBottomMargin(long margin) { ctInline.setDistB(margin); @@ -207,7 +219,7 @@ public void setChartBottomMargin(long margin) { /** * get margin from Bottom * - * @since 4.0.0 + * @since POI 4.0.0 */ public long getChartBottomMargin(long margin) { return ctInline.getDistB(); @@ -217,7 +229,7 @@ public long getChartBottomMargin(long margin) { * set margin from left * * @param margin margin from left - * @since 4.0.0 + * @since POI 4.0.0 */ public void setChartLeftMargin(long margin) { ctInline.setDistL(margin); @@ -226,7 +238,7 @@ public void setChartLeftMargin(long margin) { /** * get margin from left * - * @since 4.0.0 + * @since POI 4.0.0 */ public long getChartLeftMargin(long margin) { return ctInline.getDistL(); @@ -236,7 +248,7 @@ public long getChartLeftMargin(long margin) { * set margin from Right * * @param margin from right - * @since 4.0.0 + * @since POI 4.0.0 */ public void setChartRightMargin(long margin) { ctInline.setDistR(margin); @@ -245,7 +257,7 @@ public void setChartRightMargin(long margin) { /** * get margin from Right * - * @since 4.0.0 + * @since POI 4.0.0 */ public long getChartRightMargin(long margin) { return ctInline.getDistR(); @@ -258,7 +270,7 @@ public long getChartRightMargin(long margin) { * @param right margin from right * @param bottom margin from bottom * @param left margin from left - * @since 4.0.0 + * @since POI 4.0.0 */ public void setChartMargin(long top, long right, long bottom, long left) { this.setChartBottomMargin(bottom); @@ -266,4 +278,68 @@ public void setChartMargin(long top, long right, long bottom, long left) { this.setChartLeftMargin(left); this.setChartRightMargin(right); } + /** + * Word keeps cached values that need be refreshed after the embedded workbook + * has been updated. This is just the first step refreshing categories and + * series labels (tested for bar charts). TODO ultimately a function should be + * provided to refresh the entire cache + */ + public void refreshCachedLabels() { + Workbook wb; + try { + wb = getWorkbook(); + } catch (InvalidFormatException | IOException e) { + return; + } + for (XDDFChartData chartData : getChartSeries()) { + for (int chartIndex = 0; chartIndex < chartData.getSeriesCount(); chartIndex++) { + XDDFChartData.Series series = chartData.getSeries(chartIndex); + String freshSeriesTitle=getWorkbookCellValue( series.getTitleReference(),wb); + series.setTitleCached(freshSeriesTitle); + XDDFDataSource catData = series.getCategoryData(); + if (catData == null) + continue; + String referenceFormula = catData.getFormula(); + if (referenceFormula != null) { + List cells = getWorkbookCells(referenceFormula, wb); + String[] freshCategories = new String[cells.size()]; + int categIndex = 0; + for (Cell cell : cells) { + freshCategories[categIndex++] = cell.toString(); + } + // create categories from a range formula and the values below, it will create then as "cached" values like word expects + XDDFDataSource freshCategoryDataSrouce = XDDFDataSourcesFactory.fromArray(freshCategories,referenceFormula); + series.replaceData(freshCategoryDataSrouce, (XDDFNumericalDataSource) series.getValuesData()); + } + } + //this mystery "plot" will somehow update the underlying document XML + plot(chartData); + } + } + /** + * + * @param cell must include the sheet reference + * @param wb + * @return toString() value of the cell + */ + private static String getWorkbookCellValue(CellReference cell, Workbook wb) { + + return wb.getSheet( cell.getSheetName()).getRow(cell.getRow()).getCell(cell.getCol()).toString(); + + } + /** + * + * @param rangeFormula for example: "Sheet1!$A$2:$A$5" + * @return + */ + private static List getWorkbookCells(String rangeFormula, Workbook wb) { + List cells = new ArrayList(); + String[] parts = rangeFormula.split("!");// get formula gives the ref like: Sheet1!$A$2:$A$5 + Sheet sheet = wb.getSheet(parts[0]); + CellRangeAddress range = CellRangeAddress.valueOf(parts[1]); + for (CellAddress cellAddress : range) {// the iterator goes top to bottom, left to right + cells.add(sheet.getRow(cellAddress.getRow()).getCell(cellAddress.getColumn())); + } + return cells; + } } From 66117d8f12e153016394d6299e86e6ef33216450 Mon Sep 17 00:00:00 2001 From: emmanueldufour Date: Fri, 19 Dec 2025 15:34:38 +0700 Subject: [PATCH 2/2] Update XWPFChart.java found an existing function that performs most the the cache refreshing --- .../apache/poi/xwpf/usermodel/XWPFChart.java | 92 ++++++------------- 1 file changed, 30 insertions(+), 62 deletions(-) diff --git a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFChart.java b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFChart.java index 21d96f911f2..c22ff7b7fcc 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFChart.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xwpf/usermodel/XWPFChart.java @@ -40,6 +40,8 @@ Licensed to the Apache Software Foundation (ASF) under one or more import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.xmlbeans.XmlException; import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline; @@ -70,7 +72,7 @@ public class XWPFChart extends XDDFChart { * constructor to * Create a new chart in document * - * @since POI 4.0.0 + * @since 4.0.0 */ protected XWPFChart() { super(); @@ -81,7 +83,7 @@ protected XWPFChart() { * * @param part the package part holding the chart data, * the content type must be {@code application/vnd.openxmlformats-officedocument.drawingml.chart+xml} - * @since POI 4.0.0 + * @since 4.0.0 */ protected XWPFChart(PackagePart part) throws IOException, XmlException { super(part); @@ -128,7 +130,7 @@ public int hashCode() { * * @param chartRelId the relation id of this chart in its parent document. * @param run the text run to which this chart will be inlined. - * @since POI 4.0.0 + * @since 4.0.0 */ protected void attach(String chartRelId, XWPFRun run) throws InvalidFormatException, IOException { @@ -141,7 +143,7 @@ protected void attach(String chartRelId, XWPFRun run) * set chart height * * @param height height of chart - * @since POI 4.0.0 + * @since 4.0.0 */ public void setChartHeight(long height) { ctInline.getExtent().setCy(height); @@ -151,7 +153,7 @@ public void setChartHeight(long height) { * set chart width * * @param width width of chart - * @since POI 4.0.0 + * @since 4.0.0 */ public void setChartWidth(long width) { ctInline.getExtent().setCx(width); @@ -160,7 +162,7 @@ public void setChartWidth(long width) { /** * get chart height * - * @since POI 4.0.0 + * @since 4.0.0 */ public long getChartHeight() { return ctInline.getExtent().getCy(); @@ -169,7 +171,7 @@ public long getChartHeight() { /** * get chart width * - * @since POI 4.0.0 + * @since 4.0.0 */ public long getChartWidth() { return ctInline.getExtent().getCx(); @@ -180,7 +182,7 @@ public long getChartWidth() { * * @param width width of chart * @param height height of chart - * @since POI 4.0.0 + * @since 4.0.0 */ public void setChartBoundingBox(long width, long height) { this.setChartWidth(width); @@ -191,7 +193,7 @@ public void setChartBoundingBox(long width, long height) { * set margin from top * * @param margin margin from top - * @since POI 4.0.0 + * @since 4.0.0 */ public void setChartTopMargin(long margin) { ctInline.setDistT(margin); @@ -200,7 +202,7 @@ public void setChartTopMargin(long margin) { /** * get margin from Top * - * @since POI 4.0.0 + * @since 4.0.0 */ public long getChartTopMargin(long margin) { return ctInline.getDistT(); @@ -210,7 +212,7 @@ public long getChartTopMargin(long margin) { * set margin from bottom * * @param margin margin from Bottom - * @since POI 4.0.0 + * @since 4.0.0 */ public void setChartBottomMargin(long margin) { ctInline.setDistB(margin); @@ -219,7 +221,7 @@ public void setChartBottomMargin(long margin) { /** * get margin from Bottom * - * @since POI 4.0.0 + * @since 4.0.0 */ public long getChartBottomMargin(long margin) { return ctInline.getDistB(); @@ -229,7 +231,7 @@ public long getChartBottomMargin(long margin) { * set margin from left * * @param margin margin from left - * @since POI 4.0.0 + * @since 4.0.0 */ public void setChartLeftMargin(long margin) { ctInline.setDistL(margin); @@ -238,7 +240,7 @@ public void setChartLeftMargin(long margin) { /** * get margin from left * - * @since POI 4.0.0 + * @since 4.0.0 */ public long getChartLeftMargin(long margin) { return ctInline.getDistL(); @@ -248,7 +250,7 @@ public long getChartLeftMargin(long margin) { * set margin from Right * * @param margin from right - * @since POI 4.0.0 + * @since 4.0.0 */ public void setChartRightMargin(long margin) { ctInline.setDistR(margin); @@ -257,7 +259,7 @@ public void setChartRightMargin(long margin) { /** * get margin from Right * - * @since POI 4.0.0 + * @since 4.0.0 */ public long getChartRightMargin(long margin) { return ctInline.getDistR(); @@ -270,7 +272,7 @@ public long getChartRightMargin(long margin) { * @param right margin from right * @param bottom margin from bottom * @param left margin from left - * @since POI 4.0.0 + * @since 4.0.0 */ public void setChartMargin(long top, long right, long bottom, long left) { this.setChartBottomMargin(bottom); @@ -280,12 +282,11 @@ public void setChartMargin(long top, long right, long bottom, long left) { } /** * Word keeps cached values that need be refreshed after the embedded workbook - * has been updated. This is just the first step refreshing categories and - * series labels (tested for bar charts). TODO ultimately a function should be - * provided to refresh the entire cache + * has been updated. */ public void refreshCachedLabels() { - Workbook wb; + + XSSFWorkbook wb; try { wb = getWorkbook(); } catch (InvalidFormatException | IOException e) { @@ -294,52 +295,19 @@ public void refreshCachedLabels() { for (XDDFChartData chartData : getChartSeries()) { for (int chartIndex = 0; chartIndex < chartData.getSeriesCount(); chartIndex++) { XDDFChartData.Series series = chartData.getSeries(chartIndex); - String freshSeriesTitle=getWorkbookCellValue( series.getTitleReference(),wb); - series.setTitleCached(freshSeriesTitle); XDDFDataSource catData = series.getCategoryData(); if (catData == null) continue; - String referenceFormula = catData.getFormula(); - if (referenceFormula != null) { - List cells = getWorkbookCells(referenceFormula, wb); - String[] freshCategories = new String[cells.size()]; - int categIndex = 0; - for (Cell cell : cells) { - freshCategories[categIndex++] = cell.toString(); - } - // create categories from a range formula and the values below, it will create then as "cached" values like word expects - XDDFDataSource freshCategoryDataSrouce = XDDFDataSourcesFactory.fromArray(freshCategories,referenceFormula); - series.replaceData(freshCategoryDataSrouce, (XDDFNumericalDataSource) series.getValuesData()); + if (catData.isReference()) { + String ref = catData.getDataRangeReference(); + String sheet=ref.substring(0,ref.indexOf('!') ); + XSSFSheet sheet2 = wb.getSheet(sheet); + //calling this function and asking it to replace its sheet with the same sheet will refresh the cache + replaceReferences(sheet2); } } - //this mystery "plot" will somehow update the underlying document XML - plot(chartData); - } - } - /** - * - * @param cell must include the sheet reference - * @param wb - * @return toString() value of the cell - */ - private static String getWorkbookCellValue(CellReference cell, Workbook wb) { - - return wb.getSheet( cell.getSheetName()).getRow(cell.getRow()).getCell(cell.getCol()).toString(); - - } - /** - * - * @param rangeFormula for example: "Sheet1!$A$2:$A$5" - * @return - */ - private static List getWorkbookCells(String rangeFormula, Workbook wb) { - List cells = new ArrayList(); - String[] parts = rangeFormula.split("!");// get formula gives the ref like: Sheet1!$A$2:$A$5 - Sheet sheet = wb.getSheet(parts[0]); - CellRangeAddress range = CellRangeAddress.valueOf(parts[1]); - for (CellAddress cellAddress : range) {// the iterator goes top to bottom, left to right - cells.add(sheet.getRow(cellAddress.getRow()).getCell(cellAddress.getColumn())); + } - return cells; } + }