From e7230ed0450ff9985b4c8a159f74ca1c348d6e67 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20K=C3=A4llman?=
<982881+JanKallman@users.noreply.github.com>
Date: Fri, 29 Jun 2018 12:07:15 +0200
Subject: [PATCH 1/5] Fixed issue #220,#233,#234,#236. Added nuget config files
---
.gitignore | 1 +
.nuget/NuGet.Config | 6 +
.nuget/NuGet.targets | 151 ++++++++++++++++++
EPPlus/ExcelCellBase.cs | 57 +++----
EPPlus/ExcelNamedRange.cs | 1 -
EPPlus/ExcelNamedRangeCollection.cs | 5 +
EPPlus/ExcelRangeBase.cs | 66 ++++----
EPPlus/ExcelWorksheet.cs | 6 +-
.../ExcelUtilities/ExcelAddressUtil.cs | 43 +++++
EPPlus/Packaging/ZipPackage.cs | 6 +-
EPPlus/Style/XmlAccess/ExcelFontXml.cs | 12 +-
EPPlus/Table/ExcelTable.cs | 7 +-
EPPlus/Table/ExcelTableCollection.cs | 18 ++-
EPPlusTest/Address.cs | 16 +-
EPPlusTest/Issues.cs | 120 +++++++++++++-
EPPlusTest/TestBase.cs | 6 +-
EPPlusTest/WorkSheet.cs | 12 +-
17 files changed, 433 insertions(+), 100 deletions(-)
create mode 100644 .nuget/NuGet.Config
create mode 100644 .nuget/NuGet.targets
diff --git a/.gitignore b/.gitignore
index 2b991b64..f0b42eb0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,6 +34,7 @@ obj/
Ankh.NoLoad
*.[Pp]ublish.xml
.vs/
+.nuget/nuget.exe
#Tooling
_ReSharper*/
diff --git a/.nuget/NuGet.Config b/.nuget/NuGet.Config
new file mode 100644
index 00000000..67f8ea04
--- /dev/null
+++ b/.nuget/NuGet.Config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.nuget/NuGet.targets b/.nuget/NuGet.targets
new file mode 100644
index 00000000..02296949
--- /dev/null
+++ b/.nuget/NuGet.targets
@@ -0,0 +1,151 @@
+
+
+
+ $(MSBuildProjectDirectory)\..\
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
+ $([System.IO.Path]::Combine($(ProjectDir), "packages.config"))
+
+
+
+
+ $(SolutionDir).nuget
+ packages.config
+
+
+
+
+ $(NuGetToolsPath)\nuget.exe
+ @(PackageSource)
+
+ "$(NuGetExePath)"
+ mono --runtime=v4.0.30319 $(NuGetExePath)
+
+ $(TargetDir.Trim('\\'))
+
+ -RequireConsent
+
+ $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(RequireConsentSwitch) -solutionDir "$(SolutionDir) "
+ $(NuGetCommand) pack "$(ProjectPath)" -p Configuration=$(Configuration) -o "$(PackageOutputDir)" -symbols
+
+
+
+ RestorePackages;
+ $(BuildDependsOn);
+
+
+
+
+ $(BuildDependsOn);
+ BuildPackage;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/EPPlus/ExcelCellBase.cs b/EPPlus/ExcelCellBase.cs
index c3bb2e0d..476d95b6 100644
--- a/EPPlus/ExcelCellBase.cs
+++ b/EPPlus/ExcelCellBase.cs
@@ -758,34 +758,35 @@ public static string GetFullAddress(string worksheetName, string address)
}
internal static string GetFullAddress(string worksheetName, string address, bool fullRowCol)
{
- if (address.IndexOf("!") == -1 || address=="#REF!")
- {
- if (fullRowCol)
- {
- string[] cells = address.Split(':');
- if (cells.Length > 0)
- {
- address = string.Format("'{0}'!{1}", worksheetName, cells[0]);
- if (cells.Length > 1)
- {
- address += string.Format(":{0}", cells[1]);
- }
- }
- }
- else
- {
- var a = new ExcelAddressBase(address);
- if ((a._fromRow == 1 && a._toRow == ExcelPackage.MaxRows) || (a._fromCol == 1 && a._toCol == ExcelPackage.MaxColumns))
- {
- address = string.Format("'{0}'!{1}{2}:{3}{4}", worksheetName, ExcelAddress.GetColumnLetter(a._fromCol), a._fromRow, ExcelAddress.GetColumnLetter(a._toCol), a._toRow);
- }
- else
- {
- address=GetFullAddress(worksheetName, address, true);
- }
- }
- }
- return address;
+ if(!string.IsNullOrEmpty(worksheetName)) worksheetName = worksheetName.Replace("'", "''"); //Makesure addresses handle single qoutes
+ if (address.IndexOf("!") == -1 || address=="#REF!")
+ {
+ if (fullRowCol)
+ {
+ string[] cells = address.Split(':');
+ if (cells.Length > 0)
+ {
+ address = string.Format("'{0}'!{1}", worksheetName, cells[0]);
+ if (cells.Length > 1)
+ {
+ address += string.Format(":{0}", cells[1]);
+ }
+ }
+ }
+ else
+ {
+ var a = new ExcelAddressBase(address);
+ if ((a._fromRow == 1 && a._toRow == ExcelPackage.MaxRows) || (a._fromCol == 1 && a._toCol == ExcelPackage.MaxColumns))
+ {
+ address = string.Format("'{0}'!{1}{2}:{3}{4}", worksheetName, ExcelAddress.GetColumnLetter(a._fromCol), a._fromRow, ExcelAddress.GetColumnLetter(a._toCol), a._toRow);
+ }
+ else
+ {
+ address=GetFullAddress(worksheetName, address, true);
+ }
+ }
+ }
+ return address;
}
#endregion
#region IsValidCellAddress
diff --git a/EPPlus/ExcelNamedRange.cs b/EPPlus/ExcelNamedRange.cs
index ab1b1763..fc17999d 100644
--- a/EPPlus/ExcelNamedRange.cs
+++ b/EPPlus/ExcelNamedRange.cs
@@ -119,6 +119,5 @@ public override string ToString()
{
return Name;
}
-
}
}
diff --git a/EPPlus/ExcelNamedRangeCollection.cs b/EPPlus/ExcelNamedRangeCollection.cs
index 16e600c1..43a98549 100644
--- a/EPPlus/ExcelNamedRangeCollection.cs
+++ b/EPPlus/ExcelNamedRangeCollection.cs
@@ -34,6 +34,7 @@
using System.Text;
using System.Collections;
using System.Linq;
+using OfficeOpenXml.FormulaParsing.ExcelUtilities;
namespace OfficeOpenXml
{
@@ -65,6 +66,10 @@ internal ExcelNamedRangeCollection(ExcelWorkbook wb, ExcelWorksheet ws)
public ExcelNamedRange Add(string Name, ExcelRangeBase Range)
{
ExcelNamedRange item;
+ if(!ExcelAddressUtil.IsValidName(Name))
+ {
+ throw (new ArgumentException("Name contains invalid characters"));
+ }
if (Range.IsName)
{
diff --git a/EPPlus/ExcelRangeBase.cs b/EPPlus/ExcelRangeBase.cs
index 53d3c17e..046f41c9 100644
--- a/EPPlus/ExcelRangeBase.cs
+++ b/EPPlus/ExcelRangeBase.cs
@@ -2084,49 +2084,37 @@ public ExcelRangeBase LoadFromCollection(IEnumerable Collection, bool Prin
return null;
}
- //if (Members.Length == 0)
- //{
- // foreach (var item in Collection)
- // {
- // //_worksheet.Cells[row++, col].Value = item;
- // values[row, col++] = item;
- // }
- //}
- //else
- //{
- foreach (var item in Collection)
- {
- col = 0;
- if (item is string || item is decimal || item is DateTime || TypeCompat.IsPrimitive(item))
- {
- //_worksheet.Cells[row, col++].Value = item;
- values[row, col++] = item;
- }
- else
+ foreach (var item in Collection)
+ {
+ col = 0;
+ if (item is string || item is decimal || item is DateTime || TypeCompat.IsPrimitive(item))
+ {
+ values[row, col++] = item;
+ }
+ else
+ {
+ foreach (var t in Members)
{
- foreach (var t in Members)
+ if (isSameType == false && item.GetType().GetMember(t.Name, memberFlags).Length == 0)
{
- if (isSameType == false && item.GetType().GetMember(t.Name, memberFlags).Length == 0)
- {
- col++;
- continue; //Check if the property exists if and inherited class is used
- }
- else if (t is PropertyInfo)
- {
- values[row, col++] = ((PropertyInfo)t).GetValue(item, null);
- }
- else if (t is FieldInfo)
- {
- values[row, col++] = ((FieldInfo)t).GetValue(item);
- }
- else if (t is MethodInfo)
- {
- values[row, col++] = ((MethodInfo)t).Invoke(item, null);
- }
+ col++;
+ continue; //Check if the property exists if and inherited class is used
+ }
+ else if (t is PropertyInfo)
+ {
+ values[row, col++] = ((PropertyInfo)t).GetValue(item, null);
+ }
+ else if (t is FieldInfo)
+ {
+ values[row, col++] = ((FieldInfo)t).GetValue(item);
+ }
+ else if (t is MethodInfo)
+ {
+ values[row, col++] = ((MethodInfo)t).Invoke(item, null);
}
}
- row++;
- //}
+ }
+ row++;
}
_worksheet.SetRangeValueInner(_fromRow, _fromCol, _fromRow + row - 1, _fromCol + col - 1, values);
diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs
index 472f44bf..18a72327 100644
--- a/EPPlus/ExcelWorksheet.cs
+++ b/EPPlus/ExcelWorksheet.cs
@@ -3104,7 +3104,8 @@ private void SaveComments()
{
if (_comments.Uri == null)
{
- _comments.Uri=new Uri(string.Format(@"/xl/comments{0}.xml", SheetID), UriKind.Relative);
+ var id = SheetID;
+ _comments.Uri = XmlHelper.GetNewUri(_package.Package, @"/xl/comments{0}.xml", ref id); //Issue 236-Part already exists fix
}
if(_comments.Part==null)
{
@@ -3129,7 +3130,8 @@ private void SaveComments()
{
if (_vmlDrawings.Uri == null)
{
- _vmlDrawings.Uri = XmlHelper.GetNewUri(_package.Package, @"/xl/drawings/vmlDrawing{0}.vml");
+ var id = SheetID;
+ _vmlDrawings.Uri = XmlHelper.GetNewUri(_package.Package, @"/xl/drawings/vmlDrawing{0}.vml", ref id);
}
if (_vmlDrawings.Part == null)
{
diff --git a/EPPlus/FormulaParsing/ExcelUtilities/ExcelAddressUtil.cs b/EPPlus/FormulaParsing/ExcelUtilities/ExcelAddressUtil.cs
index 9dc51572..bbe3edd5 100644
--- a/EPPlus/FormulaParsing/ExcelUtilities/ExcelAddressUtil.cs
+++ b/EPPlus/FormulaParsing/ExcelUtilities/ExcelAddressUtil.cs
@@ -32,6 +32,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Text.RegularExpressions;
namespace OfficeOpenXml.FormulaParsing.ExcelUtilities
{
@@ -67,5 +68,47 @@ public static bool IsValidAddress(string token)
}
return OfficeOpenXml.ExcelAddress.IsValidAddress(token);
}
+ readonly static char[] NameInvalidChars = new char[] { '!', '@', '#', '$', '£', '%', '&', '/', '(', ')', '[', ']', '{', '}', '<', '>', '=', '+', '?', '\\', '*', '-', '~', '^', ':', ';', '|', ',', ' ' };
+ public static bool IsValidName(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ return false;
+ }
+ var fc = name[0];
+ if (!(char.IsLetter(fc) || fc == '_' || (fc == '\\' && name.Length > 2)))
+ {
+ return false;
+ }
+
+ if (name.IndexOfAny(NameInvalidChars, 1) > 0)
+ {
+ return false;
+ }
+
+ if(ExcelCellBase.IsValidAddress(name))
+ {
+ return false;
+ }
+
+ //TODO:Add check for functionnames.
+ return true;
+ }
+ public static string GetValidName(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ return name;
+ }
+
+ var fc = name[0];
+ if (!(char.IsLetter(fc) || fc == '_' || (fc == '\\' && name.Length > 2)))
+ {
+ name = "_" + name.Substring(1);
+ }
+
+ name=NameInvalidChars.Aggregate(name, (c1, c2) => c1.Replace(c2, '_'));
+ return name;
+ }
}
}
diff --git a/EPPlus/Packaging/ZipPackage.cs b/EPPlus/Packaging/ZipPackage.cs
index fc0cb25b..38ce3035 100644
--- a/EPPlus/Packaging/ZipPackage.cs
+++ b/EPPlus/Packaging/ZipPackage.cs
@@ -98,7 +98,11 @@ internal ZipPackage(Stream stream)
stream.Seek(0, SeekOrigin.Begin);
using (ZipInputStream zip = new ZipInputStream(stream))
{
- var e = zip.GetNextEntry();
+ var e = zip.GetNextEntry();
+ if(e==null)
+ {
+ throw (new InvalidDataException("The file is not an valid Package file. If the file is encrypted, please supply the password in the constructor."));
+ }
if (e.FileName.Contains("\\"))
{
_dirSeparator = '\\';
diff --git a/EPPlus/Style/XmlAccess/ExcelFontXml.cs b/EPPlus/Style/XmlAccess/ExcelFontXml.cs
index 824a4a81..c3274398 100644
--- a/EPPlus/Style/XmlAccess/ExcelFontXml.cs
+++ b/EPPlus/Style/XmlAccess/ExcelFontXml.cs
@@ -300,7 +300,7 @@ private static float GetHeightByName(string name, float size)
}
else
{
- float min = -1, max = 500;
+ float min = -1, max = float.MaxValue;
foreach (var h in FontSize.FontHeights[name])
{
if (min < h.Key && h.Key < size)
@@ -312,17 +312,20 @@ private static float GetHeightByName(string name, float size)
max = h.Key;
}
}
- if (min == max)
+ if (min == max || max==float.MaxValue)
{
return Convert.ToSingle(FontSize.FontHeights[name][min].Height);
}
+ else if (min == -1)
+ {
+ return Convert.ToSingle(FontSize.FontHeights[name][max].Height);
+ }
else
{
- return Convert.ToSingle(FontSize.FontHeights[name][min].Height + (FontSize.FontHeights[name][max].Height - FontSize.FontHeights[name][min].Height) * ((size - min) / (max - min)));
+ return Convert.ToSingle(FontSize.FontHeights[name][min].Height + (FontSize.FontHeights[name][max].Height - FontSize.FontHeights[name][min].Height) * ((size - min) / (max - min)));
}
}
}
-
internal ExcelFontXml Copy()
{
ExcelFontXml newFont = new ExcelFontXml(NameSpaceManager);
@@ -338,7 +341,6 @@ internal ExcelFontXml Copy()
newFont.Color = Color.Copy();
return newFont;
}
-
internal override XmlNode CreateXmlNode(XmlNode topElement)
{
TopNode = topElement;
diff --git a/EPPlus/Table/ExcelTable.cs b/EPPlus/Table/ExcelTable.cs
index 58b2541d..1cedfb67 100644
--- a/EPPlus/Table/ExcelTable.cs
+++ b/EPPlus/Table/ExcelTable.cs
@@ -37,6 +37,7 @@
using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
using OfficeOpenXml.Utils;
using System.Security;
+using OfficeOpenXml.FormulaParsing.ExcelUtilities;
namespace OfficeOpenXml.Table
{
@@ -157,7 +158,7 @@ private string GetStartXml(string name, int tblId)
xml += string.Format("",
tblId,
name,
- cleanDisplayName(name),
+ ExcelAddressUtil.GetValidName(name),
Address.Address);
xml += string.Format("", Address.Address);
@@ -191,7 +192,7 @@ private string GetStartXml(string name, int tblId)
return xml;
}
- private string cleanDisplayName(string name)
+ internal static string CleanDisplayName(string name)
{
return Regex.Replace(name, @"[^\w\.-_]", "_");
}
@@ -258,7 +259,7 @@ public string Name
WorkSheet.Tables._tableNames.Add(value,ix);
}
SetXmlNodeString(NAME_PATH, value);
- SetXmlNodeString(DISPLAY_NAME_PATH, cleanDisplayName(value));
+ SetXmlNodeString(DISPLAY_NAME_PATH, ExcelAddressUtil.GetValidName(value));
}
}
///
diff --git a/EPPlus/Table/ExcelTableCollection.cs b/EPPlus/Table/ExcelTableCollection.cs
index 1e0bff87..f4d7cee0 100644
--- a/EPPlus/Table/ExcelTableCollection.cs
+++ b/EPPlus/Table/ExcelTableCollection.cs
@@ -34,7 +34,7 @@
using System.Linq;
using System.Text;
using System.Xml;
-
+using OfficeOpenXml.FormulaParsing.ExcelUtilities;
namespace OfficeOpenXml.Table
{
///
@@ -78,16 +78,19 @@ public ExcelTable Add(ExcelAddressBase Range, string Name)
{
if (Range.WorkSheet != null && Range.WorkSheet != _ws.Name)
{
- throw new ArgumentException("Range does not belong to worksheet", "Range");
+ throw new ArgumentException("Range does not belong to a worksheet", "Range");
}
-
+
if (string.IsNullOrEmpty(Name))
{
Name = GetNewTableName();
}
- else if (_ws.Workbook.ExistsTableName(Name))
+ else
{
- throw (new ArgumentException("Tablename is not unique"));
+ if (_ws.Workbook.ExistsTableName(Name))
+ {
+ throw (new ArgumentException("Tablename is not unique"));
+ }
}
ValidateTableName(Name);
@@ -119,7 +122,10 @@ private void ValidateTableName(string Name)
{
throw new ArgumentException("Tablename has spaces");
}
-
+ if (!ExcelAddressUtil.IsValidName(Name))
+ {
+ throw (new ArgumentException("Tablename is not valid"));
+ }
}
public void Delete(int Index, bool ClearRange = false)
diff --git a/EPPlusTest/Address.cs b/EPPlusTest/Address.cs
index 7df746e9..188559cc 100644
--- a/EPPlusTest/Address.cs
+++ b/EPPlusTest/Address.cs
@@ -5,7 +5,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OfficeOpenXml;
using OfficeOpenXml.Style;
-
+using OfficeOpenXml.FormulaParsing.ExcelUtilities;
namespace EPPlusTest
{
///
@@ -134,7 +134,19 @@ public void IsValidCellAdress()
Assert.IsFalse(ExcelCellBase.IsValidCellAddress("Table1!A1048576:XFD1048576"));
Assert.IsFalse(ExcelCellBase.IsValidCellAddress("Table1!XFD1:XFD1048576"));
}
-
+ [TestMethod]
+ public void IsValidName()
+ {
+ Assert.IsFalse(ExcelAddressUtil.IsValidName("123sa")); //invalid start char
+ Assert.IsFalse(ExcelAddressUtil.IsValidName("*d")); //invalid start char
+ Assert.IsFalse(ExcelAddressUtil.IsValidName("\t")); //invalid start char
+ Assert.IsFalse(ExcelAddressUtil.IsValidName("\\t")); //Backslash at least three chars
+ Assert.IsFalse(ExcelAddressUtil.IsValidName("A+1")); //invalid char
+ Assert.IsFalse(ExcelAddressUtil.IsValidName("A%we")); //Address invalid
+ Assert.IsFalse(ExcelAddressUtil.IsValidName("BB73")); //Address invalid
+ Assert.IsTrue(ExcelAddressUtil.IsValidName("BBBB75")); //Valid
+ Assert.IsTrue(ExcelAddressUtil.IsValidName("BB1500005")); //Valid
+ }
[TestMethod]
public void NamedRangeMovesDownIfRowInsertedAbove()
{
diff --git a/EPPlusTest/Issues.cs b/EPPlusTest/Issues.cs
index 07924889..ee54ae92 100644
--- a/EPPlusTest/Issues.cs
+++ b/EPPlusTest/Issues.cs
@@ -1859,8 +1859,8 @@ public void Issue66()
ws.Cells["A1"].Value = 1;
ws.Cells["B1"].Formula = "A1";
var wb = pck.Workbook;
- wb.Names.Add("N1", ws.Cells["A1:A2"]);
- ws.Names.Add("N2", ws.Cells["A1"]);
+ wb.Names.Add("Name1", ws.Cells["A1:A2"]);
+ ws.Names.Add("Name2", ws.Cells["A1"]);
pck.Save();
using (var pck2 = new ExcelPackage(pck.Stream))
{
@@ -2103,6 +2103,7 @@ public void Issue204()
public void Issue170()
{
OpenTemplatePackage("print_titles_170.xlsx");
+ _pck.Compatibility.IsWorksheets1Based = false;
ExcelWorksheet sheet = _pck.Workbook.Worksheets[0];
sheet.PrinterSettings.RepeatColumns = new ExcelAddress("$A:$C");
@@ -2122,5 +2123,118 @@ public void Issue219()
_pck.Dispose();
}
+ [TestMethod]
+ [ExpectedException(typeof(InvalidDataException))]
+ public void Issue234()
+ {
+ using (var s = new MemoryStream())
+ {
+ var data = Encoding.UTF8.GetBytes("Bad data").ToArray();
+ s.Write(data, 0, data.Length);
+ var package = new ExcelPackage(s);
+ }
+ }
+
+ [TestMethod]
+ public void Issue220()
+ {
+ OpenPackage("sheetname_pbl.xlsx", true);
+ var ws=_pck.Workbook.Worksheets.Add("Deal's History");
+ var a = ws.Cells["A:B"];
+ ws.AutoFilterAddress = ws.Cells["A1:C3"];
+ _pck.Workbook.Names.Add("Test", ws.Cells["B1:D2"]);
+ var name = a.WorkSheet;
+
+ var a2 = new ExcelAddress("'Deal''s History'!a1:a3");
+ Assert.AreEqual(a2.WorkSheet, "Deal's History");
+ _pck.Save();
+ _pck.Dispose();
+
+ }
+ [ExpectedException(typeof(ArgumentException))]
+ [TestMethod]
+ public void Issue233()
+ {
+ //get some test data
+ var cars = Car.GenerateList();
+
+ OpenPackage("issue233.xlsx",true);
+
+ var sheetName = "Summary_GLEDHOWSUGARCO![]()PTY";
+
+ //Create the worksheet
+ var sheet = _pck.Workbook.Worksheets.Add(sheetName);
+
+ //Read the data into a range
+ var range = sheet.Cells["A1"].LoadFromCollection(cars, true);
+
+ //Make the range a table
+ var tbl = sheet.Tables.Add(range, $"data{sheetName}");
+ tbl.ShowTotal = true;
+ tbl.Columns["ReleaseYear"].TotalsRowFunction = OfficeOpenXml.Table.RowFunctions.Sum;
+
+ //save and dispose
+ _pck.Save();
+ _pck.Dispose();
+ }
+ public class Car
+ {
+ public int Id { get; set; }
+ public string Make { get; set; }
+ public string Model { get; set; }
+ public int ReleaseYear { get; set; }
+
+ public Car(int id, string make, string model, int releaseYear)
+ {
+ Id = Id;
+ Make = make;
+ Model = model;
+ ReleaseYear = releaseYear;
+ }
+
+ internal static List GenerateList()
+ {
+ return new List
+ {
+ //random data
+ new Car(1,"Toyota", "Carolla", 1950),
+ new Car(2,"Toyota", "Yaris", 2000),
+ new Car(3,"Toyota", "Hilux", 1990),
+ new Car(4,"Nissan", "Juke", 2010),
+ new Car(5,"Nissan", "Trail Blazer", 1995),
+ new Car(6,"Nissan", "Micra", 2018),
+ new Car(7,"BMW", "M3", 1980),
+ new Car(8,"BMW", "X5", 2008),
+ new Car(9,"BMW", "M6", 2003),
+ new Car(10,"Merc", "S Class", 2001)
+ };
+ }
+ }
+ [TestMethod]
+ public void Issue236()
+ {
+ OpenTemplatePackage("Issue236.xlsx");
+ _pck.Workbook.Worksheets["Sheet1"].Cells[7, 10].AddComment("test", "Author");
+ SaveWorksheet("Issue236-Saved.xlsx");
+ }
+ [TestMethod]
+ public void Issue228()
+ {
+ OpenTemplatePackage("Font55.xlsx");
+ var ws = _pck.Workbook.Worksheets["Sheet1"];
+ var d=ws.Drawings.AddShape("Shape1",eShapeStyle.Diamond);
+ ws.Cells["A1"].Value = "tasetraser";
+ ws.Cells.AutoFitColumns();
+ SaveWorksheet("Font55-Saved.xlsx");
+ }
+ [TestMethod]
+ public void Issue241()
+ {
+ OpenPackage("issue241",true);
+ var wks = _pck.Workbook.Worksheets.Add("test");
+ wks.DefaultRowHeight = 35;
+ _pck.Save();
+ _pck.Dispose();
}
- }
+ }
+}
diff --git a/EPPlusTest/TestBase.cs b/EPPlusTest/TestBase.cs
index 8e2e38fd..3e062cfb 100644
--- a/EPPlusTest/TestBase.cs
+++ b/EPPlusTest/TestBase.cs
@@ -61,9 +61,13 @@ public void InitBase()
_pck = new ExcelPackage();
}
- protected ExcelPackage OpenPackage(string name)
+ protected ExcelPackage OpenPackage(string name, bool delete=false)
{
var fi = new FileInfo(_worksheetPath + name);
+ if(delete && fi.Exists)
+ {
+ fi.Delete();
+ }
_pck = new ExcelPackage(fi);
return _pck;
}
diff --git a/EPPlusTest/WorkSheet.cs b/EPPlusTest/WorkSheet.cs
index 8963879b..7710e49a 100644
--- a/EPPlusTest/WorkSheet.cs
+++ b/EPPlusTest/WorkSheet.cs
@@ -1095,7 +1095,7 @@ public void ValueError()
Console.WriteLine(rt.Bold.ToString());
rt.Bold = true;
Console.WriteLine(rt.Bold.ToString());
- }
+ }
//[Ignore]
//[TestMethod]
public void FormulaError()
@@ -1588,7 +1588,7 @@ public void DefinedName()
ws.Names.AddValue("Value", 5);
ws.Names.Add("FullRow", ws.Cells["2:2"]);
ws.Names.Add("FullCol", ws.Cells["A:A"]);
- //ws.Names["Value"].Style.Border.Bottom.Color.SetColor(Color.Black);
+
ws.Names.AddFormula("Formula", "Names!A2+Names!A3+Names!Value");
}
[Ignore]
@@ -1630,13 +1630,6 @@ public void LoadDataReader()
dr[3] = 2.25;
dt.Rows.Add(dr);
- //dr = dt.NewRow();
- //dr[0] = "Row3";
- //dr[1] = 3;
- //dr[2] = true;
- //dr[3] = 3.125;
- //dt.Rows.Add(dr);
-
using (var reader = dt.CreateDataReader())
{
range = ws.Cells["A1"].LoadFromDataReader(reader, true, "My_Table",
@@ -1652,6 +1645,7 @@ public void LoadDataReader()
range = ws.Cells["A5"].LoadFromDataReader(reader, false, "My_Table2",
OfficeOpenXml.Table.TableStyles.Medium5);
}
+
Assert.AreEqual(1, range.Start.Column);
Assert.AreEqual(4, range.End.Column);
Assert.AreEqual(5, range.Start.Row);
From d14c2d9b6bb12563dec024c9cdea5f55dd558d11 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20K=C3=A4llman?=
<982881+JanKallman@users.noreply.github.com>
Date: Fri, 29 Jun 2018 13:33:55 +0200
Subject: [PATCH 2/5] Enabled worksheetcharts to use a pivottable as source by
adding a pivotTableSource parameter to the AddChart method of the Worksheets
collection. #229
---
EPPlus/Drawing/ExcelDrawings.cs | 5 ++---
EPPlus/ExcelWorksheet.cs | 4 ++--
EPPlus/ExcelWorksheets.cs | 23 ++++++++++++++++++-----
EPPlusTest/WorkSheet.cs | 9 +++++++--
4 files changed, 29 insertions(+), 12 deletions(-)
diff --git a/EPPlus/Drawing/ExcelDrawings.cs b/EPPlus/Drawing/ExcelDrawings.cs
index f4ac8b5f..6bf9f45b 100644
--- a/EPPlus/Drawing/ExcelDrawings.cs
+++ b/EPPlus/Drawing/ExcelDrawings.cs
@@ -125,9 +125,8 @@ private void AddDrawings()
ExcelDrawing dr;
switch(node.LocalName)
{
- case "oneCellAnchor":
- //dr = new ExcelDrawing(this, node, "xdr:sp/xdr:nvSpPr/xdr:cNvPr/@name");
- dr = ExcelDrawing.GetDrawing(this, node); //Issue 15373
+ case "oneCellAnchor":
+ dr = ExcelDrawing.GetDrawing(this, node);
break;
case "twoCellAnchor":
dr = ExcelDrawing.GetDrawing(this, node);
diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs
index 18a72327..ac1ff800 100644
--- a/EPPlus/ExcelWorksheet.cs
+++ b/EPPlus/ExcelWorksheet.cs
@@ -99,10 +99,10 @@ public struct ExcelCoreValue
public class ExcelChartsheet : ExcelWorksheet
{
//ExcelDrawings draws;
- public ExcelChartsheet(XmlNamespaceManager ns, ExcelPackage pck, string relID, Uri uriWorksheet, string sheetName, int sheetID, int positionID, eWorkSheetHidden hidden, eChartType chartType) :
+ public ExcelChartsheet(XmlNamespaceManager ns, ExcelPackage pck, string relID, Uri uriWorksheet, string sheetName, int sheetID, int positionID, eWorkSheetHidden hidden, eChartType chartType, ExcelPivotTable pivotTableSource ) :
base(ns, pck, relID, uriWorksheet, sheetName, sheetID, positionID, hidden)
{
- this.Drawings.AddChart("Chart 1", chartType);
+ this.Drawings.AddChart("Chart 1", chartType, pivotTableSource);
}
public ExcelChartsheet(XmlNamespaceManager ns, ExcelPackage pck, string relID, Uri uriWorksheet, string sheetName, int sheetID, int positionID, eWorkSheetHidden hidden) :
base(ns, pck, relID, uriWorksheet, sheetName, sheetID, positionID, hidden)
diff --git a/EPPlus/ExcelWorksheets.cs b/EPPlus/ExcelWorksheets.cs
index 07cdd033..715d4693 100644
--- a/EPPlus/ExcelWorksheets.cs
+++ b/EPPlus/ExcelWorksheets.cs
@@ -46,6 +46,8 @@
using OfficeOpenXml.Packaging.Ionic.Zlib;
using OfficeOpenXml.Utils;
using OfficeOpenXml.VBA;
+using OfficeOpenXml.Table.PivotTable;
+
namespace OfficeOpenXml
{
///
@@ -151,7 +153,7 @@ public ExcelWorksheet Add(string Name)
ExcelWorksheet worksheet = AddSheet(Name,false, null);
return worksheet;
}
- private ExcelWorksheet AddSheet(string Name, bool isChart, eChartType? chartType)
+ private ExcelWorksheet AddSheet(string Name, bool isChart, eChartType? chartType, ExcelPivotTable pivotTableSource = null)
{
int sheetID;
Uri uriWorksheet;
@@ -177,7 +179,7 @@ private ExcelWorksheet AddSheet(string Name, bool isChart, eChartType? chartType
ExcelWorksheet worksheet;
if (isChart)
{
- worksheet = new ExcelChartsheet(_namespaceManager, _pck, rel, uriWorksheet, Name, sheetID, positionID, eWorkSheetHidden.Visible, (eChartType)chartType);
+ worksheet = new ExcelChartsheet(_namespaceManager, _pck, rel, uriWorksheet, Name, sheetID, positionID, eWorkSheetHidden.Visible, (eChartType)chartType, pivotTableSource);
}
else
{
@@ -293,12 +295,23 @@ public ExcelWorksheet Add(string Name, ExcelWorksheet Copy)
///
/// Adds a chartsheet to the workbook.
///
- ///
- ///
+ /// The name of the worksheet
+ /// The type of chart
///
public ExcelChartsheet AddChart(string Name, eChartType chartType)
{
- return (ExcelChartsheet)AddSheet(Name, true, chartType);
+ return (ExcelChartsheet)AddSheet(Name, true, chartType, null);
+ }
+ ///
+ /// Adds a chartsheet to the workbook.
+ ///
+ /// The name of the worksheet
+ /// The type of chart
+ /// The pivottable source
+ ///
+ public ExcelChartsheet AddChart(string Name, eChartType chartType, ExcelPivotTable pivotTableSource)
+ {
+ return (ExcelChartsheet)AddSheet(Name, true, chartType, pivotTableSource);
}
private void CopySheetNames(ExcelWorksheet Copy, ExcelWorksheet added)
{
diff --git a/EPPlusTest/WorkSheet.cs b/EPPlusTest/WorkSheet.cs
index 7710e49a..9b7bb6d5 100644
--- a/EPPlusTest/WorkSheet.cs
+++ b/EPPlusTest/WorkSheet.cs
@@ -1144,8 +1144,13 @@ public void PivotTableTest()
new object [] { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55}
});
var table = ws.Tables.Add(ws.Cells["A1:D4"], "PivotData");
- ws.PivotTables.Add(ws.Cells["G1"], ws.Cells["A1:D4"], "PivotTable");
- Assert.AreEqual("PivotStyleMedium9", ws.PivotTables["PivotTable"].StyleName);
+ var pt=ws.PivotTables.Add(ws.Cells["G20"], ws.Cells["A1:D4"], "PivotTable1");
+ pt.ColumnFields.Add(pt.Fields[1]);
+ pt.DataFields.Add(pt.Fields[3]);
+ Assert.AreEqual("PivotStyleMedium9", ws.PivotTables["PivotTable1"].StyleName);
+
+ _pck.Workbook.Worksheets.AddChart("PivotChartWorksheet", eChartType.Line, pt);
+ SaveWorksheet("Pivot.xlsx");
}
From 9d94735e9441bbc0c0ee2d226eda3044fadf8a32 Mon Sep 17 00:00:00 2001
From: Mats Alm
Date: Tue, 17 Jul 2018 13:42:20 +0200
Subject: [PATCH 3/5] Value function did not handle blank values
---
EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs | 4 +++-
.../ExcelRanges/TextExcelRangeTests.cs | 12 ++++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs b/EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs
index c49d6a80..577e5f93 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/Text/Value.cs
@@ -21,8 +21,10 @@ public class Value : ExcelFunction
public override CompileResult Execute(IEnumerable arguments, ParsingContext context)
{
ValidateArguments(arguments, 1);
- var val = ArgToString(arguments, 0).TrimEnd(' ');
+ var val = ArgToString(arguments, 0);
double result = 0d;
+ if (string.IsNullOrEmpty(val)) return CreateResult(result, DataType.Integer);
+ val = val.TrimEnd(' ');
if (Regex.IsMatch(val, $"^[\\d]*({Regex.Escape(_groupSeparator)}?[\\d]*)?({Regex.Escape(_decimalSeparator)}[\\d]*)?[ ?% ?]?$"))
{
if (val.EndsWith("%"))
diff --git a/EPPlusTest/FormulaParsing/IntegrationTests/BuiltInFunctions/ExcelRanges/TextExcelRangeTests.cs b/EPPlusTest/FormulaParsing/IntegrationTests/BuiltInFunctions/ExcelRanges/TextExcelRangeTests.cs
index 5d97b514..f367d3e3 100644
--- a/EPPlusTest/FormulaParsing/IntegrationTests/BuiltInFunctions/ExcelRanges/TextExcelRangeTests.cs
+++ b/EPPlusTest/FormulaParsing/IntegrationTests/BuiltInFunctions/ExcelRanges/TextExcelRangeTests.cs
@@ -149,5 +149,17 @@ public void ValueShouldHandleTime()
var result = _worksheet.Cells["A4"].Value;
Assert.AreEqual(0.5, result);
}
+
+ [TestMethod]
+ public void ValueShouldReturn0IfValueIsNull()
+ {
+
+ _worksheet.Cells["A1"].Value = null;
+ _worksheet.Cells["A4"].Formula = "Value(A1)";
+ _worksheet.Calculate();
+ var result = _worksheet.Cells["A4"].Value;
+ Assert.AreEqual(0d, result);
+ }
+
}
}
From 26303b8baf28b680021345f79567f5acf53159f0 Mon Sep 17 00:00:00 2001
From: Mats Alm
Date: Mon, 6 Aug 2018 09:15:07 +0200
Subject: [PATCH 4/5] Implemented Pmt function
---
EPPlus/EPPlus.csproj | 1 +
.../Excel/Functions/BuiltInFunctions.cs | 3 +
.../Excel/Functions/Finance/Pmt.cs | 59 +++++++++++++++++++
EPPlusTest/EPPlusTest.csproj | 1 +
.../Excel/Functions/FinanceFunctionTests.cs | 24 ++++++++
5 files changed, 88 insertions(+)
create mode 100644 EPPlus/FormulaParsing/Excel/Functions/Finance/Pmt.cs
create mode 100644 EPPlusTest/FormulaParsing/Excel/Functions/FinanceFunctionTests.cs
diff --git a/EPPlus/EPPlus.csproj b/EPPlus/EPPlus.csproj
index 7f9cde7a..eeb1bdbc 100644
--- a/EPPlus/EPPlus.csproj
+++ b/EPPlus/EPPlus.csproj
@@ -400,6 +400,7 @@
+
diff --git a/EPPlus/FormulaParsing/Excel/Functions/BuiltInFunctions.cs b/EPPlus/FormulaParsing/Excel/Functions/BuiltInFunctions.cs
index d505cd19..42e59e9f 100644
--- a/EPPlus/FormulaParsing/Excel/Functions/BuiltInFunctions.cs
+++ b/EPPlus/FormulaParsing/Excel/Functions/BuiltInFunctions.cs
@@ -34,6 +34,7 @@
using OfficeOpenXml.FormulaParsing.Excel.Functions.Numeric;
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Information;
+using OfficeOpenXml.FormulaParsing.Excel.Functions.Finance;
namespace OfficeOpenXml.FormulaParsing.Excel.Functions
{
@@ -202,6 +203,8 @@ public BuiltInFunctions()
Functions["daverage"] = new Daverage();
Functions["dvar"] = new Dvar();
Functions["dvarp"] = new Dvarp();
+ //Finance
+ Functions["pmt"] = new Pmt();
}
}
}
diff --git a/EPPlus/FormulaParsing/Excel/Functions/Finance/Pmt.cs b/EPPlus/FormulaParsing/Excel/Functions/Finance/Pmt.cs
new file mode 100644
index 00000000..3f0424ba
--- /dev/null
+++ b/EPPlus/FormulaParsing/Excel/Functions/Finance/Pmt.cs
@@ -0,0 +1,59 @@
+/* Copyright (C) 2011 Jan Källman
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
+ * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
+ *
+ * All code and executables are provided "as is" with no warranty either express or implied.
+ * The author accepts no liability for any damage or loss of business that this product may cause.
+ *
+ * Code change notes:
+ *
+ * Author Change Date
+ *******************************************************************************
+ * Mats Alm Added 2018-07-17
+ *******************************************************************************/
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using OfficeOpenXml.FormulaParsing.ExpressionGraph;
+
+namespace OfficeOpenXml.FormulaParsing.Excel.Functions.Finance
+{
+ public class Pmt : ExcelFunction
+ {
+ public override CompileResult Execute(IEnumerable arguments, ParsingContext context)
+ {
+ ValidateArguments(arguments, 3);
+ var rate = ArgToDecimal(arguments, 0);
+ var nPer = ArgToInt(arguments, 1);
+ var presentValue = ArgToDecimal(arguments, 2);
+ var payEndOfPeriod = false;
+ var futureValue = 0d;
+ if (arguments.Count() > 3) futureValue = ArgToDecimal(arguments, 3);
+ if (arguments.Count() > 4) payEndOfPeriod = ArgToBool(arguments, 4);
+
+ var result = (futureValue + presentValue * System.Math.Pow(rate + 1, nPer)) * rate
+ /
+ ((payEndOfPeriod ? rate + 1 : 1) * (1 - System.Math.Pow(rate + 1, nPer)));
+
+
+ return CreateResult(result, DataType.Decimal);
+ }
+
+ private static double GetInterest(double rate, double remainingAmount)
+ {
+ return remainingAmount * rate;
+ }
+ }
+}
diff --git a/EPPlusTest/EPPlusTest.csproj b/EPPlusTest/EPPlusTest.csproj
index b1fac8e6..98430bf1 100644
--- a/EPPlusTest/EPPlusTest.csproj
+++ b/EPPlusTest/EPPlusTest.csproj
@@ -100,6 +100,7 @@
+
diff --git a/EPPlusTest/FormulaParsing/Excel/Functions/FinanceFunctionTests.cs b/EPPlusTest/FormulaParsing/Excel/Functions/FinanceFunctionTests.cs
new file mode 100644
index 00000000..3e913c99
--- /dev/null
+++ b/EPPlusTest/FormulaParsing/Excel/Functions/FinanceFunctionTests.cs
@@ -0,0 +1,24 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using OfficeOpenXml;
+
+namespace EPPlusTest.FormulaParsing.Excel.Functions
+{
+ [TestClass]
+ public class FinanceFunctionTests
+ {
+ [TestMethod]
+ public void PmtTest1()
+ {
+ using (var package = new ExcelPackage())
+ {
+ var sheet = package.Workbook.Worksheets.Add("test");
+ sheet.Cells["A1"].Formula = "PMT( 5%/12, 60, 50000 )";
+ sheet.Calculate();
+ var value = sheet.Cells["A1"].Value;
+ var value2 = System.Math.Round(Convert.ToDouble(value), 2);
+ Assert.AreEqual(-943.56, value2);
+ }
+ }
+ }
+}
From 642e73bef00699b7d193f9b6305135b5c2ad87c0 Mon Sep 17 00:00:00 2001
From: Mats Alm
Date: Mon, 6 Aug 2018 09:45:39 +0200
Subject: [PATCH 5/5] Fixed issue #276
---
EPPlus/FormulaParsing/CalculateExtentions.cs | 2 +-
.../ExpressionGraph/ExpressionConverter.cs | 3 +++
.../Excel/Functions/DateTimeFunctionsTests.cs | 14 ++++++++++++++
3 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/EPPlus/FormulaParsing/CalculateExtentions.cs b/EPPlus/FormulaParsing/CalculateExtentions.cs
index b2c5ee0c..d649ac22 100644
--- a/EPPlus/FormulaParsing/CalculateExtentions.cs
+++ b/EPPlus/FormulaParsing/CalculateExtentions.cs
@@ -157,7 +157,7 @@ private static void CalcChain(ExcelWorkbook wb, FormulaParser parser, Dependency
{
throw (fe);
}
- catch
+ catch(Exception e)
{
var error = ExcelErrorValue.Parse(ExcelErrorValue.Values.Value);
SetValue(wb, item, error);
diff --git a/EPPlus/FormulaParsing/ExpressionGraph/ExpressionConverter.cs b/EPPlus/FormulaParsing/ExpressionGraph/ExpressionConverter.cs
index d7402840..8c75fc94 100644
--- a/EPPlus/FormulaParsing/ExpressionGraph/ExpressionConverter.cs
+++ b/EPPlus/FormulaParsing/ExpressionGraph/ExpressionConverter.cs
@@ -74,6 +74,9 @@ public Expression FromCompileResult(CompileResult compileResult)
: new ExcelErrorExpression((ExcelErrorValue) compileResult.Result);
case DataType.Empty:
return new IntegerExpression(0); //Added JK
+ case DataType.Time:
+ case DataType.Date:
+ return new DecimalExpression((double)compileResult.Result);
}
return null;
diff --git a/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs b/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs
index 47507735..f56b8c57 100644
--- a/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs
+++ b/EPPlusTest/FormulaParsing/Excel/Functions/DateTimeFunctionsTests.cs
@@ -581,5 +581,19 @@ public void NetworkdayIntlShouldReduceHoliday()
Assert.AreEqual(13, ws.Cells["A1"].Value);
}
}
+
+ [TestMethod]
+ public void TimeAddition()
+ {
+ using (var package = new ExcelPackage())
+ {
+ var ws = package.Workbook.Worksheets.Add("test");
+ ws.Cells["A1"].Formula = "1 + (Time(10,0,0))";
+ ws.Calculate();
+ var result = Convert.ToDouble(ws.Cells["A1"].Value);
+ result = Math.Round(result, 2);
+ Assert.AreEqual(1.42d, result);
+ }
+ }
}
}