diff --git a/main/SS/UserModel/Charts/ChartAxis.cs b/main/SS/UserModel/Charts/ChartAxis.cs index 0a7350d83..d115cfb18 100644 --- a/main/SS/UserModel/Charts/ChartAxis.cs +++ b/main/SS/UserModel/Charts/ChartAxis.cs @@ -106,6 +106,13 @@ public interface IChartAxis * @return minor tick mark. */ AxisTickMark MinorTickMark { get; set; } + + + /// + /// Use this to check before retrieving a number format, as calling {@link #getNumberFormat()} may create a default one if none exists. + /// + /// return true if a number format element is defined, false if not + bool HasNumberFormat(); } diff --git a/main/SS/UserModel/Charts/ChartAxisFactory.cs b/main/SS/UserModel/Charts/ChartAxisFactory.cs index 6cdd465f6..df303fd8c 100644 --- a/main/SS/UserModel/Charts/ChartAxisFactory.cs +++ b/main/SS/UserModel/Charts/ChartAxisFactory.cs @@ -22,16 +22,24 @@ namespace NPOI.SS.UserModel.Charts /// /// @author Roman Kashitsyn public interface IChartAxisFactory - { + { /// - /// returns new value axis + /// create new value axis at the end of the list at the specified chart position. /// /// /// IValueAxis CreateValueAxis(AxisPosition pos); - + /// + /// create new category axis at the end of the list at the specified chart position. + /// + /// + /// IChartAxis CreateCategoryAxis(AxisPosition pos); - + /// + /// create new date category axis at the end of the list at the specified chart position. + /// + /// + /// IChartAxis CreateDateAxis(AxisPosition pos); } diff --git a/ooxml/XSSF/Streaming/SXSSFWorkbook.cs b/ooxml/XSSF/Streaming/SXSSFWorkbook.cs index 7e71323c6..61d5aa277 100644 --- a/ooxml/XSSF/Streaming/SXSSFWorkbook.cs +++ b/ooxml/XSSF/Streaming/SXSSFWorkbook.cs @@ -471,7 +471,8 @@ private void InjectData(FileInfo zipfile, Stream outStream, bool leaveOpen) zos.PutNextEntry(new ZipEntry(ze.Name)); var inputStream = zip.GetInputStream(ze); XSSFSheet xSheet = GetSheetFromZipEntryName(ze.Name); - if (xSheet != null) + // See bug 56557, we should not inject data into the special ChartSheets + if (xSheet != null && !(xSheet is XSSFChartSheet)) { SXSSFSheet sxSheet = GetSXSSFSheet(xSheet); var xis = sxSheet.GetWorksheetXMLInputStream(); diff --git a/ooxml/XSSF/UserModel/Charts/XSSFCategoryAxis.cs b/ooxml/XSSF/UserModel/Charts/XSSFCategoryAxis.cs index f44a77e9d..d01b4fc6f 100644 --- a/ooxml/XSSF/UserModel/Charts/XSSFCategoryAxis.cs +++ b/ooxml/XSSF/UserModel/Charts/XSSFCategoryAxis.cs @@ -88,6 +88,11 @@ public void SetAuto(CT_Boolean au) ctCatAx.auto = au; } + public override CT_ChartLines GetMajorGridLines() + { + return ctCatAx.majorGridlines; + } + private void createAxis(long id, AxisPosition pos) { ctCatAx = chart.GetCTChart().plotArea.AddNewCatAx(); @@ -109,6 +114,11 @@ private void createAxis(long id, AxisPosition pos) this.MajorTickMark=(AxisTickMark.Cross); this.MinorTickMark=(AxisTickMark.None); } + + public override bool HasNumberFormat() + { + return ctCatAx.IsSetNumFmt(); + } } } diff --git a/ooxml/XSSF/UserModel/Charts/XSSFChartAxis.cs b/ooxml/XSSF/UserModel/Charts/XSSFChartAxis.cs index 32448b39a..82eed5578 100644 --- a/ooxml/XSSF/UserModel/Charts/XSSFChartAxis.cs +++ b/ooxml/XSSF/UserModel/Charts/XSSFChartAxis.cs @@ -45,11 +45,11 @@ public AxisPosition Position { get { - return toAxisPosition(GetCTAxPos()); + return ToAxisPosition(GetCTAxPos()); } set { - GetCTAxPos().val = fromAxisPosition(value); + GetCTAxPos().val = FromAxisPosition(value); } } @@ -180,12 +180,12 @@ public AxisOrientation Orientation { get { - return toAxisOrientation(GetCTScaling().orientation); + return ToAxisOrientation(GetCTScaling().orientation); } set { CT_Scaling scaling = GetCTScaling(); - ST_Orientation stOrientation = fromAxisOrientation(value); + ST_Orientation stOrientation = FromAxisOrientation(value); if (scaling.IsSetOrientation()) { scaling.orientation.val = stOrientation; @@ -201,11 +201,11 @@ public AxisCrosses Crosses { get { - return toAxisCrosses(GetCTCrosses()); + return ToAxisCrosses(GetCTCrosses()); } set { - GetCTCrosses().val = fromAxisCrosses(value); + GetCTCrosses().val = FromAxisCrosses(value); } } public bool IsVisible @@ -220,27 +220,27 @@ public bool IsVisible } } - public AxisTickMark MajorTickMark + public virtual AxisTickMark MajorTickMark { get { - return toAxisTickMark(GetMajorCTTickMark()); + return ToAxisTickMark(GetMajorCTTickMark()); } set { - GetMajorCTTickMark().val = fromAxisTickMark(value); + GetMajorCTTickMark().val = FromAxisTickMark(value); } } - public AxisTickMark MinorTickMark + public virtual AxisTickMark MinorTickMark { get { - return toAxisTickMark(GetMinorCTTickMark()); + return ToAxisTickMark(GetMinorCTTickMark()); } set { - GetMinorCTTickMark().val = fromAxisTickMark(value); + GetMinorCTTickMark().val = FromAxisTickMark(value); } } @@ -251,8 +251,11 @@ public AxisTickMark MinorTickMark protected abstract CT_Boolean GetDelete(); protected abstract CT_TickMark GetMajorCTTickMark(); protected abstract CT_TickMark GetMinorCTTickMark(); + public abstract CT_ChartLines GetMajorGridLines(); - private static ST_Orientation fromAxisOrientation(AxisOrientation orientation) + public abstract bool HasNumberFormat(); + + private static ST_Orientation FromAxisOrientation(AxisOrientation orientation) { switch (orientation) { @@ -263,7 +266,7 @@ private static ST_Orientation fromAxisOrientation(AxisOrientation orientation) } } - private static AxisOrientation toAxisOrientation(CT_Orientation ctOrientation) + private static AxisOrientation ToAxisOrientation(CT_Orientation ctOrientation) { switch (ctOrientation.val) { @@ -274,7 +277,7 @@ private static AxisOrientation toAxisOrientation(CT_Orientation ctOrientation) } } - private static ST_Crosses fromAxisCrosses(AxisCrosses crosses) + private static ST_Crosses FromAxisCrosses(AxisCrosses crosses) { switch (crosses) { @@ -286,7 +289,7 @@ private static ST_Crosses fromAxisCrosses(AxisCrosses crosses) } } - private static AxisCrosses toAxisCrosses(CT_Crosses ctCrosses) + private static AxisCrosses ToAxisCrosses(CT_Crosses ctCrosses) { switch (ctCrosses.val) { @@ -298,7 +301,7 @@ private static AxisCrosses toAxisCrosses(CT_Crosses ctCrosses) } } - private static ST_AxPos fromAxisPosition(AxisPosition position) + private static ST_AxPos FromAxisPosition(AxisPosition position) { switch (position) { @@ -311,7 +314,7 @@ private static ST_AxPos fromAxisPosition(AxisPosition position) } } - private static AxisPosition toAxisPosition(CT_AxPos ctAxPos) + private static AxisPosition ToAxisPosition(CT_AxPos ctAxPos) { switch (ctAxPos.val) { @@ -322,7 +325,7 @@ private static AxisPosition toAxisPosition(CT_AxPos ctAxPos) default: return AxisPosition.Bottom; } } - private static ST_TickMark fromAxisTickMark(AxisTickMark tickMark) + private static ST_TickMark FromAxisTickMark(AxisTickMark tickMark) { switch (tickMark) { @@ -335,7 +338,7 @@ private static ST_TickMark fromAxisTickMark(AxisTickMark tickMark) } } - private static AxisTickMark toAxisTickMark(CT_TickMark ctTickMark) + private static AxisTickMark ToAxisTickMark(CT_TickMark ctTickMark) { switch (ctTickMark.val) { diff --git a/ooxml/XSSF/UserModel/Charts/XSSFDateAxis.cs b/ooxml/XSSF/UserModel/Charts/XSSFDateAxis.cs index 61e1081e2..5af8e6a51 100644 --- a/ooxml/XSSF/UserModel/Charts/XSSFDateAxis.cs +++ b/ooxml/XSSF/UserModel/Charts/XSSFDateAxis.cs @@ -1,4 +1,21 @@ -using NPOI.OpenXmlFormats.Dml.Chart; +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +using NPOI.OpenXmlFormats.Dml.Chart; using NPOI.SS.UserModel.Charts; using System; using System.Collections.Generic; @@ -14,7 +31,7 @@ public XSSFDateAxis(XSSFChart chart, long id, AxisPosition pos) : base(chart) { - createAxis(id, pos); + CreateAxis(id, pos); } public XSSFDateAxis(XSSFChart chart, CT_DateAx ctDateAx) @@ -84,7 +101,7 @@ protected override CT_TickMark GetMinorCTTickMark() return ctDateAx.minorTickMark; } - protected CT_ChartLines GetMajorGridLines() + public override CT_ChartLines GetMajorGridLines() { return ctDateAx.majorGridlines; } @@ -94,14 +111,10 @@ public override void CrossAxis(IChartAxis axis) ctDateAx.crossAx.val = (uint)axis.Id; } - public CT_TimeUnit GetBaseTimeUnit() + public CT_TimeUnit BaseTimeUnit { - return ctDateAx.baseTimeUnit; - } - - public void SetBaseTimeUnit(CT_TimeUnit unit) - { - ctDateAx.baseTimeUnit = unit; + get { return ctDateAx.baseTimeUnit; } + set { ctDateAx.baseTimeUnit = value;} } public void SetAuto(CT_Boolean au) @@ -109,7 +122,7 @@ public void SetAuto(CT_Boolean au) ctDateAx.auto = au; } - private void createAxis(long id, AxisPosition pos) + private void CreateAxis(long id, AxisPosition pos) { ctDateAx = chart.GetCTChart().plotArea.AddNewDateAx(); ctDateAx.AddNewAxId().val = (uint)id; @@ -130,5 +143,10 @@ private void createAxis(long id, AxisPosition pos) this.MajorTickMark = (AxisTickMark.Cross); this.MinorTickMark = (AxisTickMark.None); } + + public override bool HasNumberFormat() + { + return ctDateAx.IsSetNumFmt(); + } } } diff --git a/ooxml/XSSF/UserModel/Charts/XSSFValueAxis.cs b/ooxml/XSSF/UserModel/Charts/XSSFValueAxis.cs index c28a161d0..ad0ea59fc 100644 --- a/ooxml/XSSF/UserModel/Charts/XSSFValueAxis.cs +++ b/ooxml/XSSF/UserModel/Charts/XSSFValueAxis.cs @@ -55,7 +55,7 @@ public override long Id public void SetCrossBetween(AxisCrossBetween crossBetween) { - ctValAx.crossBetween.val= fromCrossBetween(crossBetween); + ctValAx.crossBetween.val= FromCrossBetween(crossBetween); } public AxisCrossBetween GetCrossBetween() @@ -106,6 +106,10 @@ protected override CT_Crosses GetCTCrosses() { return ctValAx.crosses; } + public override CT_ChartLines GetMajorGridLines() + { + return ctValAx.majorGridlines; + } public override void CrossAxis(IChartAxis axis) { @@ -135,7 +139,7 @@ private void CreateAxis(long id, AxisPosition pos) MinorTickMark=(AxisTickMark.None); } - private static ST_CrossBetween fromCrossBetween(AxisCrossBetween crossBetween) + private static ST_CrossBetween FromCrossBetween(AxisCrossBetween crossBetween) { switch (crossBetween) { @@ -156,5 +160,10 @@ private static AxisCrossBetween ToCrossBetween(ST_CrossBetween ctCrossBetween) throw new ArgumentException(); } } + + public override bool HasNumberFormat() + { + return ctValAx.IsSetNumFmt(); + } } } diff --git a/ooxml/XSSF/UserModel/XSSFChart.cs b/ooxml/XSSF/UserModel/XSSFChart.cs index eb43b5ade..e59886e49 100644 --- a/ooxml/XSSF/UserModel/XSSFChart.cs +++ b/ooxml/XSSF/UserModel/XSSFChart.cs @@ -383,6 +383,7 @@ private bool HasAxis() private void ParseAxis() { ParseCategoryAxis(); + ParseDateAxis(); ParseValueAxis(); } private void ParseCategoryAxis() @@ -394,6 +395,17 @@ private void ParseCategoryAxis() axis.Add(new XSSFCategoryAxis(this, catAx)); } } + + private void ParseDateAxis() + { + if (chart.plotArea.dateAx == null) + return; + foreach (CT_DateAx dateAx in chart.plotArea.dateAx) + { + axis.Add(new XSSFDateAxis(this, dateAx)); + } + } + private void ParseValueAxis() { if (chart.plotArea.valAx == null) diff --git a/testcases/ooxml/XSSF/Streaming/TestSXSSFWorkbook.cs b/testcases/ooxml/XSSF/Streaming/TestSXSSFWorkbook.cs index 3080e8aeb..addbfcecd 100644 --- a/testcases/ooxml/XSSF/Streaming/TestSXSSFWorkbook.cs +++ b/testcases/ooxml/XSSF/Streaming/TestSXSSFWorkbook.cs @@ -613,6 +613,20 @@ public void CreateFromReadOnlyWorkbook() ClassicAssert.AreEqual("Test Row 9", s.GetRow(9).GetCell(2).StringCellValue); } + [Test] + public void Test56557() + { + IWorkbook wb = XSSFTestDataSamples.OpenSampleWorkbook("56557.xlsx"); + + // Using streaming XSSFWorkbook makes the output file invalid + wb = new SXSSFWorkbook((XSSFWorkbook)wb); + + // Should not throw POIXMLException: java.io.IOException: Unable to parse xml bean when reading back + IWorkbook wbBack = XSSFTestDataSamples.WriteOutAndReadBack(wb); + ClassicAssert.IsNotNull(wbBack); + wbBack.Close(); + + wb.Close(); + } } - } \ No newline at end of file diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFDateAxis.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFDateAxis.cs new file mode 100644 index 000000000..4dd7f2fb1 --- /dev/null +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFDateAxis.cs @@ -0,0 +1,50 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NPOI.XSSF.UserModel; +using NPOI.SS.UserModel.Charts; +using NPOI.XSSF.UserModel.Charts; +using NUnit.Framework; +using NUnit.Framework.Legacy; + +namespace TestCases.XSSF.UserModel +{ + [TestFixture] + internal class TestXSSFDateAxis + { + [Test] + public void TestAccessMethods() + { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = wb.CreateSheet() as XSSFSheet; + XSSFDrawing Drawing = sheet.CreateDrawingPatriarch() as XSSFDrawing; + XSSFClientAnchor anchor = Drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30) as XSSFClientAnchor; + XSSFChart chart = Drawing.CreateChart(anchor) as XSSFChart; + XSSFDateAxis axis = chart.ChartAxisFactory.CreateDateAxis(AxisPosition.Bottom) as XSSFDateAxis; + + axis.Crosses = AxisCrosses.AutoZero; + ClassicAssert.AreEqual(axis.Crosses, AxisCrosses.AutoZero); + + ClassicAssert.AreEqual(chart.GetAxis().Count, 1); + } + } +} \ No newline at end of file diff --git a/testcases/ooxml/XSSF/XSSFTestDataSamples.cs b/testcases/ooxml/XSSF/XSSFTestDataSamples.cs index 77554ba63..e36198a45 100644 --- a/testcases/ooxml/XSSF/XSSFTestDataSamples.cs +++ b/testcases/ooxml/XSSF/XSSFTestDataSamples.cs @@ -25,7 +25,9 @@ limitations under the License. using NPOI.HSSF; using TestCases.HSSF; using System.Diagnostics; -using NUnit.Framework;using NUnit.Framework.Legacy; +using NUnit.Framework; +using NUnit.Framework.Legacy; +using NPOI.XSSF.Streaming; namespace NPOI.XSSF { @@ -86,6 +88,10 @@ public static IWorkbook WriteOutAndReadBack(IWorkbook wb) sw2.Stop(); Debug.WriteLine("XSSFWorkbook parse time: " + sw2.ElapsedMilliseconds + "ms"); } + else if (wb is SXSSFWorkbook) + { + result = new SXSSFWorkbook(new XSSFWorkbook(is1)); + } else { throw new RuntimeException("Unexpected workbook type (" diff --git a/testcases/test-data/spreadsheet/56557.xlsx b/testcases/test-data/spreadsheet/56557.xlsx new file mode 100644 index 000000000..7ca891cf5 Binary files /dev/null and b/testcases/test-data/spreadsheet/56557.xlsx differ