diff --git a/src/EPPlus/FormulaParsing/EpplusExcelDataProvider.cs b/src/EPPlus/FormulaParsing/EpplusExcelDataProvider.cs index 922835df62..c51ca54c19 100644 --- a/src/EPPlus/FormulaParsing/EpplusExcelDataProvider.cs +++ b/src/EPPlus/FormulaParsing/EpplusExcelDataProvider.cs @@ -24,6 +24,7 @@ Date Author Change using OfficeOpenXml.FormulaParsing.Ranges; using System.Runtime.InteropServices; using OfficeOpenXml.Utils.String; +using OfficeOpenXml.Style; namespace OfficeOpenXml.FormulaParsing { @@ -641,7 +642,21 @@ public override string GetFormat(object value, string format, out bool isValidFo { isValidFormat = true; var ws = _currentWorksheet ?? _context.CurrentWorksheet; - var arg = new NumberFormatToTextArgs(ws, _context.CurrentCell.Row, _context.CurrentCell.Column, value, ws.GetStyleInner(_context.CurrentCell.Row, _context.CurrentCell.Column)); + + var existingId = ExcelNumberFormat.GetFromBuildIdFromFormat(format); + + NumberFormatToTextArgs arg; + if (existingId == int.MinValue) + { + //The format does not have a corresponding styleId + //Still allow the NumberFormatToTextHandler to see the format + arg = new NumberFormatToTextArgs(ws, _context.CurrentCell.Row, _context.CurrentCell.Column, value, format); + } + else + { + arg = new NumberFormatToTextArgs(ws, _context.CurrentCell.Row, _context.CurrentCell.Column, value, existingId); + } + arg.FromFormula = true; return _workbook.NumberFormatToTextHandler(arg); } } diff --git a/src/EPPlus/NumberFormatToTextArgs.cs b/src/EPPlus/NumberFormatToTextArgs.cs index a4a3ec55d7..1bbfbe8b66 100644 --- a/src/EPPlus/NumberFormatToTextArgs.cs +++ b/src/EPPlus/NumberFormatToTextArgs.cs @@ -12,6 +12,7 @@ Date Author Change *************************************************************************************************/ using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; using OfficeOpenXml.Style; +using OfficeOpenXml.Style.Interfaces; using OfficeOpenXml.Style.XmlAccess; using OfficeOpenXml.Utils.String; @@ -23,6 +24,32 @@ namespace OfficeOpenXml public class NumberFormatToTextArgs { internal int _styleId; + + ExcelNumberFormatWithoutId fallbackNumberFormat = null; + + /// + /// If these args are provided from a formula + /// + public bool FromFormula = false; + + /// + /// Constructor when numberformat is not built in + /// + /// + /// + /// + /// + /// + internal NumberFormatToTextArgs(ExcelWorksheet ws, int row, int column, object value, string numberFormat) + { + Worksheet = ws; + Row = row; + Column = column; + Value = value; + _styleId = -1; + fallbackNumberFormat = new ExcelNumberFormatWithoutId(numberFormat); + } + internal NumberFormatToTextArgs(ExcelWorksheet ws, int row, int column, object value, int styleId) { Worksheet = ws; @@ -31,6 +58,7 @@ internal NumberFormatToTextArgs(ExcelWorksheet ws, int row, int column, object v Value = value; _styleId = styleId; } + /// /// The worksheet of the cell. /// @@ -46,11 +74,18 @@ internal NumberFormatToTextArgs(ExcelWorksheet ws, int row, int column, object v /// /// The number format settings for the cell /// - public ExcelNumberFormatXml NumberFormat + public IExcelNumberFormat NumberFormat { get { - return ValueToTextHandler.GetNumberFormat(_styleId, Worksheet.Workbook.Styles); + if(fallbackNumberFormat != null) + { + return fallbackNumberFormat; + } + else + { + return ValueToTextHandler.GetNumberFormat(_styleId, Worksheet.Workbook.Styles); + } } } /// @@ -64,6 +99,13 @@ public string Text { get { + if(fallbackNumberFormat != null) + { + var ft = new ExcelFormatTranslator(NumberFormat.Format, -1); + bool isValidFormat = false; + var frmt = ValueToTextHandler.FormatValue(Value, false, ft, null, out isValidFormat); + return frmt; + } return ValueToTextHandler.GetFormattedText(Value, Worksheet.Workbook, _styleId, false); } } diff --git a/src/EPPlus/Style/Interfaces/IExcelNumberFormat.cs b/src/EPPlus/Style/Interfaces/IExcelNumberFormat.cs new file mode 100644 index 0000000000..594b8b05aa --- /dev/null +++ b/src/EPPlus/Style/Interfaces/IExcelNumberFormat.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OfficeOpenXml.Style.Interfaces +{ + public interface IExcelNumberFormat + { + /// + /// The numberformat string + /// + public string Format { get; } + /// + /// Number format Id + /// + public int NumFmtId { get; } + /// + /// If this numberformat is built in + /// + public bool BuildIn { get; } + } +} diff --git a/src/EPPlus/Style/XmlAccess/ExcelNumberFormatWithoutId.cs b/src/EPPlus/Style/XmlAccess/ExcelNumberFormatWithoutId.cs new file mode 100644 index 0000000000..9b953ad1f6 --- /dev/null +++ b/src/EPPlus/Style/XmlAccess/ExcelNumberFormatWithoutId.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OfficeOpenXml.Style.Interfaces; + +namespace OfficeOpenXml.Style.XmlAccess +{ + internal class ExcelNumberFormatWithoutId : IExcelNumberFormat + { + internal ExcelNumberFormatWithoutId(string format) + { + Format = format; + NumFmtId = -1; + BuildIn = false; + } + public string Format { get; private set; } + + public int NumFmtId { get; private set; } + + public bool BuildIn { get; private set; } + } +} diff --git a/src/EPPlus/Style/XmlAccess/ExcelNumberFormatXml.cs b/src/EPPlus/Style/XmlAccess/ExcelNumberFormatXml.cs index 7b6c4a6653..a0c0eb650e 100644 --- a/src/EPPlus/Style/XmlAccess/ExcelNumberFormatXml.cs +++ b/src/EPPlus/Style/XmlAccess/ExcelNumberFormatXml.cs @@ -10,22 +10,23 @@ Date Author Change ************************************************************************************************* 01/27/2020 EPPlus Software AB Initial release EPPlus 5 *************************************************************************************************/ +using OfficeOpenXml.Style.Interfaces; +using OfficeOpenXml.Utils; using System; using System.Collections.Generic; -using System.Text; -using System.Xml; using System.Globalization; -using System.Text.RegularExpressions; -using OfficeOpenXml.Utils; -using System.Runtime.InteropServices; using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; namespace OfficeOpenXml.Style.XmlAccess { /// /// Xml access class for number customFormats /// - public sealed class ExcelNumberFormatXml : StyleXmlHelper + public sealed class ExcelNumberFormatXml : StyleXmlHelper, IExcelNumberFormat { internal ExcelNumberFormatXml(XmlNamespaceManager nameSpaceManager) : base(nameSpaceManager) diff --git a/src/EPPlusTest/Drawing/Chart/ChartSeriesTest.cs b/src/EPPlusTest/Drawing/Chart/ChartSeriesTest.cs index 353a22c52a..f6cea68a87 100644 --- a/src/EPPlusTest/Drawing/Chart/ChartSeriesTest.cs +++ b/src/EPPlusTest/Drawing/Chart/ChartSeriesTest.cs @@ -1,14 +1,22 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Castle.Components.DictionaryAdapter.Xml; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; using OfficeOpenXml; +using OfficeOpenXml.ConditionalFormatting; using OfficeOpenXml.Drawing; using OfficeOpenXml.Drawing.Chart; using OfficeOpenXml.Drawing.Chart.ChartEx; +using OfficeOpenXml.Drawing.Interfaces; +using OfficeOpenXml.FormulaParsing.Utilities; +using OfficeOpenXml.Style; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; +using System.Xml; namespace EPPlusTest.Drawing.Chart { diff --git a/src/EPPlusTest/Issues/StylingIssues.cs b/src/EPPlusTest/Issues/StylingIssues.cs index 6d0fa4bde0..1082c31630 100644 --- a/src/EPPlusTest/Issues/StylingIssues.cs +++ b/src/EPPlusTest/Issues/StylingIssues.cs @@ -8,6 +8,8 @@ using System.IO; using System.Globalization; using OfficeOpenXml.Style; +using OfficeOpenXml.FormulaParsing; + namespace EPPlusTest { [TestClass] @@ -424,6 +426,85 @@ public void s1007() //SaveAndCleanup(p); } + + [TestMethod] + public void s1005() + { + SwitchToCulture("de-DE"); + + //Set specific built-in formats + if (!ExcelPackageSettings.CultureSpecificBuildInNumberFormats.ContainsKey("de-DE")) + { + ExcelPackageSettings.CultureSpecificBuildInNumberFormats.Add("de-DE", + new Dictionary + { + {14,"dd.MM.yyyy"}, + {15,"dd. MMM yy"}, + {16,"dd. MMM"}, + {17,"MMM yy"}, + {18,"hh:mm AM/PM" }, + {22,"dd.MM.yyyy hh:mm"}, + {37,"#,##0;-#,##0"}, + {38,"#,##0;[Rot]-#,##0"}, + {39,"#,##0.00;-#,##0.00"}, + {40,"#,##0.00;[Rot]-#,##0.00"}, + {47,"mm:ss,f"} + }); + } + + using (var p = OpenTemplatePackage("s1005.xlsx")) + { + //AND use a Custom Number Format + //This caused issues in the Text.cs file when we ran Calculate + p.Workbook.NumberFormatToTextHandler = CustomNumberFormatToTextExample; + + var ws1 = p.Workbook.Worksheets[1]; + + var origText = ws1.Cells["D8"].Text; + + //Verify cell contents match when test was written + Assert.IsTrue(origText.Contains(".8000")); + Assert.IsTrue(origText.Contains("(26895559.000)")); + Assert.IsTrue(origText.Contains("69928453.64000")); + + ws1.Cells["D8"].Calculate(); + var cellRich = ws1.Cells["D8"].RichText.Text; + + //Verify formatting has changed appropriately for calculated string + Assert.IsTrue(cellRich.Contains("0,80")); + Assert.IsTrue(cellRich.Contains("(26.895.559,00)")); + Assert.IsTrue(cellRich.Contains("69.928.453,64")); + + SaveAndCleanup(p); + } + + SwitchBackToCurrentCulture(); + } + + private static string CustomNumberFormatToTextExample(NumberFormatToTextArgs options) + { + var result = options.Text; + + switch (options.Value) + { + case DateTime dt: + { + switch (options.NumberFormat.Format) + { + case "dd. mmm yy": + result = dt.ToString("dd. mmm yy"); //Return your own formatted text. Example + break; + default: + result = dt.ToString(options.NumberFormat.Format); + break; + } + break; + } + } + + return result; + } + public class TestData { public int Id { get; set; }