diff --git a/ClosedXML.sln b/ClosedXML.sln index b58a074..cd1cd87 100644 --- a/ClosedXML.sln +++ b/ClosedXML.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.3 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClosedXML_Sandbox", "ClosedXML_Sandbox\ClosedXML_Sandbox.csproj", "{38B882F0-E6F2-45C5-9BE9-CDC27FBEB4AB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClosedXML_Sandbox", "ClosedXML_Sandbox\ClosedXML_Sandbox.csproj", "{38B882F0-E6F2-45C5-9BE9-CDC27FBEB4AB}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5C94E22C-85AA-48FD-B082-CF929FFC6C31}" ProjectSection(SolutionItems) = preProject @@ -11,11 +11,11 @@ ClosedXML.vsmdi = ClosedXML.vsmdi EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClosedXML_Examples", "ClosedXML_Examples\ClosedXML_Examples.csproj", "{03A518D0-1CB7-488E-861C-C4E782B27A46}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClosedXML_Examples", "ClosedXML_Examples\ClosedXML_Examples.csproj", "{03A518D0-1CB7-488E-861C-C4E782B27A46}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClosedXML", "ClosedXML\ClosedXML.csproj", "{BD5E6BFE-E837-4A35-BCA9-39667D873A20}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClosedXML", "ClosedXML\ClosedXML.csproj", "{BD5E6BFE-E837-4A35-BCA9-39667D873A20}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClosedXML_Tests", "ClosedXML_Tests\ClosedXML_Tests.csproj", "{09B066ED-E4A7-4545-A1A4-FF03DD524BDF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClosedXML_Tests", "ClosedXML_Tests\ClosedXML_Tests.csproj", "{09B066ED-E4A7-4545-A1A4-FF03DD524BDF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{073CFB1C-43DC-4ADC-9D12-BB8D7B10C099}" ProjectSection(SolutionItems) = preProject @@ -49,6 +49,9 @@ GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2E35B7D8-9690-408F-B52A-F4FC485A6B09} + EndGlobalSection GlobalSection(TestCaseManagementSettings) = postSolution CategoryFile = ClosedXML.vsmdi EndGlobalSection diff --git a/ClosedXML/Attributes/XLColumnAttribute.cs b/ClosedXML/Attributes/XLColumnAttribute.cs index 526dcb0..67ab26f 100644 --- a/ClosedXML/Attributes/XLColumnAttribute.cs +++ b/ClosedXML/Attributes/XLColumnAttribute.cs @@ -1,8 +1,7 @@ +using ClosedXML.Excel; using System; using System.Linq; using System.Reflection; -using ClosedXML; -using ClosedXML.Excel; namespace ClosedXML.Attributes { diff --git a/ClosedXML/ClosedXML.csproj b/ClosedXML/ClosedXML.csproj index a7bc0e1..8ec60a4 100644 --- a/ClosedXML/ClosedXML.csproj +++ b/ClosedXML/ClosedXML.csproj @@ -1,414 +1,53 @@ - - + + - Debug - AnyCPU - 8.0.30703 - 2.0 - {BD5E6BFE-E837-4A35-BCA9-39667D873A20} - Library - Properties - ClosedXML - ClosedXML - v4.0 - 512 - ..\ - true - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - 1591 - false - 6 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\ClosedXML.xml - 1591 - false - 6 - - + netstandard2.0;net40;net46 + 0.93.0 + Manuel de Leon, Amir Ghezelbash, Francois Botha + + + ClosedXML makes it easier for developers to create Excel 2007+ (.xlsx, .xlsm, etc) files. It provides a nice object oriented way to manipulate the files (similar to VBA) without dealing with the hassles of XML Documents. It can be used by any .NET language like C# and VisualBasic.NET. + MIT + https://github.com/ClosedXML/ClosedXML/blob/master/LICENSE + https://github.com/ClosedXML/ClosedXML + https://github.com/ClosedXML/ClosedXML true - - ClosedXML.snk + $(NoWarn);NU1605;CS1591 + true - - - ..\packages\DocumentFormat.OpenXml.2.7.2\lib\net40\DocumentFormat.OpenXml.dll - True - - - ..\packages\ExcelNumberFormat.1.0.3\lib\net20\ExcelNumberFormat.dll - True - - - ..\packages\FastMember.Signed.1.1.0\lib\net40\FastMember.Signed.dll - True - + + + $(DefineConstants);_NETSTANDARD_;_NETSTANDARD2_0_ + + + + $(DefineConstants);_NETFRAMEWORK_;_NET40_ + + + + $(DefineConstants);_NETFRAMEWORK_;_NET46_ + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - .editorconfig - - - - Designer - + - - - \ No newline at end of file + + + + + + + diff --git a/ClosedXML/Excel/Cells/XLCell.cs b/ClosedXML/Excel/Cells/XLCell.cs index 8a4a708..45bddd5 100644 --- a/ClosedXML/Excel/Cells/XLCell.cs +++ b/ClosedXML/Excel/Cells/XLCell.cs @@ -1579,7 +1579,6 @@ get { yield break; } } - public override IXLRanges RangesUsed { get @@ -1694,6 +1693,7 @@ } #region Styles + private XLStyleValue GetStyleForRead() { return StyleValue; diff --git a/ClosedXML/Excel/Columns/XLColumn.cs b/ClosedXML/Excel/Columns/XLColumn.cs index 9521d1d..607f181 100644 --- a/ClosedXML/Excel/Columns/XLColumn.cs +++ b/ClosedXML/Excel/Columns/XLColumn.cs @@ -254,6 +254,7 @@ public IXLColumn AdjustToContents(Int32 startRow, Int32 endRow, Double minWidth, Double maxWidth) { var fontCache = new Dictionary(); + Double colMaxWidth = minWidth; List autoFilterRows = new List(); diff --git a/ClosedXML/Excel/ConditionalFormats/Save/XLCFColorScaleConverter.cs b/ClosedXML/Excel/ConditionalFormats/Save/XLCFColorScaleConverter.cs index 5fc548c..bddd195 100644 --- a/ClosedXML/Excel/ConditionalFormats/Save/XLCFColorScaleConverter.cs +++ b/ClosedXML/Excel/ConditionalFormats/Save/XLCFColorScaleConverter.cs @@ -1,5 +1,5 @@ -using System; using DocumentFormat.OpenXml.Spreadsheet; +using System; namespace ClosedXML.Excel { diff --git a/ClosedXML/Excel/ConditionalFormats/Save/XLCFDataBarConverter.cs b/ClosedXML/Excel/ConditionalFormats/Save/XLCFDataBarConverter.cs index 29300fa..67b19a0 100644 --- a/ClosedXML/Excel/ConditionalFormats/Save/XLCFDataBarConverter.cs +++ b/ClosedXML/Excel/ConditionalFormats/Save/XLCFDataBarConverter.cs @@ -1,7 +1,7 @@ -using System; -using System.Linq; using ClosedXML.Extensions; using DocumentFormat.OpenXml.Spreadsheet; +using System; +using System.Linq; namespace ClosedXML.Excel { @@ -25,9 +25,11 @@ case XLColorType.Color: color.Rgb = cf.Colors[1].Color.ToHex(); break; + case XLColorType.Theme: color.Theme = System.Convert.ToUInt32(cf.Colors[1].ThemeColor); break; + case XLColorType.Indexed: color.Indexed = System.Convert.ToUInt32(cf.Colors[1].Indexed); break; @@ -37,7 +39,6 @@ dataBar.Append(conditionalFormatValueObject2); dataBar.Append(color); - conditionalFormattingRule.Append(dataBar); if (cf.Colors.Count > 1) diff --git a/ClosedXML/Excel/Drawings/PictureEnums.cs b/ClosedXML/Excel/Drawings/PictureEnums.cs index 3d76e73..f00f85b 100644 --- a/ClosedXML/Excel/Drawings/PictureEnums.cs +++ b/ClosedXML/Excel/Drawings/PictureEnums.cs @@ -14,9 +14,9 @@ Tiff = 3, Icon = 4, Pcx = 5, - Jpeg = 6, - Emf = 7, - Wmf = 8 + Jpeg = 6 + //Emf = 7, + //Wmf = 8 } public enum XLPicturePlacement diff --git a/ClosedXML/Excel/Drawings/XLPictures.cs b/ClosedXML/Excel/Drawings/XLPictures.cs index c539d0e..705ccb1 100644 --- a/ClosedXML/Excel/Drawings/XLPictures.cs +++ b/ClosedXML/Excel/Drawings/XLPictures.cs @@ -73,9 +73,9 @@ public IXLPicture Add(string imageFile) { - using (var bitmap = Image.FromFile(imageFile) as Bitmap) + using (var fs = File.Open(imageFile, FileMode.Open)) { - var picture = new XLPicture(_worksheet, bitmap); + var picture = new XLPicture(_worksheet, fs); _pictures.Add(picture); picture.Name = GetNextPictureName(); return picture; diff --git a/ClosedXML/Excel/IXLTheme.cs b/ClosedXML/Excel/IXLTheme.cs index 17e4e57..b506e22 100644 --- a/ClosedXML/Excel/IXLTheme.cs +++ b/ClosedXML/Excel/IXLTheme.cs @@ -1,6 +1,3 @@ -using ClosedXML.Excel; -using System.Drawing; - namespace ClosedXML.Excel { public interface IXLTheme diff --git a/ClosedXML/Excel/IXLWorksheets.cs b/ClosedXML/Excel/IXLWorksheets.cs index f811507..180c304 100644 --- a/ClosedXML/Excel/IXLWorksheets.cs +++ b/ClosedXML/Excel/IXLWorksheets.cs @@ -4,21 +4,28 @@ namespace ClosedXML.Excel { - public interface IXLWorksheets: IEnumerable + public interface IXLWorksheets : IEnumerable { int Count { get; } - bool TryGetWorksheet(string sheetName,out IXLWorksheet worksheet); + + bool TryGetWorksheet(string sheetName, out IXLWorksheet worksheet); IXLWorksheet Worksheet(String sheetName); - IXLWorksheet Worksheet(Int32 position); - IXLWorksheet Add(String sheetName); - IXLWorksheet Add(String sheetName, Int32 position); - IXLWorksheet Add(DataTable dataTable); - IXLWorksheet Add(DataTable dataTable, String sheetName); - void Add(DataSet dataSet); - void Delete(String sheetName); - void Delete(Int32 position); - + IXLWorksheet Worksheet(Int32 position); + + IXLWorksheet Add(String sheetName); + + IXLWorksheet Add(String sheetName, Int32 position); + + IXLWorksheet Add(DataTable dataTable); + + IXLWorksheet Add(DataTable dataTable, String sheetName); + + void Add(DataSet dataSet); + + void Delete(String sheetName); + + void Delete(Int32 position); } } diff --git a/ClosedXML/Excel/PageSetup/XLHFText.cs b/ClosedXML/Excel/PageSetup/XLHFText.cs index f9530a3..6659cd6 100644 --- a/ClosedXML/Excel/PageSetup/XLHFText.cs +++ b/ClosedXML/Excel/PageSetup/XLHFText.cs @@ -6,11 +6,13 @@ internal class XLHFText { private readonly XLHFItem _hfItem; + public XLHFText(XLRichString richText, XLHFItem hfItem) { RichText = richText; _hfItem = hfItem; } + public XLRichString RichText { get; private set; } public String GetHFText(String prevText) @@ -98,6 +100,5 @@ return sb.ToString(); } - } } diff --git a/ClosedXML/Excel/Ranges/IXLRanges.cs b/ClosedXML/Excel/Ranges/IXLRanges.cs index cc6cdab..815f6f0 100644 --- a/ClosedXML/Excel/Ranges/IXLRanges.cs +++ b/ClosedXML/Excel/Ranges/IXLRanges.cs @@ -19,6 +19,15 @@ /// The range to remove from this group. void Remove(IXLRange range); + /// + /// Removes ranges matching the criteria from the collection, optionally releasing their event handlers. + /// + /// Criteria to filter ranges. Only those ranges that satisfy the criteria will be removed. + /// Null means the entire collection should be cleared. + /// Specify whether or not should removed ranges be unsubscribed from + /// row/column shifting events. Until ranges are unsubscribed they cannot be collected by GC. + void RemoveAll(Predicate match = null, bool releaseEventHandlers = true); + Int32 Count { get; } Boolean Contains(IXLRange range); diff --git a/ClosedXML/Excel/Ranges/XLRanges.cs b/ClosedXML/Excel/Ranges/XLRanges.cs index 1acc4a7..a57186c 100644 --- a/ClosedXML/Excel/Ranges/XLRanges.cs +++ b/ClosedXML/Excel/Ranges/XLRanges.cs @@ -45,6 +45,27 @@ _ranges.RemoveAll(r => r.ToString() == range.ToString()); } + /// + /// Removes ranges matching the criteria from the collection, optionally releasing their event handlers. + /// + /// Criteria to filter ranges. Only those ranges that satisfy the criteria will be removed. + /// Null means the entire collection should be cleared. + /// Specify whether or not should removed ranges be unsubscribed from + /// row/column shifting events. Until ranges are unsubscribed they cannot be collected by GC. + public void RemoveAll(Predicate match = null, bool releaseEventHandlers = true) + { + match = match ?? (_ => true); + + if (releaseEventHandlers) + { + _ranges + .Where(r => match(r)) + .ForEach(r => r.Dispose()); + } + + Count -= _ranges.RemoveAll(match); + } + public int Count { get; private set; } public IEnumerator GetEnumerator() diff --git a/ClosedXML/Excel/Rows/XLRow.cs b/ClosedXML/Excel/Rows/XLRow.cs index 53e4008..9e263b7 100644 --- a/ClosedXML/Excel/Rows/XLRow.cs +++ b/ClosedXML/Excel/Rows/XLRow.cs @@ -71,7 +71,7 @@ yield return cell.Style; } } - + protected override IEnumerable Children { get @@ -280,6 +280,7 @@ public IXLRow AdjustToContents(Int32 startColumn, Int32 endColumn, Double minHeight, Double maxHeight) { var fontCache = new Dictionary(); + Double rowMaxHeight = minHeight; foreach (XLCell c in from XLCell c in Row(startColumn, endColumn).CellsUsed() where !c.IsMerged() select c) { diff --git a/ClosedXML/Excel/SaveOptions.cs b/ClosedXML/Excel/SaveOptions.cs index 7dcd9a1..62edb53 100644 --- a/ClosedXML/Excel/SaveOptions.cs +++ b/ClosedXML/Excel/SaveOptions.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace ClosedXML.Excel { @@ -19,6 +15,7 @@ this.EvaluateFormulasBeforeSaving = false; this.GenerateCalculationChain = true; } + public Boolean ValidatePackage; public Boolean EvaluateFormulasBeforeSaving; public Boolean GenerateCalculationChain; diff --git a/ClosedXML/Excel/Style/Colors/XLColor_Static.cs b/ClosedXML/Excel/Style/Colors/XLColor_Static.cs index 1f1f65f..c4628bc 100644 --- a/ClosedXML/Excel/Style/Colors/XLColor_Static.cs +++ b/ClosedXML/Excel/Style/Colors/XLColor_Static.cs @@ -42,10 +42,12 @@ return FromColor(Color.FromArgb(a, r, g, b)); } +#if _NETFRAMEWORK_ public static XLColor FromKnownColor(KnownColor color) { return FromColor(Color.FromKnownColor(color)); } +#endif public static XLColor FromName(String name) { diff --git a/ClosedXML/Excel/Tables/XLTable.cs b/ClosedXML/Excel/Tables/XLTable.cs index 97f5c5f..528eb6d 100644 --- a/ClosedXML/Excel/Tables/XLTable.cs +++ b/ClosedXML/Excel/Tables/XLTable.cs @@ -219,13 +219,39 @@ get { return _name; } set { - if (Worksheet.Tables.Any(t => t.Name == value)) - { - throw new ArgumentException(String.Format("This worksheet already contains a table named '{0}'", - value)); - } + if (_name == value) return; + + // Validation rules for table names + var oldname = _name ?? string.Empty; + + if (String.IsNullOrWhiteSpace(value)) + throw new ArgumentException($"The table name '{value}' is invalid"); + + // Table names are case insensitive + if (!oldname.Equals(value, StringComparison.OrdinalIgnoreCase) + && Worksheet.Tables.Any(t => t.Name.Equals(value, StringComparison.OrdinalIgnoreCase))) + throw new ArgumentException($"This worksheet already contains a table named '{value}'"); + + if (value[0] != '_' && !char.IsLetter(value[0])) + throw new ArgumentException($"The table name '{value}' does not begin with a letter or an underscore"); + + if (value.Length > 255) + throw new ArgumentException("The table name is more than 255 characters"); + + if (new[] { 'C', 'R' }.Any(c => value.ToUpper().Equals(c.ToString()))) + throw new ArgumentException($"The table name '{value}' is invalid"); _name = value; + + // Some totals row formula depend on the table name. Update them. + if (_fieldNames?.Any() ?? false) + this.Fields.ForEach(f => (f as XLTableField).UpdateTableFieldTotalsRowFormula()); + + if (!String.IsNullOrWhiteSpace(oldname)) + { + Worksheet.Tables.Add(this); + Worksheet.Tables.Remove(oldname); + } } } @@ -337,6 +363,8 @@ var existingHeaders = this.FieldNames.Keys; var newHeaders = new HashSet(); + + // Force evaluation of f.Column field var tempArray = this.Fields.Select(f => f.Column).ToArray(); var firstRow = range.Row(1); @@ -391,7 +419,7 @@ { foreach (var f in this._fieldNames.Values.Cast()) { - f.UpdateUnderlyingCellFormula(); + f.UpdateTableFieldTotalsRowFormula(); var c = this.TotalsRow().Cell(f.Index + 1); if (!String.IsNullOrWhiteSpace(f.TotalsRowLabel)) { diff --git a/ClosedXML/Excel/Tables/XLTableField.cs b/ClosedXML/Excel/Tables/XLTableField.cs index 893d3ff..dabb701 100644 --- a/ClosedXML/Excel/Tables/XLTableField.cs +++ b/ClosedXML/Excel/Tables/XLTableField.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -89,7 +90,7 @@ set { totalsRowFunction = value; - UpdateUnderlyingCellFormula(); + UpdateTableFieldTotalsRowFormula(); } } @@ -173,25 +174,37 @@ return distinctStyles.Count() == 1; } - internal void UpdateUnderlyingCellFormula() + private static IEnumerable QuotedTableFieldCharacters = new[] { "'", "#" }; + + internal void UpdateTableFieldTotalsRowFormula() { if (TotalsRowFunction != XLTotalsRowFunction.None && TotalsRowFunction != XLTotalsRowFunction.Custom) { var cell = table.TotalsRow().Cell(Index + 1); - String formula = String.Empty; + var formulaCode = String.Empty; switch (TotalsRowFunction) { - case XLTotalsRowFunction.Sum: formula = "109"; break; - case XLTotalsRowFunction.Minimum: formula = "105"; break; - case XLTotalsRowFunction.Maximum: formula = "104"; break; - case XLTotalsRowFunction.Average: formula = "101"; break; - case XLTotalsRowFunction.Count: formula = "103"; break; - case XLTotalsRowFunction.CountNumbers: formula = "102"; break; - case XLTotalsRowFunction.StandardDeviation: formula = "107"; break; - case XLTotalsRowFunction.Variance: formula = "110"; break; + case XLTotalsRowFunction.Sum: formulaCode = "109"; break; + case XLTotalsRowFunction.Minimum: formulaCode = "105"; break; + case XLTotalsRowFunction.Maximum: formulaCode = "104"; break; + case XLTotalsRowFunction.Average: formulaCode = "101"; break; + case XLTotalsRowFunction.Count: formulaCode = "103"; break; + case XLTotalsRowFunction.CountNumbers: formulaCode = "102"; break; + case XLTotalsRowFunction.StandardDeviation: formulaCode = "107"; break; + case XLTotalsRowFunction.Variance: formulaCode = "110"; break; } - cell.FormulaA1 = "SUBTOTAL(" + formula + ",[" + Name + "])"; + var modifiedName = Name; + QuotedTableFieldCharacters.ForEach(c => modifiedName = modifiedName.Replace(c, "'" + c)); + + if (modifiedName.StartsWith(" ") || modifiedName.EndsWith(" ")) + { + modifiedName = "[" + modifiedName + "]"; + } + + var prependTableName = modifiedName.Contains(" "); + + cell.FormulaA1 = $"SUBTOTAL({formulaCode},{(prependTableName ? table.Name : string.Empty)}[{modifiedName}])"; var lastCell = table.LastRow().Cell(Index + 1); if (lastCell.DataType != XLDataType.Text) { diff --git a/ClosedXML/Excel/XLWorkbook.cs b/ClosedXML/Excel/XLWorkbook.cs index 59059b1..f64c655 100644 --- a/ClosedXML/Excel/XLWorkbook.cs +++ b/ClosedXML/Excel/XLWorkbook.cs @@ -434,7 +434,6 @@ { CopyStream(_originalStream, fileStream); CreatePackage(fileStream, false, _spreadsheetDocumentType, options); - fileStream.Close(); } } @@ -527,7 +526,6 @@ using (var fileStream = new FileStream(_originalFile, FileMode.Open, FileAccess.Read)) { CopyStream(fileStream, stream); - fileStream.Close(); } CreatePackage(stream, false, _spreadsheetDocumentType, options); } diff --git a/ClosedXML/Excel/XLWorkbook_ImageHandling.cs b/ClosedXML/Excel/XLWorkbook_ImageHandling.cs index 9dbaf3e..8ea8d29 100644 --- a/ClosedXML/Excel/XLWorkbook_ImageHandling.cs +++ b/ClosedXML/Excel/XLWorkbook_ImageHandling.cs @@ -43,13 +43,13 @@ if (!IsAllowedAnchor(anchor)) return null; - var picture = anchor - .Descendants() - .FirstOrDefault(); + // Maybe we should not restrict here, and just search for all NonVisualDrawingProperties in an anchor? + var shape = anchor.Descendants().Cast().FirstOrDefault() + ?? anchor.Descendants().Cast().FirstOrDefault(); - if (picture == null) return null; + if (shape == null) return null; - return picture + return shape .Descendants() .FirstOrDefault(); } diff --git a/ClosedXML/Excel/XLWorkbook_Load.cs b/ClosedXML/Excel/XLWorkbook_Load.cs index 5e311d9..7ca1145 100644 --- a/ClosedXML/Excel/XLWorkbook_Load.cs +++ b/ClosedXML/Excel/XLWorkbook_Load.cs @@ -1,5 +1,3 @@ -#region - using ClosedXML.Extensions; using ClosedXML.Utils; using DocumentFormat.OpenXml; @@ -17,19 +15,13 @@ using Op = DocumentFormat.OpenXml.CustomProperties; using Xdr = DocumentFormat.OpenXml.Drawing.Spreadsheet; -#endregion - namespace ClosedXML.Excel { - #region - using Ap; using Drawings; using Op; using System.Drawing; - #endregion - public partial class XLWorkbook { private readonly Dictionary _colorList = new Dictionary(); @@ -362,7 +354,7 @@ xlTable.AutoFilter.Range = xlTable.Worksheet.Range(xlTable.RangeAddress); } - #endregion + #endregion LoadTables LoadDrawings(worksheetPart, ws); @@ -418,7 +410,7 @@ } } - #endregion + #endregion LoadComments } var workbook = dSpreadsheet.WorkbookPart.Workbook; @@ -760,7 +752,7 @@ } } - #endregion + #endregion Pivot tables } private static void LoadFieldOptions(PivotField pf, IXLPivotField pivotField) @@ -825,10 +817,12 @@ var imagePart = drawingsPart.GetPartById(imgId); using (var stream = imagePart.GetStream()) + using (var ms = new MemoryStream()) { + stream.CopyTo(ms); var vsdp = GetPropertiesFromAnchor(anchor); - var picture = (ws as XLWorksheet).AddPicture(stream, vsdp.Name, Convert.ToInt32(vsdp.Id.Value)) as XLPicture; + var picture = (ws as XLWorksheet).AddPicture(ms, vsdp.Name, Convert.ToInt32(vsdp.Id.Value)) as XLPicture; picture.RelId = imgId; Xdr.ShapeProperties spPr = anchor.Descendants().First(); @@ -940,7 +934,7 @@ return shape; } - #endregion + #endregion Comment Helpers private String GetTableColumnName(string name) { @@ -1544,7 +1538,7 @@ LoadFont(pp, xlCell.RichText.Phonetics); } - #endregion + #endregion Load PhoneticProperties #region Load Phonetic Runs @@ -1554,7 +1548,7 @@ (Int32)pr.EndingBaseIndex.Value); } - #endregion + #endregion Load Phonetic Runs } private void LoadNumberFormat(NumberingFormat nfSource, IXLNumberFormat nf) @@ -1875,7 +1869,7 @@ { case XLFilterOperator.Equal: if (isText) - condition = o => o.ToString().Equals(xlFilter.Value.ToString(), StringComparison.InvariantCultureIgnoreCase); + condition = o => o.ToString().Equals(xlFilter.Value.ToString(), StringComparison.OrdinalIgnoreCase); else condition = o => (o as IComparable).CompareTo(xlFilter.Value) == 0; break; @@ -1886,7 +1880,7 @@ case XLFilterOperator.LessThan: condition = o => (o as IComparable).CompareTo(xlFilter.Value) < 0; break; case XLFilterOperator.NotEqual: if (isText) - condition = o => !o.ToString().Equals(xlFilter.Value.ToString(), StringComparison.InvariantCultureIgnoreCase); + condition = o => !o.ToString().Equals(xlFilter.Value.ToString(), StringComparison.OrdinalIgnoreCase); else condition = o => (o as IComparable).CompareTo(xlFilter.Value) != 0; break; @@ -1929,7 +1923,7 @@ if (isText) { xlFilter.Value = filter.Val.Value; - condition = o => o.ToString().Equals(xlFilter.Value.ToString(), StringComparison.InvariantCultureIgnoreCase); + condition = o => o.ToString().Equals(xlFilter.Value.ToString(), StringComparison.OrdinalIgnoreCase); } else { @@ -2185,7 +2179,7 @@ var id = fr.Descendants().FirstOrDefault(); if (id != null && id.Text != null && !String.IsNullOrWhiteSpace(id.Text)) - conditionalFormat.Id = Guid.Parse(id.Text.Substring(1, id.Text.Length - 2)); + conditionalFormat.Id = new Guid(id.Text.Substring(1, id.Text.Length - 2)); ExtractConditionalFormatValueObjects(conditionalFormat, dataBar); } diff --git a/ClosedXML/Excel/XLWorkbook_Save.cs b/ClosedXML/Excel/XLWorkbook_Save.cs index 1c68c90..fa3e568 100644 --- a/ClosedXML/Excel/XLWorkbook_Save.cs +++ b/ClosedXML/Excel/XLWorkbook_Save.cs @@ -104,7 +104,7 @@ if (errors.Any()) { - var message = string.Join("\r\n", errors.Select(e => string.Format("{0} in {1}", e.Description, e.Path.XPath)).ToArray()); + var message = string.Join("\r\n", errors.Select(e => string.Format("Part {0}, Path {1}: {2}", e.Part.Uri, e.Path.XPath, e.Description)).ToArray()); throw new ApplicationException(message); } return true; @@ -358,7 +358,8 @@ // Only delete the VmlDrawingParts for comments. if (vmlDrawingPart != null) { - var xdoc = XDocumentExtensions.Load(vmlDrawingPart.GetStream(FileMode.Open)); + var vmlStream = vmlDrawingPart.GetStream(FileMode.Open); + var xdoc = XDocumentExtensions.Load(vmlStream); //xdoc.Root.Elements().Where(e => e.Name.LocalName == "shapelayout").Remove(); xdoc.Root.Elements().Where( e => e.Name.LocalName == "shapetype" && (string)e.Attribute("id") == @"_x0000_t202").Remove(); @@ -388,6 +389,7 @@ legacyParts.ForEach(p => vmlDrawingPartNew.AddPart(p, vmlDrawingPart.GetIdOfPart(p))); } + vmlStream.Close(); worksheetPart.DeletePart(vmlDrawingPart); if (hasNewPart && rId != worksheetPart.GetIdOfPart(vmlDrawingPartNew)) @@ -1800,6 +1802,20 @@ var modified = Properties.Modified == DateTime.MinValue ? DateTime.Now : Properties.Modified; document.PackageProperties.Created = created; document.PackageProperties.Modified = modified; + +#if true // Workaround: https://github.com/OfficeDev/Open-XML-SDK/issues/235 + + if (Properties.LastModifiedBy == null) document.PackageProperties.LastModifiedBy = ""; + if (Properties.Author == null) document.PackageProperties.Creator = ""; + if (Properties.Title == null) document.PackageProperties.Title = ""; + if (Properties.Subject == null) document.PackageProperties.Subject = ""; + if (Properties.Category == null) document.PackageProperties.Category = ""; + if (Properties.Keywords == null) document.PackageProperties.Keywords = ""; + if (Properties.Comments == null) document.PackageProperties.Description = ""; + if (Properties.Status == null) document.PackageProperties.ContentStatus = ""; + +#endif + document.PackageProperties.LastModifiedBy = Properties.LastModifiedBy; document.PackageProperties.Creator = Properties.Author; @@ -2190,7 +2206,7 @@ var pivotCacheRecords = new PivotCacheRecords(); pivotCacheRecords.AddNamespaceDeclaration("r", - "http://schemas.openxmlformats.org/officeDocument/2006/relationships"); + "http://schemas.openxmlformats.org/officeDocument/2006/relationships"); pivotTableCacheRecordsPart.PivotCacheRecords = pivotCacheRecords; context.PivotTables.Add(pti.Guid, pti); @@ -2772,9 +2788,10 @@ SaveContext context) { var ms = new MemoryStream(); - CopyStream(vmlDrawingPart.GetStream(FileMode.OpenOrCreate), ms); + var stream = vmlDrawingPart.GetStream(FileMode.OpenOrCreate); + CopyStream(stream, ms); ms.Position = 0; - var writer = new XmlTextWriter(vmlDrawingPart.GetStream(FileMode.Create), Encoding.UTF8); + var writer = new XmlTextWriter(stream, Encoding.UTF8); writer.WriteStartElement("xml"); @@ -2964,8 +2981,8 @@ }, new Xdr.Picture( new Xdr.NonVisualPictureProperties( - new Xdr.NonVisualDrawingProperties { Id = nvpId, Name = pic.Name }, - new Xdr.NonVisualPictureDrawingProperties(new PictureLocks { NoChangeAspect = true }) + new Xdr.NonVisualDrawingProperties { Id = nvpId, Name = pic.Name }, + new Xdr.NonVisualPictureDrawingProperties(new PictureLocks { NoChangeAspect = true }) ), new Xdr.BlipFill( new Blip { Embed = drawingsPart.GetIdOfPart(imagePart), CompressionState = BlipCompressionValues.Print }, @@ -2987,6 +3004,7 @@ case Drawings.XLPicturePlacement.MoveAndSize: var moveAndSizeFromMarker = pic.Markers[Drawings.XLMarkerPosition.TopLeft]; + if (moveAndSizeFromMarker == null) moveAndSizeFromMarker = new Drawings.XLMarker(picture.Worksheet.Cell("A1").Address); fMark = new Xdr.FromMarker { ColumnId = new Xdr.ColumnId((moveAndSizeFromMarker.Address.ColumnNumber - 1).ToString()), @@ -2996,6 +3014,7 @@ }; var moveAndSizeToMarker = pic.Markers[Drawings.XLMarkerPosition.BottomRight]; + if (moveAndSizeToMarker == null) moveAndSizeToMarker = new Drawings.XLMarker(picture.Worksheet.Cell("A1").Address, new System.Drawing.Point(picture.Width, picture.Height)); tMark = new Xdr.ToMarker { ColumnId = new Xdr.ColumnId((moveAndSizeToMarker.Address.ColumnNumber - 1).ToString()), @@ -3032,6 +3051,7 @@ case Drawings.XLPicturePlacement.Move: var moveFromMarker = pic.Markers[Drawings.XLMarkerPosition.TopLeft]; + if (moveFromMarker == null) moveFromMarker = new Drawings.XLMarker(picture.Worksheet.Cell("A1").Address); fMark = new Xdr.FromMarker { ColumnId = new Xdr.ColumnId((moveFromMarker.Address.ColumnNumber - 1).ToString()), @@ -3072,14 +3092,22 @@ } } - private static void RebasePictureIds(WorksheetPart worksheetPart) + // Still not fully implemented for all shapes + private static void RebaseShapeIds(WorksheetPart worksheetPart) { - for (var i = 0; i < worksheetPart.DrawingsPart.WorksheetDrawing.ChildElements.Count; i++) + var worksheetDrawing = worksheetPart.DrawingsPart.WorksheetDrawing; + for (var i = 0; i < worksheetDrawing.ChildElements.Count; i++) { - var anchor = worksheetPart.DrawingsPart.WorksheetDrawing.ElementAt(i); + var anchor = worksheetDrawing.ElementAt(i); var props = GetPropertiesFromAnchor(anchor); if (props != null) - props.Id = Convert.ToUInt32(i + 1); + { + var offset = 1; + while (worksheetDrawing.Descendants().Any(p => p.Id == Convert.ToUInt32(i + offset))) + offset++; + + props.Id = Convert.ToUInt32(i + offset); + } } } @@ -3242,7 +3270,7 @@ { xlStyles.Add(s); } - + foreach (var s in worksheet.Internals.CellsCollection.GetCells().Select(c => c.StyleValue)) { xlStyles.Add(s); @@ -3252,13 +3280,12 @@ pivotTableNumberFormats.Add(ptnf); } - var alignments = xlStyles.Select(s => s.Alignment).Distinct().ToList(); - var borders = xlStyles.Select(s => s.Border).Distinct().ToList(); - var fonts = xlStyles.Select(s => s.Font).Distinct().ToList(); - var fills = xlStyles.Select(s => s.Fill).Distinct().ToList(); + var alignments = xlStyles.Select(s => s.Alignment).Distinct().ToList(); + var borders = xlStyles.Select(s => s.Border).Distinct().ToList(); + var fonts = xlStyles.Select(s => s.Font).Distinct().ToList(); + var fills = xlStyles.Select(s => s.Fill).Distinct().ToList(); var numberFormats = xlStyles.Select(s => s.NumberFormat).Distinct().ToList(); - var protections = xlStyles.Select(s => s.Protection).Distinct().ToList(); - + var protections = xlStyles.Select(s => s.Protection).Distinct().ToList(); for (int i = 0; i < fonts.Count; i++) { @@ -3378,8 +3405,6 @@ { foreach (var cf in ws.ConditionalFormats) { - //var ie = context.DifferentialFormats.Keys.First().Equals(cf.Style); - if (!cf.Style.Value.Equals(DefaultStyle.Value) && !context.DifferentialFormats.ContainsKey(cf.Style.Value.Key)) AddConditionalDifferentialFormat(workbookStylesPart.Stylesheet.DifferentialFormats, cf, context); } @@ -3419,7 +3444,7 @@ LoadBorder(df.Border, emptyContainer.Style.Border); LoadNumberFormat(df.NumberingFormat, emptyContainer.Style.NumberFormat); LoadFill(df.Fill, emptyContainer.Style.Fill, differentialFillFormat: true); - + if (!dictionary.ContainsKey(emptyContainer.StyleValue.Key)) dictionary.Add(emptyContainer.StyleValue.Key, id++); } @@ -3490,11 +3515,11 @@ differentialFormat.Append(numberFormat); } - var diffFill = GetNewFill(new FillInfo {Fill = style.Fill}, differentialFillFormat: true, ignoreMod: false); + var diffFill = GetNewFill(new FillInfo { Fill = style.Fill }, differentialFillFormat: true, ignoreMod: false); if (diffFill?.HasChildren ?? false) differentialFormat.Append(diffFill); - var diffBorder = GetNewBorder(new BorderInfo {Border = style.Border}, false); + var diffBorder = GetNewBorder(new BorderInfo { Border = style.Border }, false); if (diffBorder?.HasChildren ?? false) differentialFormat.Append(diffBorder); @@ -3619,7 +3644,7 @@ && f.FillId != null && styleInfo.FillId == f.FillId && f.FontId != null && styleInfo.FontId == f.FontId && f.NumberFormatId != null && styleInfo.NumberFormatId == f.NumberFormatId - && (f.ApplyFill == null && styleInfo.Style.Fill == XLFillValue.Default || + && (f.ApplyFill == null && styleInfo.Style.Fill == XLFillValue.Default || f.ApplyFill != null && f.ApplyFill == ApplyFill(styleInfo)) && (f.ApplyBorder == null && styleInfo.Style.Border == XLBorderValue.Default || f.ApplyBorder != null && f.ApplyBorder == ApplyBorder(styleInfo)) @@ -3764,10 +3789,10 @@ var DiagonalBorder = new DiagonalBorder { Style = borderInfo.Border.DiagonalBorder.ToOpenXml() }; if (borderInfo.Border.DiagonalBorderColor != XLBorderValue.Default.DiagonalBorderColor || ignoreMod) if (borderInfo.Border.DiagonalBorderColor != null) - { - var DiagonalBorderColor = GetNewColor(borderInfo.Border.DiagonalBorderColor); - DiagonalBorder.AppendChild(DiagonalBorderColor); - } + { + var DiagonalBorderColor = GetNewColor(borderInfo.Border.DiagonalBorderColor); + DiagonalBorder.AppendChild(DiagonalBorderColor); + } border.AppendChild(DiagonalBorder); } @@ -4062,7 +4087,7 @@ var verticalAlignment = fontInfo.Font.VerticalAlignment != XLFontValue.Default.VerticalAlignment || ignoreMod ? new VerticalTextAlignment { Val = fontInfo.Font.VerticalAlignment.ToOpenXml() } : null; - + var shadow = (fontInfo.Font.Shadow != XLFontValue.Default.Shadow || ignoreMod) && fontInfo.Font.Shadow ? new Shadow() : null; var fontSize = fontInfo.Font.FontSize != XLFontValue.Default.FontSize || ignoreMod ? new FontSize { Val = fontInfo.Font.FontSize } @@ -4425,7 +4450,8 @@ return range.RangeAddress.FirstAddress.ToStringRelative(false); else return range.RangeAddress.ToStringRelative(false); - })); + }) + ); selection.SequenceOfReferences = new ListValue { InnerText = String.Join(" ", seqRef.Distinct().ToArray()) }; @@ -5426,7 +5452,7 @@ } if (xlWorksheet.Pictures.Any()) - RebasePictureIds(worksheetPart); + RebaseShapeIds(worksheetPart); var tableParts = worksheetPart.Worksheet.Elements().First(); if (xlWorksheet.Pictures.Any() && !worksheetPart.Worksheet.OfType().Any()) diff --git a/ClosedXML/Excel/XLWorksheet.cs b/ClosedXML/Excel/XLWorksheet.cs index b0648cd..5775416 100644 --- a/ClosedXML/Excel/XLWorksheet.cs +++ b/ClosedXML/Excel/XLWorksheet.cs @@ -184,7 +184,7 @@ set { if (value > Workbook.WorksheetsInternal.Count + Workbook.UnsupportedSheets.Count + 1) - throw new IndexOutOfRangeException("Index must be equal or less than the number of worksheets + 1."); + throw new ArgumentOutOfRangeException(nameof(value), "Index must be equal or less than the number of worksheets + 1."); if (value < _position) { diff --git a/ClosedXML/Extensions.cs b/ClosedXML/Extensions.cs index 8836f26..5d14a91 100644 --- a/ClosedXML/Extensions.cs +++ b/ClosedXML/Extensions.cs @@ -213,47 +213,25 @@ public static class FontBaseExtensions { - private static Font GetCachedFont(IXLFontBase fontBase, Dictionary fontCache) - { - Font font; - if (!fontCache.TryGetValue(fontBase, out font)) - { - font = new Font(fontBase.FontName, (float)fontBase.FontSize, GetFontStyle(fontBase)); - fontCache.Add(fontBase, font); - } - return font; - } - public static Double GetWidth(this IXLFontBase fontBase, String text, Dictionary fontCache) { if (String.IsNullOrWhiteSpace(text)) return 0; var font = GetCachedFont(fontBase, fontCache); + var textWidth = GraphicsUtils.MeasureString(text, font).Width; - var textSize = GraphicsUtils.MeasureString(text, font); - - double width = (((textSize.Width / (double)7) * 256) - (128 / 7)) / 256; + double width = (textWidth / 7d * 256 - 128 / 7) / 256; width = Math.Round(width + 0.2, 2); return width; } - private static FontStyle GetFontStyle(IXLFontBase font) - { - FontStyle fontStyle = FontStyle.Regular; - if (font.Bold) fontStyle |= FontStyle.Bold; - if (font.Italic) fontStyle |= FontStyle.Italic; - if (font.Strikethrough) fontStyle |= FontStyle.Strikeout; - if (font.Underline != XLFontUnderlineValues.None) fontStyle |= FontStyle.Underline; - return fontStyle; - } - public static Double GetHeight(this IXLFontBase fontBase, Dictionary fontCache) { var font = GetCachedFont(fontBase, fontCache); - var textSize = GraphicsUtils.MeasureString("X", font); - return (double)textSize.Height * 0.85; + var textHeight = GraphicsUtils.MeasureString("X", font).Height; + return (double)textHeight * 0.85; } public static void CopyFont(this IXLFontBase font, IXLFontBase sourceFont) @@ -270,6 +248,27 @@ font.FontFamilyNumbering = sourceFont.FontFamilyNumbering; font.FontCharSet = sourceFont.FontCharSet; } + + private static Font GetCachedFont(IXLFontBase fontBase, Dictionary fontCache) + { + Font font; + if (!fontCache.TryGetValue(fontBase, out font)) + { + font = new Font(fontBase.FontName, (float)fontBase.FontSize, GetFontStyle(fontBase)); + fontCache.Add(fontBase, font); + } + return font; + } + + private static FontStyle GetFontStyle(IXLFontBase font) + { + FontStyle fontStyle = FontStyle.Regular; + if (font.Bold) fontStyle |= FontStyle.Bold; + if (font.Italic) fontStyle |= FontStyle.Italic; + if (font.Strikethrough) fontStyle |= FontStyle.Strikeout; + if (font.Underline != XLFontUnderlineValues.None) fontStyle |= FontStyle.Underline; + return fontStyle; + } } public static class XDocumentExtensions diff --git a/ClosedXML/Properties/AssemblyInfo.cs b/ClosedXML/Properties/AssemblyInfo.cs index 92b76d7..ddf1df5 100644 --- a/ClosedXML/Properties/AssemblyInfo.cs +++ b/ClosedXML/Properties/AssemblyInfo.cs @@ -1,24 +1 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ClosedXML")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ClosedXML")] -[assembly: AssemblyCopyright("Copyright © Manuel De Leon 2014")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("8747331d-de58-4621-8c7f-a7d57ca3467a")] - -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ClosedXML_Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a1fb8ba59167fe734d64128ca73d32c45cb8a117246d09c95c8769db88fe332b0a3396bedd0ea48ee42b0e5796fec0798ca5cb628a9a6de80d35d6c67b936ca1670347b3d4f2b769c8ce2ddcf959dbac6bcd88e6c08751ea1fffa0522de3507193e7035305a8aa008d6c88cca1341b3120fa9c347ab3f97e2d772e2709277da5")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ClosedXML_Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a1fb8ba59167fe734d64128ca73d32c45cb8a117246d09c95c8769db88fe332b0a3396bedd0ea48ee42b0e5796fec0798ca5cb628a9a6de80d35d6c67b936ca1670347b3d4f2b769c8ce2ddcf959dbac6bcd88e6c08751ea1fffa0522de3507193e7035305a8aa008d6c88cca1341b3120fa9c347ab3f97e2d772e2709277da5")] diff --git a/ClosedXML/Properties/AssemblyVersionInfo.cs b/ClosedXML/Properties/AssemblyVersionInfo.cs deleted file mode 100644 index da0403f..0000000 --- a/ClosedXML/Properties/AssemblyVersionInfo.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Reflection; - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("0.92.0.0")] -[assembly: AssemblyFileVersion("0.92.0.0")] -[assembly: AssemblyInformationalVersion("0.92.0")] diff --git a/ClosedXML/Utils/GraphicsUtils.cs b/ClosedXML/Utils/GraphicsUtils.cs index be2472f..f0f7644 100644 --- a/ClosedXML/Utils/GraphicsUtils.cs +++ b/ClosedXML/Utils/GraphicsUtils.cs @@ -7,6 +7,7 @@ { [ThreadStatic] private static Graphics threadLocalGraphics; + internal static Graphics Graphics { get @@ -20,9 +21,10 @@ } } + private static StringFormat defaultStringFormat = StringFormat.GenericTypographic; public static SizeF MeasureString(string s, Font font) { - SizeF result = Graphics.MeasureString(s, font, Int32.MaxValue, StringFormat.GenericTypographic); + SizeF result = Graphics.MeasureString(s, font, Int32.MaxValue, defaultStringFormat); return result; } } diff --git a/ClosedXML/XLHelper.cs b/ClosedXML/XLHelper.cs index 1c1081d..5bb1bd5 100644 --- a/ClosedXML/XLHelper.cs +++ b/ClosedXML/XLHelper.cs @@ -1,12 +1,11 @@ using System; +using System.Drawing; using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; namespace ClosedXML.Excel { - using System.Drawing; - using System.Linq; - using System.Text.RegularExpressions; - /// /// Common methods /// @@ -19,7 +18,8 @@ public const String MaxColumnLetter = "XFD"; public const Double Epsilon = 1e-10; - private const Int32 TwoT26 = 26 * 26; + private const Int32 TwoT26 = 26*26; + internal static readonly Graphics Graphic = Graphics.FromImage(new Bitmap(200, 200)); internal static readonly Double DpiX = Graphic.DpiX; internal static readonly NumberStyles NumberStyle = NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign | NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowExponent; @@ -226,6 +226,19 @@ return rows; } +#if false +// Not using this anymore, but keeping it around for in case we bring back .NET3.5 support. + public static bool IsNullOrWhiteSpace(string value) + { +#if _NET35_ + if (value == null) return true; + return value.All(c => char.IsWhiteSpace(c)); +#else + return String.IsNullOrWhiteSpace(value); +#endif + } +#endif + private static readonly Regex A1RegexRelative = new Regex( @"(?<=\W)(?\$?[a-zA-Z]{1,3}\$?\d{1,7})(?=\W)" // A1 + @"|(?<=\W)(?\$?\d{1,7}:\$?\d{1,7})(?=\W)" // 1:1 diff --git a/ClosedXML/packages.config b/ClosedXML/packages.config deleted file mode 100644 index 3f21e14..0000000 --- a/ClosedXML/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/ClosedXML_Examples/ClosedXML_Examples.csproj b/ClosedXML_Examples/ClosedXML_Examples.csproj index dca9ada..bcbad93 100644 --- a/ClosedXML_Examples/ClosedXML_Examples.csproj +++ b/ClosedXML_Examples/ClosedXML_Examples.csproj @@ -1,208 +1,38 @@ - - + + - Debug - x86 - 8.0.30703 - 2.0 - {03A518D0-1CB7-488E-861C-C4E782B27A46} + netcoreapp2.0;net40;net46 Exe - Properties - ClosedXML_Examples - ClosedXML_Examples - v4.0 - - - 512 - ..\ - true - - - true - bin\Debug\ - DEBUG;TRACE - full - AnyCPU - prompt - false - 6 - - - bin\Release\ - TRACE - true - pdbonly - AnyCPU - prompt - false - 6 - - - ClosedXML_Examples.Program - - + 0.93.0 true - - ClosedXML.snk + $(NoWarn);NU1605 - - true + + + $(DefineConstants);_NETSTANDARD_;_NETSTANDARD2_0_ + + + $(DefineConstants);_NETFRAMEWORK_;_NET40_ + + + + $(DefineConstants);_NETFRAMEWORK_;_NET46_ + + - - ..\packages\DocumentFormat.OpenXml.2.7.2\lib\net40\DocumentFormat.OpenXml.dll - True - - - - - - - - - - - + + + - - Properties\AssemblyVersionInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - .editorconfig - - - - + + - - - {BD5E6BFE-E837-4A35-BCA9-39667D873A20} - ClosedXML - - - - - - - - - - - - - - \ No newline at end of file + + diff --git a/ClosedXML_Examples/Properties/AssemblyInfo.cs b/ClosedXML_Examples/Properties/AssemblyInfo.cs deleted file mode 100644 index c7e7b8b..0000000 --- a/ClosedXML_Examples/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ClosedXML_Examples")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("ClosedXML_Examples")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("603cd89c-23f2-415e-8afc-2e9ab7a4c372")] diff --git a/ClosedXML_Examples/Styles/UsingColors.cs b/ClosedXML_Examples/Styles/UsingColors.cs index 9364c15..54fea8f 100644 --- a/ClosedXML_Examples/Styles/UsingColors.cs +++ b/ClosedXML_Examples/Styles/UsingColors.cs @@ -61,12 +61,6 @@ ro++; - // FromKnownColor(KnownColor knownColor) - ws.Cell(++ro, 1).Style.Fill.BackgroundColor = XLColor.FromKnownColor(KnownColor.Plum); - ws.Cell(ro, 2).Value = "XLColor.FromKnownColor(KnownColor.Plum)"; - - ro++; - // FromName(String colorName) ws.Cell(++ro, 1).Style.Fill.BackgroundColor = XLColor.FromName("PowderBlue"); ws.Cell(ro, 2).Value = "XLColor.FromName(\"PowderBlue\")"; @@ -87,4 +81,4 @@ wb.SaveAs(filePath); } } -} \ No newline at end of file +} diff --git a/ClosedXML_Examples/app.config b/ClosedXML_Examples/app.config index 86fcc8b..21ac8f3 100644 --- a/ClosedXML_Examples/app.config +++ b/ClosedXML_Examples/app.config @@ -1,6 +1,6 @@ - - - - - - + + + + + + diff --git a/ClosedXML_Examples/packages.config b/ClosedXML_Examples/packages.config deleted file mode 100644 index becf24f..0000000 --- a/ClosedXML_Examples/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/ClosedXML_Sandbox/ClosedXML_Sandbox.csproj b/ClosedXML_Sandbox/ClosedXML_Sandbox.csproj index 7931620..d8ea4f6 100644 --- a/ClosedXML_Sandbox/ClosedXML_Sandbox.csproj +++ b/ClosedXML_Sandbox/ClosedXML_Sandbox.csproj @@ -1,92 +1,44 @@ - - + + - Debug - x86 - 8.0.30703 - 2.0 - {38B882F0-E6F2-45C5-9BE9-CDC27FBEB4AB} Exe - Properties - ClosedXML_Sandbox - ClosedXML_Sandbox - v4.0 - 512 - ..\ - true - + netcoreapp2.0;net40;net46 + 0.93.0 + $(NoWarn);NU1605 - - true - bin\Debug\ - DEBUG;TRACE - full - AnyCPU - prompt - false - 6 + + + $(DefineConstants);_NETSTANDARD_;_NETSTANDARD2_0_ - - bin\Release\ - TRACE - true - pdbonly - AnyCPU - prompt - false - 6 + + + $(DefineConstants);_NETFRAMEWORK_;_NET40_ - - - ..\packages\DocumentFormat.OpenXml.2.7.2\lib\net40\DocumentFormat.OpenXml.dll - True - - + + + $(DefineConstants);_NETFRAMEWORK_;_NET46_ + + + + + + - - - - - - - - - - - Properties\AssemblyVersionInfo.cs - - - - - + + + + - - .editorconfig - - - - + + PreserveNewest + - - {bd5e6bfe-e837-4a35-bca9-39667d873a20} - ClosedXML - - - {03A518D0-1CB7-488E-861C-C4E782B27A46} - ClosedXML_Examples - + - - - \ No newline at end of file + + diff --git a/ClosedXML_Sandbox/Properties/AssemblyInfo.cs b/ClosedXML_Sandbox/Properties/AssemblyInfo.cs deleted file mode 100644 index a2c5998..0000000 --- a/ClosedXML_Sandbox/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ClosedXML_Sandbox")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("ClosedXML_Sandbox")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("7a3783fd-4e1b-4c17-a745-e6bbb593bd45")] diff --git a/ClosedXML_Sandbox/ReflectionExtensions.cs b/ClosedXML_Sandbox/ReflectionExtensions.cs new file mode 100644 index 0000000..7229a8c --- /dev/null +++ b/ClosedXML_Sandbox/ReflectionExtensions.cs @@ -0,0 +1,17 @@ +#if _NET40_ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace ClosedXML_Sandbox +{ + internal static class ReflectionExtensions + { + public static void SetValue(this PropertyInfo info, object obj, object value) + { + info.SetValue(obj, value, null); + } + } +} +#endif diff --git a/ClosedXML_Sandbox/app.config b/ClosedXML_Sandbox/app.config index 86fcc8b..21ac8f3 100644 --- a/ClosedXML_Sandbox/app.config +++ b/ClosedXML_Sandbox/app.config @@ -1,6 +1,6 @@ - - - - - - + + + + + + diff --git a/ClosedXML_Sandbox/packages.config b/ClosedXML_Sandbox/packages.config deleted file mode 100644 index becf24f..0000000 --- a/ClosedXML_Sandbox/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/ClosedXML_Tests/ClosedXML_Tests.csproj b/ClosedXML_Tests/ClosedXML_Tests.csproj index 43cb231..5656bfe 100644 --- a/ClosedXML_Tests/ClosedXML_Tests.csproj +++ b/ClosedXML_Tests/ClosedXML_Tests.csproj @@ -1,332 +1,67 @@ - - + + - Debug - AnyCPU - - - 2.0 - {09B066ED-E4A7-4545-A1A4-FF03DD524BDF} - Library - Properties - ClosedXML_Tests - ClosedXML_Tests - v4.5.2 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - ..\ - true + netcoreapp2.0;net40;net46 + 0.93.0 + false + $(NoWarn);NU1605 - - true - full - false - bin\Debug\ - DEBUG;TRACE;$(AppVeyor) - prompt - 4 - false - 6 - - - pdbonly - true - bin\Release\ - TRACE;$(AppVeyor) - prompt - 4 - false - 6 - + + $(AppVeyor) true - - ClosedXML.snk + + + $(DefineConstants);_NETSTANDARD_;_NETSTANDARD2_0_ + + + + $(DefineConstants);_NETFRAMEWORK_;_NET40_ + + + + $(DefineConstants);_NETFRAMEWORK_;_NET46_ + + - - ..\packages\DocumentFormat.OpenXml.2.7.2\lib\net40\DocumentFormat.OpenXml.dll - True - + + + + + + + + + + + + + + + + + + + + + - - ..\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll - True - - - - - 3.5 - - - - - - - - - Properties\AssemblyVersionInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - {BD5E6BFE-E837-4A35-BCA9-39667D873A20} - ClosedXML - - - {03A518D0-1CB7-488E-861C-C4E782B27A46} - ClosedXML_Examples - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - .editorconfig - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + diff --git a/ClosedXML_Tests/Excel/CalcEngine/DateAndTimeTests.cs b/ClosedXML_Tests/Excel/CalcEngine/DateAndTimeTests.cs index 1d4c5d4..32e6af1 100644 --- a/ClosedXML_Tests/Excel/CalcEngine/DateAndTimeTests.cs +++ b/ClosedXML_Tests/Excel/CalcEngine/DateAndTimeTests.cs @@ -12,7 +12,7 @@ [TestFixture] public class DateAndTimeTests { - [OneTimeSetUp] + [SetUp] public void SetCultureInfo() { Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US"); diff --git a/ClosedXML_Tests/Excel/CalcEngine/FunctionsTests.cs b/ClosedXML_Tests/Excel/CalcEngine/FunctionsTests.cs index a4435ef..781a945 100644 --- a/ClosedXML_Tests/Excel/CalcEngine/FunctionsTests.cs +++ b/ClosedXML_Tests/Excel/CalcEngine/FunctionsTests.cs @@ -8,7 +8,7 @@ [TestFixture] public class FunctionsTests { - [OneTimeSetUp] + [SetUp] public void Init() { // Make sure tests run on a deterministic culture diff --git a/ClosedXML_Tests/Excel/CalcEngine/InformationTests.cs b/ClosedXML_Tests/Excel/CalcEngine/InformationTests.cs index 19de6ca..07b0027 100644 --- a/ClosedXML_Tests/Excel/CalcEngine/InformationTests.cs +++ b/ClosedXML_Tests/Excel/CalcEngine/InformationTests.cs @@ -9,7 +9,7 @@ [TestFixture] public class InformationTests { - [OneTimeSetUp] + [SetUp] public void SetCultureInfo() { Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-US"); diff --git a/ClosedXML_Tests/Excel/CalcEngine/LookupTests.cs b/ClosedXML_Tests/Excel/CalcEngine/LookupTests.cs index b7b7151..ce521c9 100644 --- a/ClosedXML_Tests/Excel/CalcEngine/LookupTests.cs +++ b/ClosedXML_Tests/Excel/CalcEngine/LookupTests.cs @@ -10,7 +10,7 @@ { private XLWorkbook workbook; - [OneTimeSetUp] + [SetUp] public void Init() { // Make sure tests run on a deterministic culture diff --git a/ClosedXML_Tests/Excel/CalcEngine/StatisticalTests.cs b/ClosedXML_Tests/Excel/CalcEngine/StatisticalTests.cs index 3f451f1..34516fe 100644 --- a/ClosedXML_Tests/Excel/CalcEngine/StatisticalTests.cs +++ b/ClosedXML_Tests/Excel/CalcEngine/StatisticalTests.cs @@ -217,7 +217,7 @@ workbook.Dispose(); } - [OneTimeSetUp] + [SetUp] public void Init() { // Make sure tests run on a deterministic culture diff --git a/ClosedXML_Tests/Excel/CalcEngine/TextTests.cs b/ClosedXML_Tests/Excel/CalcEngine/TextTests.cs index b4cf9b6..3644161 100644 --- a/ClosedXML_Tests/Excel/CalcEngine/TextTests.cs +++ b/ClosedXML_Tests/Excel/CalcEngine/TextTests.cs @@ -11,7 +11,7 @@ [TestFixture] public class TextTests { - [OneTimeSetUp] + [SetUp] public void Init() { // Make sure tests run on a deterministic culture diff --git a/ClosedXML_Tests/Excel/Columns/ColumnTests.cs b/ClosedXML_Tests/Excel/Columns/ColumnTests.cs index a0571cd..35dc053 100644 --- a/ClosedXML_Tests/Excel/Columns/ColumnTests.cs +++ b/ClosedXML_Tests/Excel/Columns/ColumnTests.cs @@ -3,6 +3,7 @@ using System.Linq; using ClosedXML.Excel; using NUnit.Framework; +using ClosedXML_Tests.Utils; namespace ClosedXML_Tests.Excel { @@ -107,8 +108,6 @@ IXLColumn column3 = ws.Column(3); IXLColumn columnIns = ws.Column(2).InsertColumnsBefore(1).First(); - string outputPath = Path.Combine(TestHelper.TestsOutputDirectory, "ForTesting", "Sandbox.xlsx"); - wb.SaveAs(outputPath, true); Assert.AreEqual(XLColor.Red, ws.Column(1).Cell(1).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.Red, ws.Column(1).Cell(2).Style.Fill.BackgroundColor); diff --git a/ClosedXML_Tests/Excel/Comments/CommentsTests.cs b/ClosedXML_Tests/Excel/Comments/CommentsTests.cs index 1ba4e76..34b8382 100644 --- a/ClosedXML_Tests/Excel/Comments/CommentsTests.cs +++ b/ClosedXML_Tests/Excel/Comments/CommentsTests.cs @@ -1,10 +1,6 @@ using ClosedXML.Excel; using NUnit.Framework; -using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace ClosedXML_Tests.Excel.Comments { @@ -23,11 +19,9 @@ Assert.AreEqual(XLColorType.Indexed, xlColor.ColorType); Assert.AreEqual(81, xlColor.Indexed); - var color = xlColor.Color.ToHex(); Assert.AreEqual("FF000000", color); } - } } } diff --git a/ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs b/ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs index 1da6ecb..a52cef1 100644 --- a/ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs +++ b/ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs @@ -35,6 +35,27 @@ } [Test] + public void CanAddPictureFromStream() + { + using (var wb = new XLWorkbook()) + { + var ws = wb.AddWorksheet("Sheet1"); + + using (var resourceStream = Assembly.GetAssembly(typeof(ClosedXML_Examples.BasicTable)).GetManifestResourceStream("ClosedXML_Examples.Resources.SampleImage.jpg")) + { + var picture = ws.AddPicture(resourceStream, "MyPicture") + .WithPlacement(XLPicturePlacement.FreeFloating) + .MoveTo(50, 50) + .WithSize(200, 200); + + Assert.AreEqual(XLPictureFormat.Jpeg, picture.Format); + Assert.AreEqual(200, picture.Width); + Assert.AreEqual(200, picture.Height); + } + } + } + + [Test] public void CanAddPictureFromFile() { using (var wb = new XLWorkbook()) @@ -77,9 +98,8 @@ var ws = wb.AddWorksheet("Sheet1"); using (var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ClosedXML_Tests.Resource.Images.ImageHandling.png")) - using (var bitmap = Bitmap.FromStream(resourceStream) as Bitmap) { - var pic = ws.AddPicture(bitmap, "MyPicture") + var pic = ws.AddPicture(resourceStream, "MyPicture") .WithPlacement(XLPicturePlacement.FreeFloating) .MoveTo(50, 50); @@ -251,7 +271,6 @@ Assert.AreEqual(originalCount - 2, ws.Pictures.Count); } } - } [Test] diff --git a/ClosedXML_Tests/Excel/Ranges/CopyingRangesTests.cs b/ClosedXML_Tests/Excel/Ranges/CopyingRangesTests.cs index f2b8058..45eb96c 100644 --- a/ClosedXML_Tests/Excel/Ranges/CopyingRangesTests.cs +++ b/ClosedXML_Tests/Excel/Ranges/CopyingRangesTests.cs @@ -19,7 +19,7 @@ column1.Cell(2).Style.Fill.SetBackgroundColor(XLColor.FromArgb(1, 1, 1)); column1.Cell(3).Style.Fill.SetBackgroundColor(XLColor.FromHtml("#CCCCCC")); column1.Cell(4).Style.Fill.SetBackgroundColor(XLColor.FromIndex(26)); - column1.Cell(5).Style.Fill.SetBackgroundColor(XLColor.FromKnownColor(KnownColor.MediumSeaGreen)); + column1.Cell(5).Style.Fill.SetBackgroundColor(XLColor.FromColor(Color.MediumSeaGreen)); column1.Cell(6).Style.Fill.SetBackgroundColor(XLColor.FromName("Blue")); column1.Cell(7).Style.Fill.SetBackgroundColor(XLColor.FromTheme(XLThemeColor.Accent3)); @@ -31,7 +31,7 @@ Assert.AreEqual(XLColor.FromArgb(1, 1, 1), column2.Cell(2).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromHtml("#CCCCCC"), column2.Cell(3).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromIndex(26), column2.Cell(4).Style.Fill.BackgroundColor); - Assert.AreEqual(XLColor.FromKnownColor(KnownColor.MediumSeaGreen), + Assert.AreEqual(XLColor.FromColor(Color.MediumSeaGreen), column2.Cell(5).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromName("Blue"), column2.Cell(6).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromTheme(XLThemeColor.Accent3), column2.Cell(7).Style.Fill.BackgroundColor); @@ -41,7 +41,7 @@ Assert.AreEqual(XLColor.FromArgb(1, 1, 1), column3.Cell(2).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromHtml("#CCCCCC"), column3.Cell(3).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromIndex(26), column3.Cell(4).Style.Fill.BackgroundColor); - Assert.AreEqual(XLColor.FromKnownColor(KnownColor.MediumSeaGreen), + Assert.AreEqual(XLColor.FromColor(Color.MediumSeaGreen), column3.Cell(5).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromName("Blue"), column3.Cell(6).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromTheme(XLThemeColor.Accent3), column3.Cell(7).Style.Fill.BackgroundColor); @@ -64,7 +64,7 @@ Assert.AreEqual(XLColor.FromArgb(1, 1, 1), row2.Cell(2).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromHtml("#CCCCCC"), row2.Cell(3).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromIndex(26), row2.Cell(4).Style.Fill.BackgroundColor); - Assert.AreEqual(XLColor.FromKnownColor(KnownColor.MediumSeaGreen), row2.Cell(5).Style.Fill.BackgroundColor); + Assert.AreEqual(XLColor.FromColor(Color.MediumSeaGreen), row2.Cell(5).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromName("Blue"), row2.Cell(6).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromTheme(XLThemeColor.Accent3), row2.Cell(7).Style.Fill.BackgroundColor); @@ -73,7 +73,7 @@ Assert.AreEqual(XLColor.FromArgb(1, 1, 1), row3.Cell(2).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromHtml("#CCCCCC"), row3.Cell(3).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromIndex(26), row3.Cell(4).Style.Fill.BackgroundColor); - Assert.AreEqual(XLColor.FromKnownColor(KnownColor.MediumSeaGreen), row3.Cell(5).Style.Fill.BackgroundColor); + Assert.AreEqual(XLColor.FromColor(Color.MediumSeaGreen), row3.Cell(5).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromName("Blue"), row3.Cell(6).Style.Fill.BackgroundColor); Assert.AreEqual(XLColor.FromTheme(XLThemeColor.Accent3), row3.Cell(7).Style.Fill.BackgroundColor); @@ -100,7 +100,7 @@ Assert.AreEqual(2, ws.ConditionalFormats.Count()); Assert.IsTrue(ws.ConditionalFormats.Single(x => x.Range.RangeAddress.ToStringRelative() == "B1:B3").Values.Any(v => v.Value.Value == "G1" && v.Value.IsFormula)); Assert.IsTrue(ws.ConditionalFormats.Single(x => x.Range.RangeAddress.ToStringRelative() == "C5:C5").Values.Any(v => v.Value.Value == "H5" && v.Value.IsFormula)); - } + } private static void FillRow(IXLRow row1) { @@ -108,7 +108,7 @@ row1.Cell(2).Style.Fill.SetBackgroundColor(XLColor.FromArgb(1, 1, 1)); row1.Cell(3).Style.Fill.SetBackgroundColor(XLColor.FromHtml("#CCCCCC")); row1.Cell(4).Style.Fill.SetBackgroundColor(XLColor.FromIndex(26)); - row1.Cell(5).Style.Fill.SetBackgroundColor(XLColor.FromKnownColor(KnownColor.MediumSeaGreen)); + row1.Cell(5).Style.Fill.SetBackgroundColor(XLColor.FromColor(Color.MediumSeaGreen)); row1.Cell(6).Style.Fill.SetBackgroundColor(XLColor.FromName("Blue")); row1.Cell(7).Style.Fill.SetBackgroundColor(XLColor.FromTheme(XLThemeColor.Accent3)); diff --git a/ClosedXML_Tests/Excel/Ranges/UsedAndUnusedCellsTests.cs b/ClosedXML_Tests/Excel/Ranges/UsedAndUnusedCellsTests.cs index a4442c6..fab9b91 100644 --- a/ClosedXML_Tests/Excel/Ranges/UsedAndUnusedCellsTests.cs +++ b/ClosedXML_Tests/Excel/Ranges/UsedAndUnusedCellsTests.cs @@ -12,7 +12,7 @@ { private XLWorkbook workbook; - [OneTimeSetUp] + [SetUp] public void SetupWorkbook() { workbook = new XLWorkbook(); diff --git a/ClosedXML_Tests/Excel/Ranges/XLRangeBaseTests.cs b/ClosedXML_Tests/Excel/Ranges/XLRangeBaseTests.cs index eff6f11..5668d01 100644 --- a/ClosedXML_Tests/Excel/Ranges/XLRangeBaseTests.cs +++ b/ClosedXML_Tests/Excel/Ranges/XLRangeBaseTests.cs @@ -387,5 +387,58 @@ Assert.AreEqual(1, ws.ConditionalFormats.Count()); Assert.AreEqual("C3:G4", ws.ConditionalFormats.Single().Range.RangeAddress.ToStringRelative()); } + + [Test] + public void RangesRemoveAllWithDispose() + { + var ws = new XLWorkbook().Worksheets.Add("Sheet1"); + var ranges = new XLRanges(); + ranges.Add(ws.Range("A1:A2")); + ranges.Add(ws.Range("B1:B2")); + var rangesCopy = ranges.ToList(); + + ranges.RemoveAll(); + ws.FirstColumn().InsertColumnsBefore(1); + + Assert.AreEqual(0, ranges.Count); + // if ranges were disposed they addresses didn't change + Assert.AreEqual("A1:A2", rangesCopy.First().RangeAddress.ToString()); + Assert.AreEqual("B1:B2", rangesCopy.Last().RangeAddress.ToString()); + } + + [Test] + public void RangesRemoveAllWithoutDispose() + { + var ws = new XLWorkbook().Worksheets.Add("Sheet1"); + var ranges = new XLRanges(); + ranges.Add(ws.Range("A1:A2")); + ranges.Add(ws.Range("B1:B2")); + var rangesCopy = ranges.ToList(); + + ranges.RemoveAll(null, false); + ws.FirstColumn().InsertColumnsBefore(1); + + Assert.AreEqual(0, ranges.Count); + // if ranges were not disposed they addresses should change + Assert.AreEqual("B1:B2", rangesCopy.First().RangeAddress.ToString()); + Assert.AreEqual("C1:C2", rangesCopy.Last().RangeAddress.ToString()); + } + + + [Test] + public void RangesRemoveAllByCriteria() + { + var ws = new XLWorkbook().Worksheets.Add("Sheet1"); + var ranges = new XLRanges(); + ranges.Add(ws.Range("A1:A2")); + ranges.Add(ws.Range("B1:B3")); + ranges.Add(ws.Range("C1:C4")); + var otherRange = ws.Range("A3:D3"); + + ranges.RemoveAll(r => r.Intersects(otherRange)); + + Assert.AreEqual(1, ranges.Count); + Assert.AreEqual("A1:A2", ranges.Single().RangeAddress.ToString()); + } } } diff --git a/ClosedXML_Tests/Excel/Saving/SavingTests.cs b/ClosedXML_Tests/Excel/Saving/SavingTests.cs index 7f94697..4b19a6b 100644 --- a/ClosedXML_Tests/Excel/Saving/SavingTests.cs +++ b/ClosedXML_Tests/Excel/Saving/SavingTests.cs @@ -16,6 +16,17 @@ public class SavingTests { [Test] + public void CanSaveEmptyFile() + { + using (var ms = new MemoryStream()) + using (var wb = new XLWorkbook()) + { + wb.AddWorksheet("Sheet1"); + wb.SaveAs(ms); + } + } + + [Test] public void CanSuccessfullySaveFileMultipleTimes() { using (var wb = new XLWorkbook()) diff --git a/ClosedXML_Tests/Excel/Styles/NumberFormatTests.cs b/ClosedXML_Tests/Excel/Styles/NumberFormatTests.cs index ed012b3..76bf394 100644 --- a/ClosedXML_Tests/Excel/Styles/NumberFormatTests.cs +++ b/ClosedXML_Tests/Excel/Styles/NumberFormatTests.cs @@ -27,7 +27,7 @@ Assert.AreEqual("yy-MM-dd", ws.Cell("A5").Style.DateFormat.Format); ws.Row(1).Style.NumberFormat.Format = "yy-MM-dd"; - ws.Cell("A1").InsertData(table.AsEnumerable(), true); + ws.Cell("A1").InsertData(table.Rows, true); Assert.AreEqual("yy-MM-dd", ws.Cell("E1").Style.DateFormat.Format); } } diff --git a/ClosedXML_Tests/Excel/Tables/TablesTests.cs b/ClosedXML_Tests/Excel/Tables/TablesTests.cs index ca50c3f..e5868fa 100644 --- a/ClosedXML_Tests/Excel/Tables/TablesTests.cs +++ b/ClosedXML_Tests/Excel/Tables/TablesTests.cs @@ -555,6 +555,41 @@ } [Test] + public void TableRenameTests() + { + var l = new List() + { + new TestObjectWithAttributes() { Column1 = "a", Column2 = "b", MyField = 4, UnOrderedColumn = 999 }, + new TestObjectWithAttributes() { Column1 = "c", Column2 = "d", MyField = 5, UnOrderedColumn = 777 } + }; + + using (var wb = new XLWorkbook()) + { + IXLWorksheet ws = wb.AddWorksheet("Sheet1"); + var table1 = ws.FirstCell().InsertTable(l); + var table2 = ws.Cell("A10").InsertTable(l); + + Assert.AreEqual("Table1", table1.Name); + Assert.AreEqual("Table2", table2.Name); + + table1.Name = "table1"; + Assert.AreEqual("table1", table1.Name); + + Assert.Throws(() => table1.Name = ""); + Assert.Throws(() => table1.Name = "R"); + Assert.Throws(() => table1.Name = "C"); + Assert.Throws(() => table1.Name = "r"); + Assert.Throws(() => table1.Name = "c"); + + Assert.Throws(() => table1.Name = "123"); + Assert.Throws(() => table1.Name = new String('A', 256)); + + Assert.Throws(() => table1.Name = "Table2"); + Assert.Throws(() => table1.Name = "TABLE2"); + } + } + + [Test] public void CanResizeTable() { using (var wb = new XLWorkbook()) @@ -692,6 +727,42 @@ } } + [Test] + public void TotalsFunctionsOfHeadersWithWeirdCharacters() + { + var l = new List() + { + new TestObjectWithAttributes() { Column1 = "a", Column2 = "b", MyField = 4, UnOrderedColumn = 999 }, + new TestObjectWithAttributes() { Column1 = "c", Column2 = "d", MyField = 5, UnOrderedColumn = 777 } + }; + + using (var wb = new XLWorkbook()) + { + var ws = wb.AddWorksheet("Sheet1"); + ws.FirstCell().InsertTable(l, false); + + // Give the headings weird names (i.e. spaces, hashes, single quotes + ws.Cell("A1").Value = "ABCD "; + ws.Cell("B1").Value = " #BCD"; + ws.Cell("C1").Value = " as'df "; + ws.Cell("D1").Value = "Normal"; + + var table = ws.RangeUsed().CreateTable(); + Assert.IsNotNull(table); + + table.ShowTotalsRow = true; + table.Field(0).TotalsRowFunction = XLTotalsRowFunction.Count; + table.Field(1).TotalsRowFunction = XLTotalsRowFunction.Count; + table.Field(2).TotalsRowFunction = XLTotalsRowFunction.Sum; + table.Field(3).TotalsRowFunction = XLTotalsRowFunction.Sum; + + Assert.AreEqual("SUBTOTAL(103,Table1[[ABCD ]])", table.Field(0).TotalsRowFormulaA1); + Assert.AreEqual("SUBTOTAL(103,Table1[[ '#BCD]])", table.Field(1).TotalsRowFormulaA1); + Assert.AreEqual("SUBTOTAL(109,Table1[[ as''df ]])", table.Field(2).TotalsRowFormulaA1); + Assert.AreEqual("SUBTOTAL(109,[Normal])", table.Field(3).TotalsRowFormulaA1); + } + } + //TODO: Delete table (not underlying range) } } diff --git a/ClosedXML_Tests/ExcelDocsComparerTests.cs b/ClosedXML_Tests/ExcelDocsComparerTests.cs index ac2fa36..42677eb 100644 --- a/ClosedXML_Tests/ExcelDocsComparerTests.cs +++ b/ClosedXML_Tests/ExcelDocsComparerTests.cs @@ -1,6 +1,6 @@ -using System.IO; using ClosedXML_Examples; using NUnit.Framework; +using System.IO; namespace ClosedXML_Tests { @@ -17,7 +17,7 @@ new BasicTable().Create(left); new BasicTable().Create(right); string message; - Assert.IsTrue(ExcelDocsComparer.Compare(left, right, TestHelper.IsRunningOnUnix, out message)); + Assert.IsTrue(ExcelDocsComparer.Compare(left, right, out message)); } finally { @@ -43,7 +43,7 @@ new HelloWorld().Create(right); string message; - Assert.IsFalse(ExcelDocsComparer.Compare(left, right, TestHelper.IsRunningOnUnix, out message)); + Assert.IsFalse(ExcelDocsComparer.Compare(left, right, out message)); } finally { @@ -58,4 +58,4 @@ } } } -} \ No newline at end of file +} diff --git a/ClosedXML_Tests/OleDb/OleDbTests.cs b/ClosedXML_Tests/OleDb/OleDbTests.cs index 2aa897c..b7cd054 100644 --- a/ClosedXML_Tests/OleDb/OleDbTests.cs +++ b/ClosedXML_Tests/OleDb/OleDbTests.cs @@ -1,4 +1,5 @@ -using ClosedXML.Excel; +#if !APPVEYOR && _NETFRAMEWORK_ +using ClosedXML.Excel; using ClosedXML_Tests.Utils; using NUnit.Framework; using System; @@ -13,7 +14,6 @@ [TestFixture] public class OleDbTests { -#if !APPVEYOR [Test] public void TestOleDbValues() { @@ -78,7 +78,6 @@ } } } -#endif private string CreateTestFile() { @@ -118,3 +117,4 @@ } } } +#endif diff --git a/ClosedXML_Tests/OpenXMLTests.cs b/ClosedXML_Tests/OpenXMLTests.cs new file mode 100644 index 0000000..c0d0ddf --- /dev/null +++ b/ClosedXML_Tests/OpenXMLTests.cs @@ -0,0 +1,30 @@ +using DocumentFormat.OpenXml.Packaging; +using NUnit.Framework; +using System.IO; + +namespace ClosedXML_Tests +{ + [TestFixture] + public class OpenXMLTests + { + [Test] + [Ignore("Workaround has been included in ClosedXML")] + public static void SetPackagePropertiesEntryToNullWithOpenXml() + { + // Fixed in .NET Standard 2.1 + // See: + // https://github.com/OfficeDev/Open-XML-SDK/issues/235 + // https://github.com/dotnet/corefx/issues/23795 + using (var stream = TestHelper.GetStreamFromResource(TestHelper.GetResourcePath(@"Examples\PivotTables\PivotTables.xlsx"))) + using (var ms = new MemoryStream()) + { + stream.CopyTo(ms); + + using (var document = SpreadsheetDocument.Open(ms, true)) + { + document.PackageProperties.Creator = null; + } + } + } + } +} diff --git a/ClosedXML_Tests/Properties/AssemblyInfo.cs b/ClosedXML_Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index bfb414e..0000000 --- a/ClosedXML_Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. - -[assembly: AssemblyTitle("ClosedXML_Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("ClosedXML_Tests")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. - -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM - -[assembly: Guid("a16b867e-6b00-4ccc-a368-c9f9b14e0e6a")] - diff --git a/ClosedXML_Tests/Resource/Examples/Styles/UsingColors.xlsx b/ClosedXML_Tests/Resource/Examples/Styles/UsingColors.xlsx index 2ada7d9..2d49b85 100644 --- a/ClosedXML_Tests/Resource/Examples/Styles/UsingColors.xlsx +++ b/ClosedXML_Tests/Resource/Examples/Styles/UsingColors.xlsx Binary files differ diff --git a/ClosedXML_Tests/TestHelper.cs b/ClosedXML_Tests/TestHelper.cs index 5c1acf8..1887cd5 100644 --- a/ClosedXML_Tests/TestHelper.cs +++ b/ClosedXML_Tests/TestHelper.cs @@ -43,6 +43,8 @@ // the columns widths after AdjustToContents() will // cause the tests to fail. // Therefore we ignore the width attribute when running on Unix + public static bool StripColumnWidths { get { return IsRunningOnUnix; } } + public static bool IsRunningOnUnix { get @@ -83,7 +85,7 @@ using (var streamActual = File.OpenRead(filePath2)) { string message; - var success = ExcelDocsComparer.Compare(streamActual, streamExpected, TestHelper.IsRunningOnUnix, out message); + var success = ExcelDocsComparer.Compare(streamActual, streamExpected, out message); var formattedMessage = String.Format( "Actual file '{0}' is different than the expected file '{1}'. The difference is: '{2}'", @@ -108,7 +110,6 @@ fileName += ActualTestResultPostFix; fileName = Path.ChangeExtension(fileName, extension); - filePath1 = Path.Combine(directory, "z" + fileName); var filePath2 = Path.Combine(directory, fileName); using (var wb = workbookGenerator.Invoke()) @@ -121,7 +122,7 @@ using (var streamActual = File.OpenRead(filePath2)) { string message; - var success = ExcelDocsComparer.Compare(streamActual, streamExpected, TestHelper.IsRunningOnUnix, out message); + var success = ExcelDocsComparer.Compare(streamActual, streamExpected, out message); var formattedMessage = String.Format( "Actual file '{0}' is different than the expected file '{1}'. The difference is: '{2}'", diff --git a/ClosedXML_Tests/Utils/ExcelDocsComparer.cs b/ClosedXML_Tests/Utils/ExcelDocsComparer.cs index 2ad4ab2..9c35602 100644 --- a/ClosedXML_Tests/Utils/ExcelDocsComparer.cs +++ b/ClosedXML_Tests/Utils/ExcelDocsComparer.cs @@ -6,25 +6,21 @@ { internal static class ExcelDocsComparer { - public static bool Compare(string left, string right, bool stripColumnWidths, out string message) + public static bool Compare(string left, string right, out string message) { using (FileStream leftStream = File.OpenRead(left)) + using (FileStream rightStream = File.OpenRead(right)) { - using (FileStream rightStream = File.OpenRead(right)) - { - return Compare(leftStream, rightStream, stripColumnWidths, out message); - } + return Compare(leftStream, rightStream, out message); } } - public static bool Compare(Stream left, Stream right, bool stripColumnWidths, out string message) + public static bool Compare(Stream left, Stream right, out string message) { - using (Package leftPackage = Package.Open(left)) + using (Package leftPackage = Package.Open(left, FileMode.Open, FileAccess.Read)) + using (Package rightPackage = Package.Open(right, FileMode.Open, FileAccess.Read)) { - using (Package rightPackage = Package.Open(right)) - { - return PackageHelper.Compare(leftPackage, rightPackage, false, ExcludeMethod, stripColumnWidths, out message); - } + return PackageHelper.Compare(leftPackage, rightPackage, false, ExcludeMethod, out message); } } @@ -39,4 +35,4 @@ return false; } } -} \ No newline at end of file +} diff --git a/ClosedXML_Tests/Utils/PackageHelper.cs b/ClosedXML_Tests/Utils/PackageHelper.cs index 60bc482..f4f43cd 100644 --- a/ClosedXML_Tests/Utils/PackageHelper.cs +++ b/ClosedXML_Tests/Utils/PackageHelper.cs @@ -273,9 +273,9 @@ /// /// /// - public static bool Compare(Package left, Package right, bool compareToFirstDifference, bool stripColumnWidths, out string message) + public static bool Compare(Package left, Package right, bool compareToFirstDifference, out string message) { - return Compare(left, right, compareToFirstDifference, null, stripColumnWidths, out message); + return Compare(left, right, compareToFirstDifference, null, out message); } /// @@ -288,7 +288,7 @@ /// /// public static bool Compare(Package left, Package right, bool compareToFirstDifference, - Func excludeMethod, bool stripColumnWidths, out string message) + Func excludeMethod, out string message) { #region Check @@ -346,15 +346,23 @@ } var leftPart = left.GetPart(pair.Uri); var rightPart = right.GetPart(pair.Uri); - using (Stream oneStream = leftPart.GetStream(FileMode.Open, FileAccess.Read)) - using (Stream otherStream = rightPart.GetStream(FileMode.Open, FileAccess.Read)) + using (Stream leftPackagePartStream = leftPart.GetStream(FileMode.Open, FileAccess.Read)) + using (Stream rightPackagePartStream = rightPart.GetStream(FileMode.Open, FileAccess.Read)) + using (var leftMemoryStream = new MemoryStream()) + using (var rightMemoryStream = new MemoryStream()) { - bool stripColumnWidthsFromSheet = stripColumnWidths && + leftPackagePartStream.CopyTo(leftMemoryStream); + rightPackagePartStream.CopyTo(rightMemoryStream); + + leftMemoryStream.Seek(0, SeekOrigin.Begin); + rightMemoryStream.Seek(0, SeekOrigin.Begin); + + bool stripColumnWidthsFromSheet = TestHelper.StripColumnWidths && leftPart.ContentType == @"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" && rightPart.ContentType == @"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"; - var tuple1 = new Tuple(pair.Uri, oneStream); - var tuple2 = new Tuple(pair.Uri, otherStream); + var tuple1 = new Tuple(pair.Uri, leftMemoryStream); + var tuple2 = new Tuple(pair.Uri, rightMemoryStream); if (!StreamHelper.Compare(tuple1, tuple2, stripColumnWidthsFromSheet)) { diff --git a/ClosedXML_Tests/Utils/StreamHelper.cs b/ClosedXML_Tests/Utils/StreamHelper.cs index bf89911..59ef12c 100644 --- a/ClosedXML_Tests/Utils/StreamHelper.cs +++ b/ClosedXML_Tests/Utils/StreamHelper.cs @@ -147,6 +147,9 @@ foreach (var pair in uriSpecificIgnores.Where(p => p.Key.Equals(uri.OriginalString))) s = pair.Value.Replace(s, ""); + // Collapse empty xml elements + s = emptyXmlElementRegex.Replace(s, "<$1 />"); + if (ignoreColumnWidths) s = RemoveColumnWidths(s); @@ -162,6 +165,7 @@ new KeyValuePair("/docProps/core.xml", new Regex(@"", RegexOptions.Compiled)) }; + private static Regex emptyXmlElementRegex = new Regex(@"<([\w:]+)><\/\1>", RegexOptions.Compiled); private static Regex columnRegex = new Regex("", RegexOptions.Compiled); private static Regex widthRegex = new Regex("width=\"\\d+(\\.\\d+)?\"\\s+", RegexOptions.Compiled); diff --git a/ClosedXML_Tests/packages.config b/ClosedXML_Tests/packages.config deleted file mode 100644 index 5e0ab7c..0000000 --- a/ClosedXML_Tests/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index db11140..42a2dd4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,43 +1,47 @@ -version: 0.92.{build} -branches: - # whitelist - only: - - develop +version: 0.93.0.{build} - # blacklist - except: - - gh-pages -# Build worker image (VM template) -image: Visual Studio 2015 +os: Visual Studio 2017 +image: Visual Studio 2017 + environment: AppVeyor: APPVEYOR -# enable patching of AssemblyInfo.* files -assembly_info: +branches: + only: + - develop + except: + - gh-pages + +dotnet_csproj: patch: true - file: AssemblyInfo.* - assembly_version: "{version}" - assembly_file_version: "{version}" - assembly_informational_version: "{version}" + file: '**\*.csproj' + version: '{version}' + package_version: '{version}' + assembly_version: '{version}' + file_version: '{version}' + informational_version: '{version}' -#---------------------------------# -# build configuration # -#---------------------------------# - -# build platform, i.e. x86, x64, Any CPU. This setting is optional. -platform: Any CPU - -build: - parallel: true # enable MSBuild parallel builds - project: ClosedXML.sln # path to Visual Studio solution or project - verbosity: minimal - +# platform: Any CPU configuration : Release -#Restore +build: + parallel: true + project: ClosedXML.sln + verbosity: minimal + before_build: + - ps: if (Test-Path 'C:\Tools\NuGet43') { $nugetDir = 'C:\Tools\NuGet43' } else { $nugetDir = 'C:\Tools\NuGet' } + - ps: (New-Object Net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/v4.3.0/nuget.exe', "$nugetDir\NuGet.exe") + - cmd: nuget update -self - nuget restore artifacts: - - path: ClosedXML/bin/Release/ClosedXML.dll - - path: ClosedXML/bin/Release/ClosedXML.xml + - path: ClosedXML/bin/Release/netstandard2.0/ClosedXML.dll + - path: ClosedXML/bin/Release/net40/ClosedXML.dll + - path: ClosedXML/bin/Release/net46/ClosedXML.dll + +test: + assemblies: + - ClosedXML_Tests/bin/Release/netcoreapp2.0/ClosedXML_Tests.dll + - ClosedXML_Tests/bin/Release/net40/ClosedXML_Tests.dll + - ClosedXML_Tests/bin/Release/net46/ClosedXML_Tests.dll