diff --git a/ClosedXML/ClosedXML/ClosedXML/ClosedXML.csproj b/ClosedXML/ClosedXML/ClosedXML/ClosedXML.csproj index 3e55efc..7f22e1d 100644 --- a/ClosedXML/ClosedXML/ClosedXML/ClosedXML.csproj +++ b/ClosedXML/ClosedXML/ClosedXML/ClosedXML.csproj @@ -138,6 +138,7 @@ + diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Cells/XLCell.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Cells/XLCell.cs index 09c6e3f..e7a6c06 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/Cells/XLCell.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Cells/XLCell.cs @@ -2062,19 +2062,19 @@ if (row1String[0] == '$') { row1 = "$" + - (Int32.Parse(row1String.Substring(1)) + rowsShifted).ToStringLookup(); + (Int32.Parse(row1String.Substring(1)) + rowsShifted).ToInvariantString(); } else - row1 = (Int32.Parse(row1String) + rowsShifted).ToStringLookup(); + row1 = (Int32.Parse(row1String) + rowsShifted).ToInvariantString(); string row2; if (row2String[0] == '$') { row2 = "$" + - (Int32.Parse(row2String.Substring(1)) + rowsShifted).ToStringLookup(); + (Int32.Parse(row2String.Substring(1)) + rowsShifted).ToInvariantString(); } else - row2 = (Int32.Parse(row2String) + rowsShifted).ToStringLookup(); + row2 = (Int32.Parse(row2String) + rowsShifted).ToInvariantString(); sb.Append(useSheetName ? String.Format("'{0}'!{1}:{2}", sheetName, row1, row2) diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Coordinates/XLAddress.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Coordinates/XLAddress.cs index 8d0f040..d9b9a8e 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/Coordinates/XLAddress.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Coordinates/XLAddress.cs @@ -207,7 +207,7 @@ { retVal += "$"; } - retVal += _rowNumber.ToStringLookup(); + retVal += _rowNumber.ToInvariantString(); return retVal; } @@ -215,23 +215,23 @@ { if (referenceStyle == XLReferenceStyle.A1) { - return ColumnLetter + _rowNumber.ToStringLookup(); + return ColumnLetter + _rowNumber.ToInvariantString(); } if (referenceStyle == XLReferenceStyle.R1C1) { - return String.Format("R{0}C{1}", _rowNumber.ToStringLookup(), ColumnNumber); + return String.Format("R{0}C{1}", _rowNumber.ToInvariantString(), ColumnNumber); } if (HasWorksheet && Worksheet.Workbook.ReferenceStyle == XLReferenceStyle.R1C1) { - return String.Format("R{0}C{1}", _rowNumber.ToStringLookup(), ColumnNumber); + return String.Format("R{0}C{1}", _rowNumber.ToInvariantString(), ColumnNumber); } - return ColumnLetter + _rowNumber.ToStringLookup(); + return ColumnLetter + _rowNumber.ToInvariantString(); } #endregion #region Methods public string GetTrimmedAddress() { - return _trimmedAddress ?? (_trimmedAddress = ColumnLetter + _rowNumber.ToStringLookup()); + return _trimmedAddress ?? (_trimmedAddress = ColumnLetter + _rowNumber.ToInvariantString()); } @@ -362,13 +362,13 @@ { String address; if (referenceStyle == XLReferenceStyle.A1) - address = String.Format("${0}${1}", ColumnLetter, _rowNumber.ToStringLookup()); + address = String.Format("${0}${1}", ColumnLetter, _rowNumber.ToInvariantString()); else if (referenceStyle == XLReferenceStyle.R1C1) - address = String.Format("R{0}C{1}", _rowNumber.ToStringLookup(), ColumnNumber); + address = String.Format("R{0}C{1}", _rowNumber.ToInvariantString(), ColumnNumber); else if (HasWorksheet && Worksheet.Workbook.ReferenceStyle == XLReferenceStyle.R1C1) - address = String.Format("R{0}C{1}", _rowNumber.ToStringLookup(), ColumnNumber); + address = String.Format("R{0}C{1}", _rowNumber.ToInvariantString(), ColumnNumber); else - address = String.Format("${0}${1}", ColumnLetter, _rowNumber.ToStringLookup()); + address = String.Format("${0}${1}", ColumnLetter, _rowNumber.ToInvariantString()); if (includeSheet) return String.Format("'{0}'!{1}", diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Ranges/XLRangeAddress.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Ranges/XLRangeAddress.cs index 1a02e91..d5b0e94 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/Ranges/XLRangeAddress.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Ranges/XLRangeAddress.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Globalization; using System.Linq; namespace ClosedXML.Excel @@ -64,7 +65,7 @@ else { FirstAddress = XLAddress.Create(worksheet, firstPart + "1"); - LastAddress = XLAddress.Create(worksheet, secondPart + XLHelper.MaxRowNumber.ToStringLookup()); + LastAddress = XLAddress.Create(worksheet, secondPart + XLHelper.MaxRowNumber.ToString(CultureInfo.InvariantCulture)); } } diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Style/Colors/XLColor_Static.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Style/Colors/XLColor_Static.cs index dfaaaa7..d66e191 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/Style/Colors/XLColor_Static.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Style/Colors/XLColor_Static.cs @@ -7,14 +7,19 @@ { public partial class XLColor { - private static Dictionary byColor = new Dictionary(); + private static readonly Dictionary ByColor = new Dictionary(); + private static readonly Object ByColorLock = new Object(); + public static XLColor FromColor(Color color) { XLColor ret; - if (!byColor.TryGetValue(color, out ret)) + lock (ByColorLock) { - ret = new XLColor(color); - byColor.Add(color, ret); + if (!ByColor.TryGetValue(color, out ret)) + { + ret = new XLColor(color); + ByColor.Add(color, ret); + } } return ret; } @@ -44,50 +49,65 @@ return FromColor(ColorTranslator.FromHtml(htmlColor)); } - private static Dictionary byIndex = new Dictionary(); + private static readonly Dictionary ByIndex = new Dictionary(); + private static readonly Object ByIndexLock = new Object(); + public static XLColor FromIndex(Int32 index) { XLColor ret; - if (!byIndex.TryGetValue(index, out ret)) + lock (ByIndexLock) { - ret = new XLColor(index); - byIndex.Add(index, ret); + if (!ByIndex.TryGetValue(index, out ret)) + { + ret = new XLColor(index); + ByIndex.Add(index, ret); + } } return ret; } - private static Dictionary byTheme = new Dictionary(); + private static readonly Dictionary ByTheme = new Dictionary(); + private static readonly Object ByThemeLock = new Object(); + public static XLColor FromTheme(XLThemeColor themeColor) { XLColor ret; - if (!byTheme.TryGetValue(themeColor, out ret)) + lock (ByThemeLock) { - ret = new XLColor(themeColor); - byTheme.Add(themeColor, ret); + if (!ByTheme.TryGetValue(themeColor, out ret)) + { + ret = new XLColor(themeColor); + ByTheme.Add(themeColor, ret); + } } return ret; } - private static Dictionary> byThemeTint = new Dictionary>(); + private static readonly Dictionary> ByThemeTint = new Dictionary>(); + private static readonly Object ByThemeTintLock = new Object(); + public static XLColor FromTheme(XLThemeColor themeColor, Double themeTint) { XLColor ret; - Dictionary themeTints; - if (byThemeTint.TryGetValue(themeColor, out themeTints)) + lock (ByThemeTintLock) { - if (!themeTints.TryGetValue(themeTint, out ret)) + Dictionary themeTints; + if (ByThemeTint.TryGetValue(themeColor, out themeTints)) { + if (!themeTints.TryGetValue(themeTint, out ret)) + { + ret = new XLColor(themeColor, themeTint); + themeTints.Add(themeTint, ret); + } + } + else + { + themeTints = new Dictionary(); ret = new XLColor(themeColor, themeTint); themeTints.Add(themeTint, ret); - } - } - else - { - themeTints = new Dictionary(); - ret = new XLColor(themeColor, themeTint); - themeTints.Add(themeTint, ret); - byThemeTint.Add(themeColor, themeTints); + ByThemeTint.Add(themeColor, themeTints); + } } return ret; } diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Tables/XLTable.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Tables/XLTable.cs index 112221e..a090dfc 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/Tables/XLTable.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Tables/XLTable.cs @@ -396,7 +396,7 @@ foreach (IXLCell c in range.Row(1).Cells()) { if (XLHelper.IsNullOrWhiteSpace(((XLCell)c).InnerText)) - c.Value = GetUniqueName("Column" + co.ToStringLookup()); + c.Value = GetUniqueName("Column" + co.ToInvariantString()); _uniqueNames.Add(c.GetString()); co++; } @@ -410,11 +410,11 @@ if (_uniqueNames.Contains(name)) { Int32 i = 1; - name = originalName + i.ToStringLookup(); + name = originalName + i.ToInvariantString(); while (_uniqueNames.Contains(name)) { i++; - name = originalName + i.ToStringLookup(); + name = originalName + i.ToInvariantString(); } } @@ -446,7 +446,7 @@ foreach (IXLCell c in headersRow.Cells()) { if (XLHelper.IsNullOrWhiteSpace(((XLCell)c).InnerText)) - c.Value = GetUniqueName("Column" + co.ToStringLookup()); + c.Value = GetUniqueName("Column" + co.ToInvariantString()); _uniqueNames.Add(c.GetString()); co++; } diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Save.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Save.cs index 21072b9..70ef083 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Save.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Save.cs @@ -1580,11 +1580,11 @@ if (context.TableNames.Contains(name)) { var i = 1; - name = tableName + i.ToStringLookup(); + name = tableName + i.ToInvariantString(); while (context.TableNames.Contains(name)) { i++; - name = tableName + i.ToStringLookup(); + name = tableName + i.ToInvariantString(); } } @@ -3282,7 +3282,7 @@ maxColumn = xlWorksheet.Internals.CellsCollection.MaxColumnUsed; var maxRow = xlWorksheet.Internals.CellsCollection.MaxRowUsed; sheetDimensionReference = "A1:" + XLHelper.GetColumnLetterFromNumber(maxColumn) + - maxRow.ToStringLookup(); + maxRow.ToInvariantString(); } if (xlWorksheet.Internals.ColumnsCollection.Count > 0) @@ -3637,7 +3637,7 @@ } if (maxColumn > 0) - row.Spans = new ListValue {InnerText = "1:" + maxColumn.ToStringLookup()}; + row.Spans = new ListValue {InnerText = "1:" + maxColumn.ToInvariantString()}; row.Height = null; row.CustomHeight = null; @@ -3679,7 +3679,7 @@ { foreach (var delCo in kpDel.Value.ToList()) { - var key = XLHelper.GetColumnLetterFromNumber(delCo) + kpDel.Key.ToStringLookup(); + var key = XLHelper.GetColumnLetterFromNumber(delCo) + kpDel.Key.ToInvariantString(); if (!cellsByReference.ContainsKey(key)) continue; row.RemoveChild(cellsByReference[key]); kpDel.Value.Remove(delCo); diff --git a/ClosedXML/ClosedXML/ClosedXML/Extensions.cs b/ClosedXML/ClosedXML/ClosedXML/Extensions.cs index cac055e..dbc2e57 100644 --- a/ClosedXML/ClosedXML/ClosedXML/Extensions.cs +++ b/ClosedXML/ClosedXML/ClosedXML/Extensions.cs @@ -9,6 +9,7 @@ using System.Xml.Linq; using System.IO; using System.Xml; +using ClosedXML.Utils; [assembly: CLSCompliantAttribute(true)] namespace ClosedXML.Excel @@ -162,18 +163,12 @@ public static class IntegerExtensions { - private static readonly NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat; - private static readonly Dictionary intToString = new Dictionary(); - public static String ToStringLookup(this Int32 value) + + public static String ToInvariantString(this Int32 value) { - if (!intToString.ContainsKey(value)) - { - intToString.Add(value, value.ToString(nfi)); - } - return intToString[value]; + return value.ToString(CultureInfo.InvariantCulture); } - } public static class FontBaseExtensions @@ -190,7 +185,6 @@ return font; } - private static Graphics graphics = Graphics.FromImage(new Bitmap(1, 1)); public static Double GetWidth(this IXLFontBase fontBase, String text, Dictionary fontCache) { if (XLHelper.IsNullOrWhiteSpace(text)) @@ -198,7 +192,7 @@ var font = GetCachedFont(fontBase, fontCache); - var textSize = graphics.MeasureString(text, font, Int32.MaxValue, StringFormat.GenericTypographic); + var textSize = GraphicsUtils.MeasureString(text, font); double width = (((textSize.Width / (double)7) * 256) - (128 / 7)) / 256; width = (double)decimal.Round((decimal)width + 0.2M, 2); @@ -219,7 +213,7 @@ public static Double GetHeight(this IXLFontBase fontBase, Dictionary fontCache) { var font = GetCachedFont(fontBase, fontCache); - var textSize = graphics.MeasureString("X", font, Int32.MaxValue, StringFormat.GenericTypographic); + var textSize = GraphicsUtils.MeasureString("X", font); return (double)textSize.Height * 0.85; } diff --git a/ClosedXML/ClosedXML/ClosedXML/Utils/GraphicsUtils.cs b/ClosedXML/ClosedXML/ClosedXML/Utils/GraphicsUtils.cs new file mode 100644 index 0000000..954c22c --- /dev/null +++ b/ClosedXML/ClosedXML/ClosedXML/Utils/GraphicsUtils.cs @@ -0,0 +1,22 @@ +using System; +using System.Drawing; + +namespace ClosedXML.Utils +{ + internal static class GraphicsUtils + { + public static SizeF MeasureString(string s, Font font) + { + SizeF result; + using (var image = new Bitmap(1, 1)) + { + using (var g = Graphics.FromImage(image)) + { + result = g.MeasureString(s, font, Int32.MaxValue, StringFormat.GenericTypographic); + } + } + + return result; + } + } +} diff --git a/ClosedXML/ClosedXML/ClosedXML/XLHelper.cs b/ClosedXML/ClosedXML/ClosedXML/XLHelper.cs index 6bb853c..8f5f073 100644 --- a/ClosedXML/ClosedXML/ClosedXML/XLHelper.cs +++ b/ClosedXML/ClosedXML/ClosedXML/XLHelper.cs @@ -54,85 +54,52 @@ RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture ); - private static Dictionary columnNumbers = new Dictionary(); /// - /// Gets the column number of a given column letter. + /// Gets the column number of a given column letter. /// /// The column letter to translate into a column number. public static int GetColumnNumberFromLetter(string columnLetter) { + if (string.IsNullOrEmpty(columnLetter)) throw new ArgumentNullException("columnLetter"); + int retVal; columnLetter = columnLetter.ToUpper(); - if (columnNumbers.TryGetValue(columnLetter, out retVal)) - return retVal; + //Extra check because we allow users to pass row col positions in as strings if (columnLetter[0] <= '9') { retVal = Int32.Parse(columnLetter, NumberFormatForParse); - columnNumbers.Add(columnLetter, retVal); return retVal; } - - var length = columnLetter.Length; - if (length == 1) + + int sum = 0; + + for (int i = 0; i < columnLetter.Length; i++) { - retVal = Convert.ToByte(columnLetter[0]) - 64; - columnNumbers.Add(columnLetter, retVal); - return retVal; + sum *= 26; + sum += (columnLetter[i] - 'A' + 1); } - if (length == 2) - { - retVal = - ((Convert.ToByte(columnLetter[0]) - 64)*26) + - (Convert.ToByte(columnLetter[1]) - 64); - columnNumbers.Add(columnLetter, retVal); - return retVal; - } - if (length == 3) - { - retVal = ((Convert.ToByte(columnLetter[0]) - 64)*TwoT26) + - ((Convert.ToByte(columnLetter[1]) - 64)*26) + - (Convert.ToByte(columnLetter[2]) - 64); - columnNumbers.Add(columnLetter, retVal); - return retVal; - } - throw new ApplicationException("Column Length must be between 1 and 3."); + + return sum; } - private static Dictionary columnLetters = new Dictionary(); /// /// Gets the column letter of a given column number. /// - /// The column number to translate into a column letter. + /// The column number to translate into a column letter. public static string GetColumnLetterFromNumber(int columnNumber) { - String retVal; - if (columnLetters.TryGetValue(columnNumber, out retVal)) - return retVal; + var dividend = columnNumber; + var columnName = String.Empty; - #region Check - - if (columnNumber <= 0) - throw new ArgumentOutOfRangeException("column", "Must be more than 0"); - - #endregion - - var value = new StringBuilder(6); - int column = columnNumber; - while (column > 0) + while (dividend > 0) { - var residue = column%26; - column /= 26; - if (residue == 0) - { - residue = 26; - column--; - } - value.Insert(0, (char) (64 + residue)); + var modulo = (dividend - 1) % 26; + columnName = Convert.ToChar(65 + modulo) + columnName; + dividend = (dividend - modulo) / 26; } - retVal = value.ToString(); - columnLetters.Add(columnNumber, retVal); - return retVal; + + return columnName; } public static bool IsValidColumn(string column) diff --git a/ClosedXML/ClosedXML/ClosedXML_Net3.5/ClosedXML_Net3.5.csproj b/ClosedXML/ClosedXML/ClosedXML_Net3.5/ClosedXML_Net3.5.csproj index b73f954..b41afcb 100644 --- a/ClosedXML/ClosedXML/ClosedXML_Net3.5/ClosedXML_Net3.5.csproj +++ b/ClosedXML/ClosedXML/ClosedXML_Net3.5/ClosedXML_Net3.5.csproj @@ -876,6 +876,9 @@ PathHelper.cs + + Utils\GraphicsUtils.cs + XLHelper.cs