diff --git a/EPPlus/CellStore.cs b/EPPlus/CellStore.cs index 50b6595f..18ae374b 100644 --- a/EPPlus/CellStore.cs +++ b/EPPlus/CellStore.cs @@ -30,11 +30,8 @@ *******************************************************************************/ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Collections; using OfficeOpenXml; -using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; internal class IndexBase : IComparable { @@ -53,7 +50,7 @@ internal int IndexPointer get; set; } - internal short Index; + internal int Index; public int CompareTo(IndexItem other) { return Index - other.Index; @@ -653,7 +650,7 @@ internal void SetValue(int Row, int Column, T Value) pageItem = _columnIndex[col]._pages[pos]; } - short ix = (short)(Row - ((pageItem.Index << pageBits) + pageItem.Offset)); + int ix = Row - ((pageItem.Index << pageBits) + pageItem.Offset); _searchItem.Index = ix; var cellPos = Array.BinarySearch(pageItem.Rows, 0, pageItem.RowCount, _searchItem); if (cellPos < 0) @@ -671,7 +668,7 @@ internal void SetValue(int Row, int Column, T Value) col = ~col; AddColumn(col, Column); AddPage(_columnIndex[col], 0, page); - short ix = (short)(Row - (page << pageBits)); + int ix = Row - (page << pageBits); AddCell(_columnIndex[col], 0, 0, ix, Value); } } @@ -742,7 +739,7 @@ internal void SetRangeValueSpecial(int fromRow, int fromColumn, int toRow, int t pageItem = _columnIndex[col]._pages[pos]; } - short ix = (short)(rowIx - ((pageItem.Index << pageBits) + pageItem.Offset)); + int ix = rowIx - ((pageItem.Index << pageBits) + pageItem.Offset); _searchItem.Index = ix; var cellPos = Array.BinarySearch(pageItem.Rows, 0, pageItem.RowCount, _searchItem); if (cellPos < 0) @@ -761,7 +758,7 @@ internal void SetRangeValueSpecial(int fromRow, int fromColumn, int toRow, int t col = ~col; AddColumn(col, colIx); AddPage(_columnIndex[col], 0, page); - short ix = (short)(rowIx - (page << pageBits)); + int ix = rowIx - (page << pageBits); AddCell(_columnIndex[col], 0, 0, ix, default(T)); Updater(_values, _columnIndex[col]._pages[0].Rows[0].IndexPointer, rowIx, colIx, Value); } @@ -812,7 +809,7 @@ internal void SetValueSpecial(int Row, int Column, SetValueDelegate Updater, obj pageItem = _columnIndex[col]._pages[pos]; } - short ix = (short)(Row - ((pageItem.Index << pageBits) + pageItem.Offset)); + int ix = Row - ((pageItem.Index << pageBits) + pageItem.Offset); _searchItem.Index = ix; var cellPos = Array.BinarySearch(pageItem.Rows, 0, pageItem.RowCount, _searchItem); if (cellPos < 0) @@ -831,7 +828,7 @@ internal void SetValueSpecial(int Row, int Column, SetValueDelegate Updater, obj col = ~col; AddColumn(col, Column); AddPage(_columnIndex[col], 0, page); - short ix = (short)(Row - (page << pageBits)); + int ix = Row - (page << pageBits); AddCell(_columnIndex[col], 0, 0, ix, default(T)); Updater(_values, _columnIndex[col]._pages[0].Rows[0].IndexPointer, Value); } @@ -1421,7 +1418,7 @@ internal static int GetSize(int size) } return newSize; } - private void AddCell(ColumnIndex columnIndex, int pagePos, int pos, short ix, T value) + private void AddCell(ColumnIndex columnIndex, int pagePos, int pos, int ix, T value) { PageIndex pageItem = columnIndex._pages[pagePos]; if (pageItem.RowCount == pageItem.Rows.Length) diff --git a/EPPlus/Drawing/ExcelPicture.cs b/EPPlus/Drawing/ExcelPicture.cs index e413ae02..3e208fc4 100644 --- a/EPPlus/Drawing/ExcelPicture.cs +++ b/EPPlus/Drawing/ExcelPicture.cs @@ -38,8 +38,10 @@ using System.Drawing; using System.Drawing.Imaging; using System.Diagnostics; +using System.Windows; using OfficeOpenXml.Utils; using OfficeOpenXml.Compatibility; +using Size = System.Drawing.Size; namespace OfficeOpenXml.Drawing { @@ -353,6 +355,16 @@ internal string ContentType get; set; } + + //public Vector GetOffset() + //{ + // return new Vector(_left, _top); + //} + public Size GetSize() + { + return new Size(_width, _height); + } + /// /// Set the size of the image in percent from the orginal size /// Note that resizing columns / rows after using this function will effect the size of the picture diff --git a/EPPlus/EPPlus.MultiTarget.csproj b/EPPlus/EPPlus.MultiTarget.csproj index 9566094a..e954205e 100644 --- a/EPPlus/EPPlus.MultiTarget.csproj +++ b/EPPlus/EPPlus.MultiTarget.csproj @@ -1,7 +1,7 @@  - netstandard2.1;netstandard2.0;net35;net40 + netcoreapp3.1;netcoreapp3.0;netstandard2.1;netcoreapp2.2;netcoreapp2.1;netstandard2.0;net40 4.5.3.3 4.5.3.3 4.5.3.3 @@ -207,30 +207,30 @@ Beta 2 Changes * Fixed bug when using RepeatColumns & RepeatRows at the same time. -* VBA project will be left untouched if it’s not accessed. -* Fixed problem with strings on save. -* Added locks to the cell store for access by multiple threads. -* Implemented Indirect function -* Used DisplayNameAttribute to generate column headers from LoadFromCollection -* Rewrote ExcelRangeBase.Copy function. -* Added caching to Save ZipStream for Cells and shared strings to speed up the Save method. -* Added Missing InsertColumn and DeleteColumn -* Added pull request to support Date1904 -* Added pull request ExcelWorksheet. LoadFromDataReader - -Release Candidate changes -* Fixed some problems with Range.Copy Function -* InsertColumn and Delete column didn't work in some cases -* Chart.DisplayBlankAs had the wrong default type in Excel 2010+ -* Datavalidation list overflow caused corruption of the package -* Fixed a few Calculation when referring ranges (for example If function) -* Added ChartAxis.DisplayUnit -* Fixed a bug related to shared formulas -* Named styles failed in some cases. -* Style.Indent got an invalid value in some cases. -* Fixed a problem with AutofitColumns method. -* Performance fix. -* A whole lot of other small fixes. + * VBA project will be left untouched if it’s not accessed. + * Fixed problem with strings on save. + * Added locks to the cell store for access by multiple threads. + * Implemented Indirect function + * Used DisplayNameAttribute to generate column headers from LoadFromCollection + * Rewrote ExcelRangeBase.Copy function. + * Added caching to Save ZipStream for Cells and shared strings to speed up the Save method. + * Added Missing InsertColumn and DeleteColumn + * Added pull request to support Date1904 + * Added pull request ExcelWorksheet. LoadFromDataReader + + Release Candidate changes + * Fixed some problems with Range.Copy Function + * InsertColumn and Delete column didn't work in some cases + * Chart.DisplayBlankAs had the wrong default type in Excel 2010+ + * Datavalidation list overflow caused corruption of the package + * Fixed a few Calculation when referring ranges (for example If function) + * Added ChartAxis.DisplayUnit + * Fixed a bug related to shared formulas + * Named styles failed in some cases. + * Style.Indent got an invalid value in some cases. + * Fixed a problem with AutofitColumns method. + * Performance fix. + * A whole lot of other small fixes. true OpenOfficeXml.snk @@ -246,6 +246,23 @@ Release Candidate changes Core + + Core + + + + Core + + + + Core + + + + Core + + + NET35;NETFULL @@ -291,12 +308,10 @@ Release Candidate changes - + - - 4.7.0 - + @@ -305,20 +320,41 @@ Release Candidate changes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - 4.7.0 - - - + + - - @@ -329,4 +365,4 @@ Release Candidate changes - \ No newline at end of file + diff --git a/EPPlus/EPPlus.csproj b/EPPlus/EPPlus.csproj index 1f411ce0..29d2775d 100644 --- a/EPPlus/EPPlus.csproj +++ b/EPPlus/EPPlus.csproj @@ -225,6 +225,7 @@ + diff --git a/EPPlus/Encryption/EncryptionHandler.cs b/EPPlus/Encryption/EncryptionHandler.cs index 2f3d23ac..fdf70819 100644 --- a/EPPlus/Encryption/EncryptionHandler.cs +++ b/EPPlus/Encryption/EncryptionHandler.cs @@ -270,7 +270,7 @@ private HMAC GetHmacProvider(EncryptionInfoAgile.EncryptionKeyData ei, byte[] sa { switch (ei.HashAlgorithm) { -#if (!Core) +#if !Core case eHashAlogorithm.RIPEMD160: return new HMACRIPEMD160(salt); #endif diff --git a/EPPlus/ExcelHeaderFooter.cs b/EPPlus/ExcelHeaderFooter.cs index cf894fc3..107b6979 100644 --- a/EPPlus/ExcelHeaderFooter.cs +++ b/EPPlus/ExcelHeaderFooter.cs @@ -164,7 +164,7 @@ public ExcelVmlDrawingPicture InsertPicture(FileInfo PictureFile, PictureAlignme string contentType = ExcelPicture.GetContentType(PictureFile.Extension); var uriPic = XmlHelper.GetNewUri(_ws._package.Package, "/xl/media/" + PictureFile.Name.Substring(0, PictureFile.Name.Length-PictureFile.Extension.Length) + "{0}" + PictureFile.Extension); -#if (Core) +#if Core var imgBytes=ImageCompat.GetImageAsByteArray(Picture); #else var ic = new ImageConverter(); diff --git a/EPPlus/ExcelIgnoredError.cs b/EPPlus/ExcelIgnoredError.cs new file mode 100644 index 00000000..5f6816a0 --- /dev/null +++ b/EPPlus/ExcelIgnoredError.cs @@ -0,0 +1,67 @@ +/******************************************************************************* + * Implemented following briddums advise ( https://stackoverflow.com/users/260473/briddums ), + * as himself explained in https://stackoverflow.com/questions/11858109/using-epplus-excel-how-to-ignore-excel-error-checking-or-remove-green-tag-on-t/14483234#14483234 + * The best way to address this issue is adding a whorksheet property that allows to ignore the warnings in a specified range. + *******************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; + +namespace OfficeOpenXml +{ + public class ExcelIgnoredError : XmlHelper + { + private ExcelWorksheet _worksheet; + + /// + /// Constructor + /// + internal ExcelIgnoredError(XmlNamespaceManager ns, XmlNode node, ExcelWorksheet xlWorkSheet) : + base(ns, node) + { + _worksheet = xlWorkSheet; + } + + + public bool NumberStoredAsText + { + get + { + return GetXmlNodeBool("@numberStoredAsText"); + } + set + { + SetXmlNodeBool("@numberStoredAsText", value); + } + } + + + public bool TwoDigitTextYear + { + get + { + return GetXmlNodeBool("@twoDigitTextYear"); + } + set + { + SetXmlNodeBool("@twoDigitTextYear", value); + } + } + + + public string Range + { + get + { + return GetXmlNodeString("@sqref"); + } + set + { + SetXmlNodeString("@sqref", value); + } + } + } +} \ No newline at end of file diff --git a/EPPlus/ExcelPackage.cs b/EPPlus/ExcelPackage.cs index c3e5dddb..e253f8f4 100644 --- a/EPPlus/ExcelPackage.cs +++ b/EPPlus/ExcelPackage.cs @@ -47,6 +47,8 @@ using OfficeOpenXml.Compatibility; using System.Text; #if (Core) +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Configuration; #endif namespace OfficeOpenXml @@ -174,34 +176,34 @@ public enum CompressionLevel /// /// public sealed class ExcelPackage : IDisposable - { - internal const bool preserveWhitespace=false; + { + internal const bool preserveWhitespace = false; Stream _stream = null; - private bool _isExternalStream=false; + private bool _isExternalStream = false; internal class ImageInfo { internal string Hash { get; set; } - internal Uri Uri{get;set;} + internal Uri Uri { get; set; } internal int RefCount { get; set; } internal Packaging.ZipPackagePart Part { get; set; } } internal Dictionary _images = new Dictionary(); - #region Properties - /// - /// Extention Schema types - /// + #region Properties + /// + /// Extention Schema types + /// internal const string schemaXmlExtension = "application/xml"; internal const string schemaRelsExtension = "application/vnd.openxmlformats-package.relationships+xml"; /// /// Main Xml schema name /// internal const string schemaMain = @"http://schemas.openxmlformats.org/spreadsheetml/2006/main"; - - /// - /// Relationship schema name - /// - internal const string schemaRelationships = @"http://schemas.openxmlformats.org/officeDocument/2006/relationships"; - + + /// + /// Relationship schema name + /// + internal const string schemaRelationships = @"http://schemas.openxmlformats.org/officeDocument/2006/relationships"; + internal const string schemaDrawings = @"http://schemas.openxmlformats.org/drawingml/2006/main"; internal const string schemaSheetDrawings = @"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"; internal const string schemaMarkupCompatibility = @"http://schemas.openxmlformats.org/markup-compatibility/2006"; @@ -210,7 +212,7 @@ internal class ImageInfo internal const string schemaMicrosoftOffice = "urn:schemas-microsoft-com:office:office"; internal const string schemaMicrosoftExcel = "urn:schemas-microsoft-com:office:excel"; - internal const string schemaChart = @"http://schemas.openxmlformats.org/drawingml/2006/chart"; + internal const string schemaChart = @"http://schemas.openxmlformats.org/drawingml/2006/chart"; internal const string schemaHyperlink = @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"; internal const string schemaComment = @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"; internal const string schemaImage = @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"; @@ -223,7 +225,7 @@ internal class ImageInfo internal const string schemaDcmiType = @"http://purl.org/dc/dcmitype/"; internal const string schemaXsi = @"http://www.w3.org/2001/XMLSchema-instance"; internal const string schemaVt = @"http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"; - + internal const string schemaMainX14 = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"; internal const string schemaMainXm = "http://schemas.microsoft.com/office/excel/2006/main"; internal const string schemaXr = "http://schemas.microsoft.com/office/spreadsheetml/2014/revision"; @@ -243,7 +245,7 @@ internal class ImageInfo internal const string contentTypeSharedString = @"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"; //Package reference private Packaging.ZipPackage _package; - internal ExcelWorkbook _workbook; + internal ExcelWorkbook _workbook; /// /// Maximum number of columns in a worksheet (16384). /// @@ -252,8 +254,8 @@ internal class ImageInfo /// Maximum number of rows in a worksheet (1048576). /// public const int MaxRows = 1048576; - #endregion - #region ExcelPackage Constructors + #endregion + #region ExcelPackage Constructors /// /// Create a new instance of the ExcelPackage. /// Output is accessed through the Stream property, using the method or later set the property. @@ -268,7 +270,7 @@ public ExcelPackage() /// /// If newFile exists, it is opened. Otherwise it is created from scratch. public ExcelPackage(FileInfo newFile) - { + { Init(); File = newFile; ConstructNewFile(null); @@ -284,18 +286,18 @@ public ExcelPackage(FileInfo newFile, string password) File = newFile; ConstructNewFile(password); } - /// - /// Create a new instance of the ExcelPackage class based on a existing template. - /// If newFile exists, it will be overwritten when the Save method is called - /// - /// The name of the Excel file to be created - /// The name of the Excel template to use as the basis of the new Excel file - public ExcelPackage(FileInfo newFile, FileInfo template) - { + /// + /// Create a new instance of the ExcelPackage class based on a existing template. + /// If newFile exists, it will be overwritten when the Save method is called + /// + /// The name of the Excel file to be created + /// The name of the Excel template to use as the basis of the new Excel file + public ExcelPackage(FileInfo newFile, FileInfo template) + { Init(); File = newFile; CreateFromTemplate(template, null); - } + } /// /// Create a new instance of the ExcelPackage class based on a existing template. /// If newFile exists, it will be overwritten when the Save method is called @@ -342,7 +344,7 @@ public ExcelPackage(FileInfo template, bool useStream, string password) /// Create a new instance of the ExcelPackage class based on a stream /// /// The stream object can be empty or contain a package. The stream must be Read/Write - public ExcelPackage(Stream newStream) + public ExcelPackage(Stream newStream) { Init(); if (newStream.Length == 0) @@ -352,7 +354,7 @@ public ExcelPackage(Stream newStream) ConstructNewFile(null); } else - { + { Load(newStream); } } @@ -371,7 +373,7 @@ public ExcelPackage(Stream newStream, string Password) Init(); if (newStream.Length > 0) { - Load(newStream,Password); + Load(newStream, Password); } else { @@ -390,7 +392,7 @@ public ExcelPackage(Stream newStream, Stream templateStream) { if (newStream.Length > 0) { - throw(new Exception("The output stream must be empty. Length > 0")); + throw (new Exception("The output stream must be empty. Length > 0")); } else if (!(newStream.CanRead && newStream.CanWrite)) { @@ -430,7 +432,7 @@ internal ImageInfo AddImage(byte[] image, Uri uri, string contentType) #else var hashProvider = new SHA1CryptoServiceProvider(); #endif - var hash = BitConverter.ToString(hashProvider.ComputeHash(image)).Replace("-",""); + var hash = BitConverter.ToString(hashProvider.ComputeHash(image)).Replace("-", ""); lock (_images) { if (_images.ContainsKey(hash)) @@ -498,7 +500,7 @@ internal ImageInfo GetImageInfo(byte[] image) #else var hashProvider = new SHA1CryptoServiceProvider(); #endif - var hash = BitConverter.ToString(hashProvider.ComputeHash(image)).Replace("-",""); + var hash = BitConverter.ToString(hashProvider.ComputeHash(image)).Replace("-", ""); if (_images.ContainsKey(hash)) { @@ -531,7 +533,7 @@ private void Init() var build = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", true,false); + .AddJsonFile("appsettings.json", true, false); var c = build.Build(); var v = c["EPPlus:ExcelPackage:Compatibility:IsWorksheets1Based"]; @@ -540,7 +542,7 @@ private void Init() #endif if (v != null) { - if(Boolean.TryParse(v.ToLowerInvariant(), out bool value)) + if (Boolean.TryParse(v.ToLowerInvariant(), out bool value)) { Compatibility.IsWorksheets1Based = value; } @@ -557,7 +559,7 @@ private void CreateFromTemplate(FileInfo template, string password) if (template != null) template.Refresh(); if (template.Exists) { - if(_stream==null) _stream=new MemoryStream(); + if (_stream == null) _stream = new MemoryStream(); var ms = new MemoryStream(); if (password != null) { @@ -569,7 +571,7 @@ private void CreateFromTemplate(FileInfo template, string password) } else { - WriteFileToStream(template.FullName, ms); + WriteFileToStream(template.FullName, ms); } try { @@ -617,7 +619,7 @@ private void ConstructNewFile(string password) _package = new Packaging.ZipPackage(ms); } catch (Exception ex) - { + { if (password == null && CompoundDocument.IsCompoundDocument(File)) { throw new Exception("Can not open the package. Package is an OLE compound document. If this is an encrypted package, please supply the password", ex); @@ -659,11 +661,11 @@ private void CreateBlankWb() _package.CreateRelationship(UriHelper.GetRelativeUri(new Uri("/xl", UriKind.Relative), Workbook.WorkbookUri), Packaging.TargetMode.Internal, schemaRelationships + "/officeDocument"); } - /// - /// Returns a reference to the package - /// - public Packaging.ZipPackage Package { get { return (_package); } } - ExcelEncryption _encryption=null; + /// + /// Returns a reference to the package + /// + public Packaging.ZipPackage Package { get { return (_package); } } + ExcelEncryption _encryption = null; /// /// Information how and if the package is encrypted /// @@ -678,14 +680,14 @@ public ExcelEncryption Encryption return _encryption; } } - /// - /// Returns a reference to the workbook component within the package. - /// All worksheets and cells can be accessed through the workbook. - /// - public ExcelWorkbook Workbook - { - get - { + /// + /// Returns a reference to the workbook component within the package. + /// All worksheets and cells can be accessed through the workbook. + /// + public ExcelWorkbook Workbook + { + get + { if (_workbook == null) { var nsm = CreateDefaultNSM(); @@ -697,8 +699,8 @@ public ExcelWorkbook Workbook } return (_workbook); - } - } + } + } /// /// Automaticlly adjust drawing size when column width/row height are adjusted, depending on the drawings editBy property. /// Default True @@ -715,7 +717,7 @@ private XmlNamespaceManager CreateDefaultNSM() NameTable nt = new NameTable(); var ns = new XmlNamespaceManager(nt); ns.AddNamespace(string.Empty, ExcelPackage.schemaMain); - ns.AddNamespace("d", ExcelPackage.schemaMain); + ns.AddNamespace("d", ExcelPackage.schemaMain); ns.AddNamespace("r", ExcelPackage.schemaRelationships); ns.AddNamespace("c", ExcelPackage.schemaChart); ns.AddNamespace("vt", schemaVt); @@ -738,31 +740,31 @@ private XmlNamespaceManager CreateDefaultNSM() return ns; } - -#region SavePart - /// - /// Saves the XmlDocument into the package at the specified Uri. - /// - /// The Uri of the component - /// The XmlDocument to save - internal void SavePart(Uri uri, XmlDocument xmlDoc) - { + + #region SavePart + /// + /// Saves the XmlDocument into the package at the specified Uri. + /// + /// The Uri of the component + /// The XmlDocument to save + internal void SavePart(Uri uri, XmlDocument xmlDoc) + { Packaging.ZipPackagePart part = _package.GetPart(uri); var stream = part.GetStream(FileMode.Create, FileAccess.Write); var xr = new XmlTextWriter(stream, Encoding.UTF8); xr.Formatting = Formatting.None; - + xmlDoc.Save(xr); - } + } /// /// Saves the XmlDocument into the package at the specified Uri. /// /// The Uri of the component /// The XmlDocument to save internal void SaveWorkbook(Uri uri, XmlDocument xmlDoc) - { + { Packaging.ZipPackagePart part = _package.GetPart(uri); - if(Workbook.VbaProject==null) + if (Workbook.VbaProject == null) { if (part.ContentType != contentTypeWorkbookDefault) { @@ -788,24 +790,24 @@ internal void SaveWorkbook(Uri uri, XmlDocument xmlDoc) xr.Formatting = Formatting.None; xmlDoc.Save(xr); - } + } -#endregion + #endregion -#region Dispose - /// - /// Closes the package. - /// - public void Dispose() - { - if(_package != null) + #region Dispose + /// + /// Closes the package. + /// + public void Dispose() + { + if (_package != null) { - if (_isExternalStream==false && _stream != null && (_stream.CanRead || _stream.CanWrite)) + if (_isExternalStream == false && _stream != null && (_stream.CanRead || _stream.CanWrite)) { CloseStream(); } _package.Close(); - if(_workbook != null) + if (_workbook != null) { _workbook.Dispose(); } @@ -817,7 +819,7 @@ public void Dispose() _workbook = null; GC.Collect(); } - } + } #endregion #region Save // ExcelPackage save @@ -842,15 +844,15 @@ public void Save() Workbook.Save(); if (File == null) { - if(Encryption.IsEncrypted) + if (Encryption.IsEncrypted) { var ms = new MemoryStream(); _package.Save(ms); - byte[] file = ms.ToArray(); + byte[] file = ms.ToArray(); EncryptedPackageHandler eph = new EncryptedPackageHandler(); var msEnc = eph.EncryptPackage(file, Encryption); CopyStream(msEnc, ref _stream); - } + } else { _package.Save(_stream); @@ -883,11 +885,11 @@ public void Save() byte[] file = ((MemoryStream)Stream).ToArray(); EncryptedPackageHandler eph = new EncryptedPackageHandler(); var ms = eph.EncryptPackage(file, Encryption); - + fi.Write(ms.ToArray(), 0, (int)ms.Length); } else - { + { fi.Write(((MemoryStream)Stream).ToArray(), 0, (int)Stream.Length); } fi.Close(); @@ -919,7 +921,7 @@ public void Save() /// /// This parameter overrides the Workbook.Encryption.Password. public void Save(string password) - { + { Encryption.Password = password; Save(); } @@ -961,6 +963,7 @@ public void SaveAs(Stream OutputStream) CopyStream(_stream, ref OutputStream); } } + /// /// Copies the Package to the Outstream /// The package is closed after it has been saved @@ -1014,12 +1017,12 @@ public Stream Stream return _stream; } } -#endregion + #endregion /// /// Compression option for the package /// - public CompressionLevel Compression - { + public CompressionLevel Compression + { get { return Package.Compression; @@ -1037,9 +1040,9 @@ public CompatibilitySettings Compatibility { get { - if(_compatibility==null) + if (_compatibility == null) { - _compatibility=new CompatibilitySettings(this); + _compatibility = new CompatibilitySettings(this); } return _compatibility; } @@ -1051,13 +1054,13 @@ public CompatibilitySettings Compatibility /// The Uri to the part /// The XmlDocument internal XmlDocument GetXmlFromUri(Uri uri) - { - XmlDocument xml = new XmlDocument(); - Packaging.ZipPackagePart part = _package.GetPart(uri); - XmlHelper.LoadXmlSafe(xml, part.GetStream()); - return (xml); - } -#endregion + { + XmlDocument xml = new XmlDocument(); + Packaging.ZipPackagePart part = _package.GetPart(uri); + XmlHelper.LoadXmlSafe(xml, part.GetStream()); + return (xml); + } + #endregion /// /// Saves and returns the Excel files as a bytearray. @@ -1071,13 +1074,13 @@ internal XmlDocument GetXmlFromUri(Uri uri) /// Byte[] bin = package.GetAsByteArray(); /// Response.ContentType = "Application/vnd.ms-Excel"; /// Response.AddHeader("content-disposition", "attachment; filename=TheFile.xlsx"); - /// Response.BinaryWrite(bin); + /// Response.BinaryWrite(bin); /// /// /// public byte[] GetAsByteArray() { - return GetAsByteArray(true); + return GetAsByteArray(true); } /// /// Saves and returns the Excel files as a bytearray @@ -1114,14 +1117,14 @@ internal byte[] GetAsByteArray(bool save) _package.Save(_stream); } Byte[] byRet = new byte[Stream.Length]; - long pos = Stream.Position; + long pos = Stream.Position; Stream.Seek(0, SeekOrigin.Begin); Stream.Read(byRet, 0, (int)Stream.Length); //Encrypt Workbook? if (Encryption.IsEncrypted) { - EncryptedPackageHandler eph=new EncryptedPackageHandler(); + EncryptedPackageHandler eph = new EncryptedPackageHandler(); var ms = eph.EncryptPackage(byRet, Encryption); byRet = ms.ToArray(); } @@ -1208,13 +1211,13 @@ private void Load(Stream input, Stream output, string Password) throw; } } - } + } //Clear the workbook so that it gets reinitialized next time this._workbook = null; } - static object _lock=new object(); + static object _lock = new object(); #if (Core) - internal int _worksheetAdd=0; + internal int _worksheetAdd = 0; #else internal int _worksheetAdd=1; #endif @@ -1238,19 +1241,457 @@ internal static void CopyStream(Stream inputStream, ref Stream outputStream) inputStream.Seek(0, SeekOrigin.Begin); } - const int bufferLength = 8096; - var buffer = new Byte[bufferLength]; - lock (_lock) + const int bufferLength = 8096; + var buffer = new Byte[bufferLength]; + lock (_lock) + { + int bytesRead = inputStream.Read(buffer, 0, bufferLength); + // write the required bytes + while (bytesRead > 0) { - int bytesRead = inputStream.Read(buffer, 0, bufferLength); - // write the required bytes - while (bytesRead > 0) + outputStream.Write(buffer, 0, bytesRead); + bytesRead = inputStream.Read(buffer, 0, bufferLength); + } + outputStream.Flush(); + } + } +#if (Core) + /// + /// Create a new file from a template + /// + /// An existing xlsx file to use as a template + /// The password to decrypt the package. + /// + private async Task CreateFromTemplateAsync(FileInfo template, string password, CancellationToken cancellationToken) + { + if (template != null) template.Refresh(); + if (template.Exists) + { + if (_stream == null) _stream = new MemoryStream(); + var ms = new MemoryStream(); + if (password != null) + { + Encryption.IsEncrypted = true; + Encryption.Password = password; + var encrHandler = new EncryptedPackageHandler(); + ms = encrHandler.DecryptPackage(template, Encryption); + encrHandler = null; + } + else + { + await WriteFileToStreamAsync(template.FullName, ms, cancellationToken); + } + try + { + //_package = Package.Open(_stream, FileMode.Open, FileAccess.ReadWrite); + _package = new Packaging.ZipPackage(ms); + } + catch (Exception ex) + { + if (password == null && CompoundDocument.IsCompoundDocument(ms)) + { + throw new Exception("Can not open the package. Package is an OLE compound document. If this is an encrypted package, please supply the password", ex); + } + else { - outputStream.Write(buffer, 0, bytesRead); - bytesRead = inputStream.Read(buffer, 0, bufferLength); + throw; } - outputStream.Flush(); + } } + else + throw new Exception("Passed invalid TemplatePath to Excel Template"); + //return newFile; } + private async Task ConstructNewFileAsync(string password, CancellationToken cancellationToken) + { + var ms = new MemoryStream(); + if (_stream == null) _stream = new MemoryStream(); + if (File != null) File.Refresh(); + if (File != null && File.Exists) + { + if (password != null) + { + var encrHandler = new EncryptedPackageHandler(); + Encryption.IsEncrypted = true; + Encryption.Password = password; + ms = encrHandler.DecryptPackage(File, Encryption); + encrHandler = null; + } + else + { + await WriteFileToStreamAsync(File.FullName, ms, cancellationToken); + } + try + { + //_package = Package.Open(_stream, FileMode.Open, FileAccess.ReadWrite); + _package = new Packaging.ZipPackage(ms); + } + catch (Exception ex) + { + if (password == null && CompoundDocument.IsCompoundDocument(File)) + { + throw new Exception("Can not open the package. Package is an OLE compound document. If this is an encrypted package, please supply the password", ex); + } + else + { + throw; + } + } + } + else + { + //_package = Package.Open(_stream, FileMode.Create, FileAccess.ReadWrite); + _package = new Packaging.ZipPackage(ms); + CreateBlankWb(); + } + } + private static async Task WriteFileToStreamAsync(string path, Stream stream, CancellationToken cancellationToken = default) + { + using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 8096, useAsync:true)) + { + var buffer = new byte[4096]; + int read; + while ((read = await fileStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) + { + await stream.WriteAsync(buffer, 0, read, cancellationToken); + } + } + } + /// + /// Saves all the components back into the package. + /// This method recursively calls the Save method on all sub-components. + /// We close the package after the save is done. + /// + public async Task SaveAsync(CancellationToken cancellationToken = default) + { + try + { + if (_stream is MemoryStream && _stream.Length > 0) + { + //Close any open memorystream and "renew" then. This can occure if the package is saved twice. + //The stream is left open on save to enable the user to read the stream-property. + //Non-memorystream streams will leave the closing to the user before saving a second time. + CloseStream(); + } + + Workbook.Save(); + if (File == null) + { + if (Encryption.IsEncrypted) + { + var ms = new MemoryStream(); + _package.Save(ms); + byte[] file = ms.ToArray(); + EncryptedPackageHandler eph = new EncryptedPackageHandler(); + var msEnc = eph.EncryptPackage(file, Encryption); + await CopyStreamAsync(msEnc, _stream, cancellationToken); + } + else + { + _package.Save(_stream); + } + await _stream.FlushAsync(cancellationToken); + _package.Close(); + } + else + { + if (System.IO.File.Exists(File.FullName)) + { + try + { + System.IO.File.Delete(File.FullName); + } + catch (Exception ex) + { + throw (new Exception(string.Format("Error overwriting file {0}", File.FullName), ex)); + } + } + + _package.Save(_stream); + _package.Close(); + if (Stream is MemoryStream) + { + var fi = new FileStream(File.FullName, FileMode.Create, FileAccess.Write, FileShare.None, 8096, useAsync:true); + //EncryptPackage + if (Encryption.IsEncrypted) + { + byte[] file = ((MemoryStream)Stream).ToArray(); + EncryptedPackageHandler eph = new EncryptedPackageHandler(); + var ms = eph.EncryptPackage(file, Encryption); + + await fi.WriteAsync(ms.ToArray(), 0, (int)ms.Length, cancellationToken); + } + else + { + await fi.WriteAsync(((MemoryStream)Stream).ToArray(), 0, (int)Stream.Length, cancellationToken); + } + fi.Close(); + fi.Dispose(); + } + else + { +#if (STANDARD20) + System.IO.File.WriteAllBytes(File.FullName, GetAsByteArray(false)); +#else + + await System.IO.File.WriteAllBytesAsync(File.FullName, GetAsByteArray(false), cancellationToken); +#endif + } + } + } + catch (Exception ex) + { + if (File == null) + { + throw; + } + else + { + throw (new InvalidOperationException(string.Format("Error saving file {0}", File.FullName), ex)); + } + } + } + /// + /// Saves all the components back into the package. + /// This method recursively calls the Save method on all sub-components. + /// The package is closed after it has ben saved + /// d to encrypt the workbook with. + /// + /// This parameter overrides the Workbook.Encryption.Password. + /// Cancellation token + public async Task SaveAsync(string password, CancellationToken cancellationToken = default) + { + Encryption.Password = password; + await SaveAsync(cancellationToken); + } + /// + /// Saves the workbook to a new file + /// The package is closed after it has been saved + /// + /// The file location + /// Cancellation token + public async Task SaveAsAsync(FileInfo file, CancellationToken cancellationToken = default) + { + File = file; + await SaveAsync(cancellationToken); + } + /// + /// Saves the workbook to a new file + /// The package is closed after it has been saved + /// + /// The file + /// The password to encrypt the workbook with. + /// This parameter overrides the Encryption.Password. + /// Cancellation token + public async Task SaveAsAsync(FileInfo file, string password, CancellationToken cancellationToken = default) + { + File = file; + Encryption.Password = password; + await SaveAsync(cancellationToken); + } + /// + /// Copies the Package to the Outstream + /// The package is closed after it has been saved + /// + /// The stream to copy the package to + /// Cancellation token + public async Task SaveAsAsync(Stream OutputStream, CancellationToken cancellationToken = default) + { + File = null; + await SaveAsync(cancellationToken); + + if (OutputStream != _stream) + { + await CopyStreamAsync(_stream, OutputStream, cancellationToken); + } + } + /// + /// Copies the Package to the Outstream + /// The package is closed after it has been saved + /// + /// The stream to copy the package to + /// The password to encrypt the workbook with. + /// This parameter overrides the Encryption.Password. + /// Cancellation token + public async Task SaveAsAsync(Stream OutputStream, string password, CancellationToken cancellationToken = default) + { + Encryption.Password = password; + await SaveAsAsync(OutputStream, cancellationToken); + } + /// + /// Copies the input stream to the output stream. + /// + /// The input stream. + /// The output stream. + /// the cancellation token + internal static async Task CopyStreamAsync(Stream inputStream, Stream outputStream, CancellationToken cancellationToken = default) + { + if (!inputStream.CanRead) + { + throw (new Exception("Can not read from inputstream")); + } + if (!outputStream.CanWrite) + { + throw (new Exception("Can not write to outputstream")); + } + if (inputStream.CanSeek) + { + inputStream.Seek(0, SeekOrigin.Begin); + } + + const int bufferLength = 8096; + await inputStream.CopyToAsync(outputStream, 81920, cancellationToken); + } + + /// + /// Saves and returns the Excel files as a bytearray. + /// Note that the package is closed upon save + /// + /// + /// Example how to return a document from a Webserver... + /// + /// ExcelPackage package=new ExcelPackage(); + /// /**** ... Create the document ****/ + /// Byte[] bin = package.GetAsByteArray(); + /// Response.ContentType = "Application/vnd.ms-Excel"; + /// Response.AddHeader("content-disposition", "attachment; filename=TheFile.xlsx"); + /// Response.BinaryWrite(bin); + /// + /// + /// + public Task GetAsByteArrayAsync(CancellationToken cancellationToken = default) + { + return GetAsByteArrayAsync(true, cancellationToken); + } + /// + /// Saves and returns the Excel files as a bytearray + /// Note that the package is closed upon save + /// + /// + /// Example how to return a document from a Webserver... + /// + /// ExcelPackage package=new ExcelPackage(); + /// /**** ... Create the document ****/ + /// Byte[] bin = package.GetAsByteArray(); + /// Response.ContentType = "Application/vnd.ms-Excel"; + /// Response.AddHeader("content-disposition", "attachment; filename=TheFile.xlsx"); + /// Response.BinaryWrite(bin); + /// + /// + /// The password to encrypt the workbook with. + /// This parameter overrides the Encryption.Password. + /// + public async Task GetAsByteArrayAsync(string password, CancellationToken cancellationToken = default) + { + if (password != null) + { + Encryption.Password = password; + } + return await GetAsByteArrayAsync(true, cancellationToken); + } + internal async Task GetAsByteArrayAsync(bool save, CancellationToken cancellationToken) + { + if (save) + { + Workbook.Save(); + _package.Close(); + _package.Save(_stream); + } + Byte[] byRet = new byte[Stream.Length]; + long pos = Stream.Position; + Stream.Seek(0, SeekOrigin.Begin); + await Stream.ReadAsync(byRet, 0, (int)Stream.Length); + + //Encrypt Workbook? + if (Encryption.IsEncrypted) + { + EncryptedPackageHandler eph = new EncryptedPackageHandler(); + var ms = eph.EncryptPackage(byRet, Encryption); + byRet = ms.ToArray(); + } + + Stream.Seek(pos, SeekOrigin.Begin); + Stream.Close(); + return byRet; + } + + /// + /// Loads the specified package data from a stream. + /// + /// The input. + public Task LoadAsync(Stream input, CancellationToken cancellationToken = default) + { + return LoadAsync(input, new MemoryStream(), null, cancellationToken); + } + /// + /// Loads the specified package data from a stream. + /// + /// The input. + /// The password to decrypt the document + public Task LoadAsync(Stream input, string Password, CancellationToken cancellationToken = default) + { + return LoadAsync(input, new MemoryStream(), Password, cancellationToken); + } + + private async Task LoadAsync(Stream input, Stream output, string Password, CancellationToken cancellationToken) + { + //Release some resources: + if (this._package != null) + { + this._package.Close(); + this._package = null; + } + if (this._stream != null) + { + this._stream.Close(); + this._stream.Dispose(); + this._stream = null; + } + _isExternalStream = true; + if (input.Length == 0) // Template is blank, Construct new + { + _stream = output; + ConstructNewFile(Password); + } + else + { + Stream ms; + this._stream = output; + if (Password != null) + { + Stream encrStream = new MemoryStream(); + await CopyStreamAsync(input, encrStream, cancellationToken); + EncryptedPackageHandler eph = new EncryptedPackageHandler(); + Encryption.Password = Password; + ms = eph.DecryptPackage((MemoryStream)encrStream, Encryption); + } + else + { + ms = new MemoryStream(); + await CopyStreamAsync(input, ms, cancellationToken); + } + + try + { + //this._package = Package.Open(this._stream, FileMode.Open, FileAccess.ReadWrite); + _package = new Packaging.ZipPackage(ms); + } + catch (Exception ex) + { + EncryptedPackageHandler eph = new EncryptedPackageHandler(); + if (Password == null && CompoundDocument.IsCompoundDocument((MemoryStream)_stream)) + { + throw new Exception("Can not open the package. Package is an OLE compound document. If this is an encrypted package, please supply the password", ex); + } + else + { + throw; + } + } + } + //Clear the workbook so that it gets reinitialized next time + this._workbook = null; + } +#endif } } \ No newline at end of file diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs index ac1ff800..b2601013 100644 --- a/EPPlus/ExcelWorksheet.cs +++ b/EPPlus/ExcelWorksheet.cs @@ -371,6 +371,7 @@ internal void Clear(ExcelAddressBase Destination) private XmlDocument _worksheetXml; internal ExcelWorksheetView _sheetView; internal ExcelHeaderFooter _headerFooter; + private ExcelIgnoredError _ignoredError; #endregion #region ExcelWorksheet Constructor /// @@ -406,6 +407,7 @@ public ExcelWorksheet(XmlNamespaceManager ns, ExcelPackage excelPackage, string _flags = new FlagCellStore(); _commentsStore = new CellStore(); _hyperLinks = new CellStore(); + _mergedCells = new MergeCellsCollection(); _names = new ExcelNamedRangeCollection(Workbook, this); @@ -799,6 +801,40 @@ public VBA.ExcelVBAModule CodeModule } } } + + /// + /// Returns a IgnoredError object that allows you to ignore excel cell warning errors of the worksheet + /// + public ExcelIgnoredError IgnoredError + { + get + { + if (_ignoredError == null) + { + // Check that ignoredErrors exists + XmlNode node = TopNode.SelectSingleNode("d:ignoredErrors", NameSpaceManager); + + if (node == null) + { + CreateNode("d:ignoredErrors"); + } + + //Check that ignoredError exists + node = TopNode.SelectSingleNode("d:ignoredErrors/d:ignoredError", NameSpaceManager); + + if (node == null) + { + CreateNode("d:ignoredErrors/d:ignoredError"); + node = TopNode.SelectSingleNode("d:ignoredErrors/d:ignoredError", NameSpaceManager); + } + + _ignoredError = new ExcelIgnoredError(NameSpaceManager, node, this); + } + + return (_ignoredError); + } + } + #region WorksheetXml /// /// The XML document holding the worksheet data. @@ -900,7 +936,7 @@ private void CreateXml() stream.Dispose(); packPart.Stream = new MemoryStream(); - //first char is invalid sometimes?? + //first char is invalid sometimes?? Probably Byte Order Mark (BOM) if (xml[0] != '<') LoadXmlSafe(_worksheetXml, xml.Substring(1, xml.Length - 1), encoding); else @@ -1024,7 +1060,7 @@ private string GetWorkSheetXml(Stream stream, long start, long end, out Encoding length += size; } while (length < start + 20 && length < end); //the start-pos contains the stream position of the sheetData element. Add 20 (with some safty for whitespace, streampointer diff etc, just so be sure). - startmMatch = Regex.Match(sb.ToString(), string.Format("(<[^>]*{0}[^>]*>)", "sheetData")); + startmMatch = Regex.Match(sb.ToString(), string.Format("<(?:([^:]+):)?[^>]*{0}[^>]*>", "sheetData")); // (?:xxx) means non capturing group, i.e. use parentheses for logical formatting, but do not add to found match groups if (!startmMatch.Success) //Not found { encoding = sr.CurrentEncoding; @@ -1072,6 +1108,15 @@ private string GetWorkSheetXml(Stream stream, long start, long end, out Encoding } encoding = sr.CurrentEncoding; + if (startmMatch.Groups[1].Success) // Default namespace (http://schemas.openxmlformats.org/spreadsheetml/2006/main) for worksheet has prefix. Just remove the prefix. + { + if (Regex.Matches(xml, ".<[^\\s/>:]+[\\s/>]").Count > 1) // Make sure we do not assimilate other tags without prefixes in the namespace. The only found tags should be the above inserted "". The leading dot assures, we do not match the " Utils.ConvertUtil._invariantCompareInfo.IsSuffix(xr.LocalName, tag))) + { + if (xr.Depth == desiredDepth) + { + xr.Read(); + if (xr.EOF) return false; + } + else + { + while (xr.Depth != desiredDepth) + { + xr.Read(); + if (xr.EOF) return false; + } + } + } + return (Utils.ConvertUtil._invariantCompareInfo.IsSuffix(xr.LocalName, tagName[0])); + } private void LoadColumns(XmlReader xr)//(string xml) { var colList = new List(); @@ -1427,7 +1493,7 @@ private bool DoAddRow(XmlReader xr) /// private void LoadMergeCells(XmlReader xr) { - if (ReadUntil(xr, "mergeCells", "hyperlinks", "rowBreaks", "colBreaks") && !xr.EOF) + if (ReadUntil(xr, 1, "mergeCells", "hyperlinks", "rowBreaks", "colBreaks") && !xr.EOF) { while (xr.Read()) { diff --git a/EPPlus/FormulaParsing/Excel/Functions/BuiltInFunctions.cs b/EPPlus/FormulaParsing/Excel/Functions/BuiltInFunctions.cs index 2b4772c9..0a891609 100644 --- a/EPPlus/FormulaParsing/Excel/Functions/BuiltInFunctions.cs +++ b/EPPlus/FormulaParsing/Excel/Functions/BuiltInFunctions.cs @@ -21,20 +21,17 @@ * Author Change Date ******************************************************************************* * Mats Alm Added 2013-12-03 + * César Morgan Fixing of namespaces 2020-01-14 *******************************************************************************/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using OfficeOpenXml.FormulaParsing.Excel.Functions.Database; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Math; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime; +using OfficeOpenXml.FormulaParsing.Excel.Functions.Finance; +using OfficeOpenXml.FormulaParsing.Excel.Functions.Information; +using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; +using OfficeOpenXml.FormulaParsing.Excel.Functions.Math; using OfficeOpenXml.FormulaParsing.Excel.Functions.Numeric; using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Information; -using OfficeOpenXml.FormulaParsing.Excel.Functions.Finance; +using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; namespace OfficeOpenXml.FormulaParsing.Excel.Functions { diff --git a/EPPlus/FormulaParsing/ExcelUtilities/ValueMatcher.cs b/EPPlus/FormulaParsing/ExcelUtilities/ValueMatcher.cs index c4a5e40a..634177cc 100644 --- a/EPPlus/FormulaParsing/ExcelUtilities/ValueMatcher.cs +++ b/EPPlus/FormulaParsing/ExcelUtilities/ValueMatcher.cs @@ -29,9 +29,6 @@ * Mats Alm Added 2013-03-01 (Prior file history on https://github.com/swmal/ExcelFormulaParser) *******************************************************************************/ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace OfficeOpenXml.FormulaParsing.ExcelUtilities { @@ -52,7 +49,7 @@ public virtual int IsMatch(object o1, object o2) { return CompareStringToString(o1.ToString().ToLower(), o2.ToString().ToLower()); } - else if( o1.GetType() == typeof(string)) + else if (o1.GetType() == typeof(string)) { return CompareStringToObject(o1.ToString(), o2); } @@ -60,6 +57,10 @@ public virtual int IsMatch(object o1, object o2) { return CompareObjectToString(o1, o2.ToString()); } + else if (o1.GetType() == typeof(DateTime)) + { + return CompareDatetimeToDouble(o1, o2.ToString()); + } return Convert.ToDouble(o1).CompareTo(Convert.ToDouble(o2)); } @@ -116,5 +117,15 @@ protected virtual int CompareObjectToString(object o1, string o2) } return IncompatibleOperands; } + + protected virtual int CompareDatetimeToDouble(object o1, string o2) + { + double dt1; + if (double.TryParse(o2, out dt1)) + { + return DateTime.FromOADate(double.Parse(o2)).CompareTo(o1); + } + return IncompatibleOperands; + } } } diff --git a/EPPlus/Style/XmlAccess/ExcelBorderItemXml.cs b/EPPlus/Style/XmlAccess/ExcelBorderItemXml.cs index 7bc4acea..b5762e5d 100644 --- a/EPPlus/Style/XmlAccess/ExcelBorderItemXml.cs +++ b/EPPlus/Style/XmlAccess/ExcelBorderItemXml.cs @@ -127,7 +127,10 @@ internal ExcelBorderItemXml Copy() { ExcelBorderItemXml borderItem = new ExcelBorderItemXml(NameSpaceManager); borderItem.Style = _borderStyle; - borderItem.Color = _color.Copy(); + if (_color != null) + { + borderItem.Color = _color.Copy(); + } return borderItem; } diff --git a/EPPlusTest/FormulaParsing/ExcelUtilities/ValueMatcherTests.cs b/EPPlusTest/FormulaParsing/ExcelUtilities/ValueMatcherTests.cs index 3c67dd06..cdaf38af 100644 --- a/EPPlusTest/FormulaParsing/ExcelUtilities/ValueMatcherTests.cs +++ b/EPPlusTest/FormulaParsing/ExcelUtilities/ValueMatcherTests.cs @@ -103,5 +103,14 @@ public void ShouldReturn0WhenParamsAreEqualButDifferentTypes() var result = _matcher.IsMatch(o1, o2); Assert.AreEqual(-2, result); } + + [TestMethod] + public void ShouldReturnMinus1WhenTypesAreDateTimeAndDouble() + { + object o1 = Convert.ToDateTime("18/01/2024"); + object o2 = 41711; + var result = _matcher.IsMatch(o1, o2); + Assert.AreEqual(-1, result); + } } } diff --git a/EPPlusTest/WorkSheetTests.cs b/EPPlusTest/WorkSheetTests.cs index 2a636656..30df1f28 100644 --- a/EPPlusTest/WorkSheetTests.cs +++ b/EPPlusTest/WorkSheetTests.cs @@ -15,6 +15,7 @@ using OfficeOpenXml.Table; using System.Threading; using System.Globalization; +using System.Threading.Tasks; namespace EPPlusTest { @@ -2931,6 +2932,42 @@ public void Comment() pck.Save(); } + [TestMethod] + public async Task SaveAsync() + { + InitBase(); + var pck = new ExcelPackage(); + var ws1 = pck.Workbook.Worksheets.Add("Comment1"); + ws1.Cells[1, 1].AddComment("Testing", "test1"); + + await pck.SaveAsAsync(new FileInfo(_worksheetPath + "comment-async.xlsx")); + + pck = new ExcelPackage(new FileInfo(_worksheetPath + "comment-async.xlsx")); + pck.Compatibility.IsWorksheets1Based = true; + var ws2 = pck.Workbook.Worksheets[1]; + ws2.Cells[1, 2].AddComment("Testing", "test1"); + await pck.SaveAsync(); + + } + + [TestMethod] + public async Task LoadAsync() + { + InitBase(); + var pck = new ExcelPackage(); + var ws1 = pck.Workbook.Worksheets.Add("Comment1"); + ws1.Cells[1, 1].AddComment("Testing", "test1"); + var stream = new MemoryStream(); + await pck.SaveAsAsync(stream); + pck = new ExcelPackage(); + await pck.LoadAsync(stream); + pck.Compatibility.IsWorksheets1Based = true; + var ws2 = pck.Workbook.Worksheets[1]; + ws2.Cells[1, 2].AddComment("Testing", "test1"); + await pck.SaveAsync(); + + } + [TestMethod] public void CommentShiftsWithRowInserts() { diff --git a/SampleApp/EPPlusSamples.csproj b/SampleApp/EPPlusSamples.csproj index 7b00fc13..4a2174a4 100644 --- a/SampleApp/EPPlusSamples.csproj +++ b/SampleApp/EPPlusSamples.csproj @@ -88,6 +88,7 @@ + diff --git a/SampleApp/Sample_Main.cs b/SampleApp/Sample_Main.cs index f7d34dde..e8a8b642 100644 --- a/SampleApp/Sample_Main.cs +++ b/SampleApp/Sample_Main.cs @@ -170,6 +170,12 @@ static void Main(string[] args) Console.WriteLine("Running Sample_AddFormulaFunction"); Sample_AddFormulaFunction.RunSample_AddFormulaFunction(); Console.WriteLine(); + + //Sample ManageWarningErrors - Shows how to enable/disable cells warning errors, like "Number Stored as Text" or "Two Digit Text Year". + Console.WriteLine("Running Sample_ManageWarningErrors"); + string sampleManageWarningErrorsPath = Sample_ManageWarningErrors.RunSample_ManageWarningErrors(); + Console.WriteLine("Sample ManageWarningErrors created: {0}", sampleManageWarningErrorsPath); + Console.WriteLine(); } catch (Exception ex) { diff --git a/SampleApp/Sample_ManageWarningErrors.cs b/SampleApp/Sample_ManageWarningErrors.cs new file mode 100644 index 00000000..2145522b --- /dev/null +++ b/SampleApp/Sample_ManageWarningErrors.cs @@ -0,0 +1,110 @@ +/******************************************************************************* + * You may amend and distribute as you like, but don't remove this header! + * + * All rights reserved. + * + * EPPlus is an Open Source project provided under the + * GNU General Public License (GPL) as published by the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * EPPlus provides server-side generation of Excel 2007 spreadsheets. + * See https://github.com/JanKallman/EPPlus for details. + * + * + * + * The GNU General Public License can be viewed at http://www.opensource.org/licenses/gpl-license.php + * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html + * + * The code for this project may be used and redistributed by any means PROVIDING it is + * not sold for profit without the author's written consent, and providing that this notice + * and the author's name and all copyright notices remain intact. + * + * 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 + ******************************************************************************* + * Jan Källman Added 21 Mar 2010 + *******************************************************************************/ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using OfficeOpenXml; +using System.Xml; +using System.Drawing; +using OfficeOpenXml.Style; +namespace EPPlusSamples +{ + class Sample_ManageWarningErrors + { + /// + /// Sample ManageWarningErrors - Shows how to enable/disable cells warning errors, like "Number Stored as Text" or "Two Digit Text Year". + /// Useful when you want to display "numbers" like excel detects as text (e.g. numbers leading by zeros) + /// The workbook contains worksheets with / without warnings + /// + public static string RunSample_ManageWarningErrors() + { + using (var package = new ExcelPackage()) + { + // Add a new worksheet to the empty workbook + ExcelWorksheet worksheet = CreateSampleBaseWorkSheet(package, "Warnings-ON"); + + ExcelWorksheet worksheet2 = CreateSampleBaseWorkSheet(package, "Warnings-OFF"); + // Set the cell range to ignore errors on to the entire worksheet + worksheet2.IgnoredError.Range = worksheet2.Dimension.Address; + + // Do not display the warning 'number stored as text' + worksheet2.IgnoredError.NumberStoredAsText = true; + + ExcelWorksheet worksheet3 = CreateSampleBaseWorkSheet(package, "Warnings-Selective"); + // Set the cell range to ignore errors only on to column B + worksheet3.IgnoredError.Range = "B:B"; + + // Do not display the warning 'number stored as text' + worksheet3.IgnoredError.NumberStoredAsText = true; + + // set some document properties + package.Workbook.Properties.Title = "Check Warnings Sample"; + package.Workbook.Properties.Author = "romcode"; + package.Workbook.Properties.Comments = "This sample demonstrates how to use the managing errors feature using EPPlus"; + + var xlFile = Utils.GetFileInfo("sample_warning_errors.xlsx"); + // save our new workbook in the output directory and we are done! + package.SaveAs(xlFile); + return xlFile.FullName; + } + } + + private static ExcelWorksheet CreateSampleBaseWorkSheet(ExcelPackage package,string name) + { + // Create the worksheet + Console.WriteLine("Creating worksheet {0}", name); + + ExcelWorksheet worksheet = package.Workbook.Worksheets.Add(name); + + // Add the headers + worksheet.Cells[1, 1].Value = "ID"; + worksheet.Cells[1, 2].Value = "Tag Code"; + worksheet.Cells[1, 3].Value = "Product"; + + //Add some items... + worksheet.Cells["A2"].Value = "01"; + worksheet.Cells["B2"].Value = "00001"; + worksheet.Cells["C2"].Value = "Nails"; + + worksheet.Cells["A3"].Value = "02"; + worksheet.Cells["B3"].Value = "00002"; + worksheet.Cells["C3"].Value = "Books"; + + worksheet.Cells["A4"].Value = "03"; + worksheet.Cells["B4"].Value = "00003"; + worksheet.Cells["C4"].Value = "Fruits"; + + return worksheet; + } + } +}