diff --git a/docs/articles/breakingchanges.md b/docs/articles/breakingchanges.md index 89a45fcf1c..d6abb22c67 100644 --- a/docs/articles/breakingchanges.md +++ b/docs/articles/breakingchanges.md @@ -215,4 +215,9 @@ Renaming worksheet's will now change the formula correctly to include single quo `ExcelColor.Tint` from `decimal` to `double`. ### 8.0.2 -* Removed base class from ExcelVmlDrawingPosition and with that the Load, UpdateXml methods and the RowOff and ColOff properties as they were duplicates. \ No newline at end of file +* Removed base class from ExcelVmlDrawingPosition and with that the Load, UpdateXml methods and the RowOff and ColOff properties as they were duplicates. + +### 8.5.5 +* `ws.Cells["A1"].RichText` no longer sets cells with `null` to `string.empty` +* .RichText no longer sets the cell or contents to be RichText automatically. + This is instead done when properties such as; `.Text`, `.Add` or `.Insert` are set on the .RichText property. \ No newline at end of file diff --git a/docs/articles/fixedissues.md b/docs/articles/fixedissues.md index 1046e87a84..57131a563e 100644 --- a/docs/articles/fixedissues.md +++ b/docs/articles/fixedissues.md @@ -1,4 +1,11 @@ # Features / Fixed issues - EPPlus 8 + +## Version 8.5.5 +### Minor Features +* Ranges can now convert their values to richText via ´ConvertToRichText()´ e.g. ´ws.Cells["A1"].ConvertToRichText()´ +### Fixed issues +* Several issues related to RichText in ranges. Simply looking at the .RichText attribute no longer changes the cell value. Setting a propert e.g. ´range.RichText.Text = "Hi"´ or using ConvertToRichText() actually changes the value. + ## Version 8.5.4 ### Minor Features * Added ´IncludeInHtmlOnly´ option to the ´ePictureInclude´ enum. This allows the HTML Exporter to include pictures directly in the HTML output rather than in the CSS. diff --git a/src/EPPlus/ExcelRangeBase.cs b/src/EPPlus/ExcelRangeBase.cs index a681380e37..20f66fe12b 100644 --- a/src/EPPlus/ExcelRangeBase.cs +++ b/src/EPPlus/ExcelRangeBase.cs @@ -10,29 +10,27 @@ Date Author Change ************************************************************************************************* 01/27/2020 EPPlus Software AB Initial release EPPlus 5 *************************************************************************************************/ -using System; -using System.Collections.Generic; -using OfficeOpenXml.FormulaParsing; -using OfficeOpenXml.Style; -using System.Globalization; -using System.Collections; -using OfficeOpenXml.Table; -using OfficeOpenXml.DataValidation; +using OfficeOpenXml.CellPictures; using OfficeOpenXml.ConditionalFormatting; -using OfficeOpenXml.FormulaParsing.LexicalAnalysis; using OfficeOpenXml.Core; using OfficeOpenXml.Core.CellStore; using OfficeOpenXml.Core.Worksheet; -using OfficeOpenXml.ThreadedComments; -using OfficeOpenXml.CellPictures; -using OfficeOpenXml.Sorting; +using OfficeOpenXml.DataValidation; using OfficeOpenXml.Export.HtmlExport.Interfaces; +using OfficeOpenXml.FormulaParsing; using OfficeOpenXml.FormulaParsing.Excel.Functions; -using OfficeOpenXml.Utils.TypeConversion; -using OfficeOpenXml.Utils.String; +using OfficeOpenXml.FormulaParsing.LexicalAnalysis; +using OfficeOpenXml.Sorting; +using OfficeOpenXml.Style; +using OfficeOpenXml.Table; +using OfficeOpenXml.ThreadedComments; using OfficeOpenXml.Utils.Cell; -using static OfficeOpenXml.ExcelWorksheet; -using System.Linq; +using OfficeOpenXml.Utils.String; +using OfficeOpenXml.Utils.TypeConversion; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; namespace OfficeOpenXml { @@ -1330,8 +1328,10 @@ public bool IsRichText var isRt = _worksheet._flags.GetFlagValue(_fromRow, _fromCol, CellFlags.RichText); if (isRt) { - _rtc = _worksheet.GetRichText(_fromRow, _fromCol, this); - return _rtc.Count > 0; + //Do not update _rtc. This is a boolean getter. + //It should not set any values even if they diff. + var couldBeEmptyRT = _worksheet.GetRichText(_fromRow, _fromCol, this); + return couldBeEmptyRT.Count > 0; } return isRt; } @@ -1457,6 +1457,16 @@ public ExcelRichTextCollection RichText } } + /// + /// Convert the contents of the top left cell of this range to a richtext string + /// And set the cell value as .RichText + /// + public ExcelRichTextCollection ConvertToRichText() + { + _rtc = _worksheet.ConvertCellValueToRichText(_fromRow, _fromCol, this); + return _rtc; + } + /// /// Returns the comment object of the first cell in the range /// diff --git a/src/EPPlus/ExcelWorksheet.cs b/src/EPPlus/ExcelWorksheet.cs index f0ebe3dec3..d45a7c1aa5 100644 --- a/src/EPPlus/ExcelWorksheet.cs +++ b/src/EPPlus/ExcelWorksheet.cs @@ -1515,10 +1515,12 @@ private void LoadHyperLinks(XmlReader xr) delRelIds.ToList().ForEach(x => Part.DeleteRelationship(x)); } - internal ExcelRichTextCollection GetRichText(int row, int col, ExcelRangeBase r = null) + internal ExcelRichTextCollection ConvertCellValueToRichText(int row, int col, ExcelRangeBase r = null) { var v = GetCoreValueInner(row, col); var isRt = _flags.GetFlagValue(row, col, CellFlags.RichText); + + //If it already is rt then no need to actually convert if (isRt && v._value is ExcelRichTextCollection rtc) { if (rtc._cells == null) rtc._cells = r; @@ -1526,19 +1528,42 @@ internal ExcelRichTextCollection GetRichText(int row, int col, ExcelRangeBase r } else { - var text = ValueToTextHandler.GetFormattedText(v._value, Workbook, v._styleId, false); - if (string.IsNullOrEmpty(text)) + object textValue = v._value; + if (textValue != null && typeof(ExcelRichTextCollection) == textValue.GetType()) { - var item = new ExcelRichTextCollection(Workbook, r); - SetValue(row, col, item); - return item; + textValue = ((ExcelRichTextCollection)textValue).Text; } - else + + var text = ValueToTextHandler.GetFormattedText(textValue, Workbook, v._styleId, false); + var item = new ExcelRichTextCollection(text, r); + SetValue(row, col, item); + + return item; + } + } + + internal ExcelRichTextCollection GetRichText(int row, int col, ExcelRangeBase r = null) + { + var v = GetCoreValueInner(row, col); + var isRt = _flags.GetFlagValue(row, col, CellFlags.RichText); + if (isRt && v._value is ExcelRichTextCollection rtc) + { + if (rtc._cells == null) rtc._cells = r; + return rtc; + } + else + { + object textValue = v._value; + if (textValue != null && typeof(ExcelRichTextCollection) == textValue.GetType()) { - var item = new ExcelRichTextCollection(text, r); - SetValue(row, col, item); - return item; + //This should only really happen if e.g. rich-text from a Note is Set to a cell value + textValue = ((ExcelRichTextCollection)textValue).Text; + var text = ValueToTextHandler.GetFormattedText(textValue, Workbook, v._styleId, false); + var itemRt = new ExcelRichTextCollection(text, r); + return itemRt; } + var item = new ExcelRichTextCollection(Workbook, r); + return item; } } @@ -2287,7 +2312,7 @@ public object GetValue(int Row, int Column) var v = GetValueInner(Row, Column); if (v != null) { - if (_flags.GetFlagValue(Row, Column, CellFlags.RichText)) + if (_flags.GetFlagValue(Row, Column, CellFlags.RichText) && Cells[Row, Column].IsRichText) { return (object)Cells[Row, Column].RichText.Text; } diff --git a/src/EPPlus/Style/RichText/ExcelRichTextCollection.cs b/src/EPPlus/Style/RichText/ExcelRichTextCollection.cs index de032100a6..657ccdb9df 100644 --- a/src/EPPlus/Style/RichText/ExcelRichTextCollection.cs +++ b/src/EPPlus/Style/RichText/ExcelRichTextCollection.cs @@ -10,19 +10,14 @@ Date Author Change ************************************************************************************************* 01/27/2020 EPPlus Software AB Initial release EPPlus 5 *************************************************************************************************/ +using OfficeOpenXml.Utils.TypeConversion; +using OfficeOpenXml.Utils.XML; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml; using System.Drawing; using System.Globalization; -using OfficeOpenXml.Drawing.Style.Coloring; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Statistical; -using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -using OfficeOpenXml.Utils.XML; -using OfficeOpenXml.Utils.TypeConversion; +using System.Text; +using System.Xml; namespace OfficeOpenXml.Style { @@ -40,7 +35,6 @@ internal ExcelRichTextCollection(ExcelWorkbook wb, ExcelRangeBase cells) { _wb = wb; _cells = cells; - _cells._worksheet._flags.SetFlagValue(_cells._fromRow, _cells._fromCol, true, CellFlags.RichText); } internal ExcelRichTextCollection(string s, ExcelRangeBase cells) @@ -162,6 +156,15 @@ public ExcelRichText Insert(int index, string text) { CheckDeleted(); if (text == null) throw new ArgumentException("Text can't be null", "text"); + + //If just a note we can't clear formulas on the cell itself. + if (_isComment == false) + { + //We MUST clear formulas before setting richtext + //To ensure calculate does not create missmatch between formula and richtext. + _cells.ClearFormulas(); + } + var rt = new ExcelRichText(text, this); rt.PreserveSpace = true; int prevIndex = 0; @@ -204,8 +207,19 @@ public ExcelRichText Insert(int index, string text) { rt.Color = Color.FromArgb(hex); } + + //If just a note we cannot set rich text flag on the cell itself. if (_isComment == false) { + //If the range value is not richtext it is now. + //Must set value first in order to not overwrite flags after. + if (_cells.Value != this) + { + //var flags = _cells._worksheet._flags; + _cells._worksheet._flags.GetFlagValue(_cells._fromRow, _cells._fromCol, CellFlags.RichText); + _cells.Value = this; + } + //If not a note then we are a cell and can set the flag. _cells._worksheet._flags.SetFlagValue(_cells._fromRow, _cells._fromCol, true, CellFlags.RichText); } } @@ -220,6 +234,8 @@ public void Clear() { CheckDeleted(); _list.Clear(); + _cells._worksheet._flags.SetFlagValue(_cells._fromRow, _cells._fromCol, false, CellFlags.RichText); + _cells.Value = string.Empty; } /// /// Removes an item at the specific index diff --git a/src/EPPlusTest/Core/Range/RangeRichTextTests.cs b/src/EPPlusTest/Core/Range/RangeRichTextTests.cs index 03aa3181d0..abe7f5d0e5 100644 --- a/src/EPPlusTest/Core/Range/RangeRichTextTests.cs +++ b/src/EPPlusTest/Core/Range/RangeRichTextTests.cs @@ -191,7 +191,7 @@ public void VerifyRichTextIsBlankIfAccess() var ws = p.Workbook.Worksheets.Add("Sheet1"); var t = ws.Cells["A1"].RichText.Text; - Assert.AreEqual(string.Empty, ws.Cells["A1"].Value); + Assert.AreEqual(null, ws.Cells["A1"].Value); } } [TestMethod] diff --git a/src/EPPlusTest/Issues/ChartIssues.cs b/src/EPPlusTest/Issues/ChartIssues.cs index 9bdf8d1f94..f717b73517 100644 --- a/src/EPPlusTest/Issues/ChartIssues.cs +++ b/src/EPPlusTest/Issues/ChartIssues.cs @@ -454,8 +454,6 @@ public void s1038() var firstDrawing = drawingsWithinGroupShape[0]; firstDrawing.SetPosition(500, 500); - //drawingsWithinGroupShape[0].Position.Y = 2000; - SaveAndCleanup(package); } diff --git a/src/EPPlusTest/Issues/RichDataIssues.cs b/src/EPPlusTest/Issues/RichDataIssues.cs index d3e14bab44..53b120faa1 100644 --- a/src/EPPlusTest/Issues/RichDataIssues.cs +++ b/src/EPPlusTest/Issues/RichDataIssues.cs @@ -1,8 +1,4 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using OfficeOpenXml; namespace EPPlusTest.Issues @@ -85,5 +81,40 @@ public void VerifyCalcError() SaveAndCleanup(p); } } + + [TestMethod] + public void AddingRichTextToFormulasShouldOverwrite() + { + using (var pck = OpenPackage("dirtyRT.xlsx", true)) + { + var ws = pck.Workbook.Worksheets.Add("richText"); + + ws.Cells["A1"].Value = 1001.1d; + ws.Cells["C1"].Formula = "ROUND(A1, 1)"; + ws.Cells["B1"].Formula = "\"My favorite number is: \"&TEXT(ROUND(A1,1),\"#,##0.00;(#,##0.00)\")"; + + ws.Cells["B1"].RichText.Add("My favorite number is: 1001.1"); + + var myFormula = ws.Cells["B1"].Formula; + + Assert.IsTrue(string.IsNullOrEmpty(myFormula)); + } + } + + [TestMethod] + public void RtComment() + { + using(var p = OpenTemplatePackage("RtWithOldComment.xlsx")) + { + var ws = p.Workbook.Worksheets[0]; + + var isRichtext = ws.Cells["B3"]; + var text = ws.Cells["B3"].RichText; + var rtComment = ws.Cells["B3"].Comment; + var rtFromComment = rtComment.RichText; + + Assert.AreNotEqual(text.Text, rtFromComment.Text); + } + } } } diff --git a/src/EPPlusTest/Issues/StylingIssues.cs b/src/EPPlusTest/Issues/StylingIssues.cs index 1082c31630..737e22603a 100644 --- a/src/EPPlusTest/Issues/StylingIssues.cs +++ b/src/EPPlusTest/Issues/StylingIssues.cs @@ -468,7 +468,8 @@ public void s1005() Assert.IsTrue(origText.Contains("69928453.64000")); ws1.Cells["D8"].Calculate(); - var cellRich = ws1.Cells["D8"].RichText.Text; + + var cellRich = ws1.Cells["D8"].ConvertToRichText().Text; //Verify formatting has changed appropriately for calculated string Assert.IsTrue(cellRich.Contains("0,80")); diff --git a/src/EPPlusTest/Style/RichTextTest.cs b/src/EPPlusTest/Style/RichTextTest.cs index f5f35c8181..48a2ab3355 100644 --- a/src/EPPlusTest/Style/RichTextTest.cs +++ b/src/EPPlusTest/Style/RichTextTest.cs @@ -370,5 +370,46 @@ public void CheckRichTextProperties() Assert.AreEqual("Arial", C4.RichText[1].FontName); } + + [TestMethod] + public void issue2214() + { + var p = new ExcelPackage(); + + //Ensure that 'getting' any properties does not change type + var ws = p.Workbook.Worksheets.Add("Sheet1"); + ws.Cells["A1"].Value = 1D; + + Assert.IsInstanceOfType(ws.Cells["A1"].Value, typeof(double)); + var r = ws.Cells["A1"].RichText; + Assert.IsInstanceOfType(ws.Cells["A1"].Value, typeof(double)); + + var test = r.Text; + Assert.IsInstanceOfType(ws.Cells["A1"].Value, typeof(double)); + Assert.IsFalse(ws.Cells["A1"].IsRichText); + + //Assert that Setting the property does. + string text = "Hello"; + r.Text = text; + + Assert.IsTrue(ws.Cells["A1"].IsRichText); + Assert.IsInstanceOfType(ws.Cells["A1"].Value, typeof(string)); + Assert.AreEqual(text, (string)ws.Cells["A1"].Value); + } + + [TestMethod] + public void ConvertCellToRichText() + { + var p = new ExcelPackage(); + var ws = p.Workbook.Worksheets.Add("Sheet1"); + ws.Cells["A1"].Value = 1D; + + ws.Cells["A1"].ConvertToRichText(); + + Assert.IsTrue(ws.Cells["A1"].IsRichText); + Assert.IsInstanceOfType(ws.Cells["A1"].Value, typeof(string)); + Assert.AreEqual("1", (string)ws.Cells["A1"].Value); + Assert.AreEqual("1", ws.Cells["A1"].RichText.Text); + } } }