using FastReport.Format;
using FastReport.Utils;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Text;
namespace FastReport.Export
{
///
/// For internal use only.
///
public static class ExportUtils
{
internal const string XCONV = "0123456789ABCDEF";
private const int BASE = 65521;
private const int NMAX = 5552;
internal enum CRLF
{
html,
xml,
odt
};
///
/// Gets current page width.
///
///
///
public static float GetPageWidth(ReportPage page)
{
if (page.UnlimitedWidth)
return page.UnlimitedWidthValue / Units.Millimeters;
else
return page.PaperWidth;
}
///
/// Gets current page height.
///
///
///
public static float GetPageHeight(ReportPage page)
{
if (page.UnlimitedHeight)
return page.UnlimitedHeightValue / Units.Millimeters;
else
return page.PaperHeight;
}
private static readonly NumberFormatInfo _provider;
public static string FloatToString(double value, int digits = 2)
{
return Convert.ToString(Math.Round(value, digits), _provider);
}
internal static bool ParseTextToDecimal(string text, FormatBase format, out decimal value)
{
value = 0;
if (format is NumberFormat)
{
return decimal.TryParse(text, NumberStyles.Number, (format as NumberFormat).GetNumberFormatInfo(), out value);
}
else if (format is CurrencyFormat)
{
return decimal.TryParse(text, NumberStyles.Currency, (format as CurrencyFormat).GetNumberFormatInfo(), out value);
}
else if (format is CustomFormat && !(format is DateFormat))
{
return decimal.TryParse(text, out value);
}
return false;
}
internal static bool ParseTextToDateTime(string text, FormatBase format, out DateTime value)
{
value = DateTime.MinValue;
if (format is DateFormat)
return DateTime.TryParse(text, CultureInfo.CurrentCulture.DateTimeFormat, DateTimeStyles.None, out value);
return false;
}
internal static bool ParseTextToPercent(string text, FormatBase format, out decimal value)
{
value = 0;
if (format is PercentFormat)
{
bool returned = decimal.TryParse(text.Replace("%", ""), out value);
value /= 100;
return returned;
}
return false;
}
internal static string GetExcelFormatSpecifier(FormatBase format, bool useLocale = false, bool currencyToAccounting = false)
{
if (format is CurrencyFormat)
{
NumberFormatInfo f = (format as CurrencyFormat).GetNumberFormatInfo();
if (useLocale)
{
(format as CurrencyFormat).UseLocale = true;
f = (format as CurrencyFormat).GetNumberFormatInfo();
f.CurrencyDecimalDigits = CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalDigits;
}
string fm_str = "#,##0";
if (f.CurrencyDecimalDigits > 0)
{
fm_str += ".";// f.DecimalSeparator;
for (int i = 0; i < f.CurrencyDecimalDigits; i++)
fm_str += "0";
}
string currency_symbol = """ + f.CurrencySymbol + """;
string positive_pattern = "";
string negative_pattern = "";
switch (f.CurrencyPositivePattern)
{
case 0: positive_pattern = currency_symbol + fm_str; break; // $n
case 1: positive_pattern = fm_str + currency_symbol; break; // n$
case 2: positive_pattern = currency_symbol + " " + fm_str; break; // $ n
case 3: positive_pattern = fm_str + " " + currency_symbol; break; // n $
}
switch (f.CurrencyNegativePattern)
{
case 0: negative_pattern = "(" + currency_symbol + fm_str + ")"; break; // ($n)
case 1: negative_pattern = "-" + currency_symbol + fm_str; break; // -$n
case 2: negative_pattern = currency_symbol + "-" + fm_str; break; // $-n
case 3: negative_pattern = currency_symbol + fm_str + "-"; break; // $n-
case 4: negative_pattern = "(" + currency_symbol + fm_str + ")"; break; // (n$)
case 5: negative_pattern = "-" + fm_str + currency_symbol; break; // -n$
case 6: negative_pattern = fm_str + "-" + currency_symbol; break; // n-$
case 7: negative_pattern = fm_str + currency_symbol + "-"; break; // n$-
case 8: negative_pattern = "-" + fm_str + " " + currency_symbol; break; // -n $
case 9: negative_pattern = "-" + currency_symbol + " " + fm_str; break; // -$ n
case 10: negative_pattern = fm_str + " " + currency_symbol + "-"; break; // n $-
case 11: negative_pattern = currency_symbol + " " + fm_str + "-"; break; // $ n-
case 12: negative_pattern = currency_symbol + " -" + fm_str; break; // $ -n
case 13: negative_pattern = fm_str + "- " + currency_symbol; break; // n- $
case 14: negative_pattern = "(" + currency_symbol + " " + fm_str + ")"; break; // ($ n)
case 15: negative_pattern = "(" + fm_str + " " + currency_symbol + ")"; break; // (n $)
}
if (currencyToAccounting)
{
string financeSymbol = "";
switch (f.CurrencySymbol)
{
case "₽": financeSymbol = "[$₽-419]"; break;
case "$": financeSymbol = "[$$-409]"; break;
case "\u20AC": financeSymbol = "[$€-2]"; break;
case "\u00A3": financeSymbol = "[$£-809]"; break;
case "\u20B9": financeSymbol = "[$₹-4009]"; break;
}
switch (f.CurrencyPositivePattern)
{
case 0: positive_pattern = @"_-* " + financeSymbol + fm_str + "_-;"; break; // $n
case 1: positive_pattern = @"_-* " + fm_str + financeSymbol + "_-;"; break; // n$
case 2: positive_pattern = @"_-* " + financeSymbol + " " + fm_str + "_-;"; break; // $ n
case 3: positive_pattern = @"_-* " + fm_str + " "+ financeSymbol + "_-;"; break; // n $
}
switch (f.CurrencyNegativePattern)
{
case 0: negative_pattern = @"_-* " + "(" + financeSymbol + fm_str + ")" + "_-;"; break; // ($n)
case 1: negative_pattern = @"_-* " + "-" + financeSymbol + fm_str + "_-;"; break; // -$n
case 2: negative_pattern = @"_-* " + financeSymbol + "-" + fm_str + "_-;"; break; // $-n
case 3: negative_pattern = @"_-* " + financeSymbol + fm_str + "-" + "_-;"; break; // $n-
case 4: negative_pattern = @"_-* " + "(" + fm_str + financeSymbol + ")" + "_-;"; break; // (n$)
case 5: negative_pattern = @"_-* " + "-" + fm_str + financeSymbol + "_-;"; break; // -n$
case 6: negative_pattern = @"_-* " + fm_str + "-" + financeSymbol + "_-;"; break; // n-$
case 7: negative_pattern = @"_-* " + fm_str + financeSymbol + "-" + "_-;"; break; // n$-
case 8: negative_pattern = @"_-* " + "-" + fm_str + " " + financeSymbol + "_-;"; break; // -n $
case 9: negative_pattern = @"_-* " + "-" + financeSymbol + " " + fm_str + "_-;"; break; // -$ n
case 10: negative_pattern = @"_-* " + fm_str + " " + financeSymbol + "-" + "_-;"; break; // n $-
case 11: negative_pattern = @"_-* " + financeSymbol + " " + fm_str + "-" + "_-;"; break; // $ n-
case 12: negative_pattern = @"_-* " + financeSymbol + " " + "-" + fm_str + "_-;"; break; // $ -n
case 13: negative_pattern = @"_-* " + fm_str + "- " + financeSymbol + "_-;"; break; // n- $
case 14: negative_pattern = @"_-* " + "(" + financeSymbol + " " + fm_str + ")" + "_-;"; break; // ($ n)
case 15: negative_pattern = @"_-* " + "(" + fm_str + " " + financeSymbol + ")" + "_-;"; break; // (n $)
}
string financeFormat_Options = @"_-* "-"??\ " + financeSymbol + "_-;_-@_-";
return positive_pattern + negative_pattern + financeFormat_Options;
}
return positive_pattern + ";" + negative_pattern;
}
else if (format is NumberFormat)
{
NumberFormatInfo f = (format as NumberFormat).GetNumberFormatInfo();
if (useLocale)
{
(format as NumberFormat).UseLocale = true;
f = (format as NumberFormat).GetNumberFormatInfo();
f.CurrencyDecimalDigits = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalDigits;
}
string fm_str = "#,##0";
if (f.NumberDecimalDigits > 0)
{
fm_str += ".";// f.DecimalSeparator;
for (int i = 0; i < f.NumberDecimalDigits; i++)
fm_str += "0";
}
string positive_pattern = "";
string negative_pattern = "";
positive_pattern = fm_str;
switch (f.NumberNegativePattern)
{
case 0: negative_pattern = "(" + fm_str + ")"; break; // (n)
case 1: negative_pattern = "-" + fm_str; break; // -n
case 2: negative_pattern = "- " + fm_str; break; // - n
case 3: negative_pattern = fm_str + "-"; break; // n-
case 4: negative_pattern = fm_str + " -"; break; // n -
}
return positive_pattern + ";" + negative_pattern;
}
else if (format is DateFormat)
{
string parentalCase = CultureInfo.CurrentCulture.TwoLetterISOLanguageName == "ru" ? "[$-FC19]" : "";
switch ((format as DateFormat).Format)
{
case "d": return DateTimeFormatInfo.CurrentInfo.ShortDatePattern + ";@";
case "D": return "[$-F800]" + DateTimeFormatInfo.CurrentInfo.LongDatePattern.Replace("tt", "AM/PM") + ";@";
case "f": return parentalCase + (DateTimeFormatInfo.CurrentInfo.LongDatePattern + " " + DateTimeFormatInfo.CurrentInfo.ShortTimePattern).Replace("tt", "AM/PM") + ";@";
case "F": return parentalCase + DateTimeFormatInfo.CurrentInfo.FullDateTimePattern.Replace("tt", "AM/PM") + ";@";
case "MMMM yyyy": return (format as DateFormat).Format + ";@";
default: return parentalCase + (format as DateFormat).Format.Replace("tt", "AM/PM") + ";@";
}
}
else if (format is PercentFormat)
{
string pattern = "0";
if (useLocale)
{
(format as PercentFormat).UseLocale = true;
(format as PercentFormat).DecimalDigits = CultureInfo.CurrentCulture.NumberFormat.PercentDecimalDigits;
}
if ((format as PercentFormat).DecimalDigits > 0)
{
pattern += ".";
for (int i = 0; i < (format as PercentFormat).DecimalDigits; i++)
pattern += "0";
}
return pattern + "%";
}
return "";
}
internal static string HTMLColor(Color color)
{
return ColorTranslator.ToHtml(color);
}
internal static string HTMLColorCode(Color color)
{
return String.Join(String.Empty, new String[] {
"#",
color.R.ToString("X2"),
color.G.ToString("X2"),
color.B.ToString("X2")
});
}
internal static string ByteToHex(byte Byte)
{
char[] s = new char[2];
s[0] = XCONV[(Byte >> 4)];
s[1] = XCONV[(Byte & 0xF)];
return new String(s);
}
internal static string UInt16Tohex(UInt16 word)
{
FastString sb = new FastString(4);
return sb.Append(ByteToHex((byte)((word >> 8) & 0xFF))).Append(ByteToHex((byte)(word & 0xFF))).ToString();
}
internal static string TruncReturns(string Str)
{
int l;
l = Str.Length;
if ((l > 1) && (Str.Substring(l - 2, 2) == "\r\n"))
return Str.Substring(0, l - 2);
else
return Str;
}
internal static FastString HtmlString(string text)
{
return HtmlString(text, TextRenderType.Default);
}
internal static FastString HtmlString(string text, TextRenderType textRenderType, CRLF crlf, bool excel2007, string fontSize = "13px;")
{
FastString Result = new FastString(text.Length);
int len = text.Length;
int lineBreakCount = 0;
if (textRenderType == TextRenderType.HtmlTags)
{
const string wingdings = "";
const string webdings = "";
int ind1 = 0, ind2 = 0;
if (text.Contains(wingdings))
{
ind1 = text.IndexOf(wingdings) + wingdings.Length;
ind2 = text.IndexOf('<', ind1);
text = text.Substring(0, ind1) +
WingdingsToUnicodeConverter.Convert(text.Substring(ind1, ind2 - ind1)) +
text.Substring(ind2, text.Length - ind2);
}
else if (text.Contains(webdings))
{
ind1 = text.IndexOf(webdings) + webdings.Length;
ind2 = text.IndexOf('<', ind1);
text = text.Substring(0, ind1) +
WingdingsToUnicodeConverter.Convert(text.Substring(ind1, ind2 - ind1)) +
text.Substring(ind2, text.Length - ind2);
}
}
for (int i = 0; i < len; i++)
{
if (crlf != CRLF.xml && crlf != CRLF.odt && text[i] == ' ' && (text.Length == 1 ||
(i < (len - 1) && text[i + 1] == ' ') ||
(i > 0 && text[i - 1] == ' ')
|| i == len - 1))
{
Result.Append(" ");
}
else if (text[i] == '<' && textRenderType == TextRenderType.HtmlTags && crlf == CRLF.odt)
i += text.IndexOf('>', i) - i;
else if (i < text.Length - 1 && text[i] == '\r' && text[i + 1] == '\n')
{
if (crlf == CRLF.xml)
Result.Append("
");
else if (crlf == CRLF.odt)
Result.Append("");
else
{
if (lineBreakCount == 0)
Result.Append("");
else
Result.Append($"");
lineBreakCount++;
}
i++;
}
else
{
lineBreakCount = 0;
if (text[i] == '\t' && crlf == CRLF.odt)
Result.Append("");
else if (text[i] == ' ' && crlf == CRLF.odt)
{
int spaces = 1;
while (i < text.Length - 1)
{
if (text[i + 1] == ' ')
{
i++;
spaces++;
}
else
break;
}
Result.Append("");
}
else if (text[i] == '\\')
Result.Append("\");
else if (text[i] == '~' && !excel2007)
Result.Append("˜");
else if (text[i] == '€' && !excel2007)
Result.Append("€");
else if (text[i] == '‹' && !excel2007)
Result.Append("‹");
else if (text[i] == '›' && !excel2007)
Result.Append("›");
else if (text[i] == 'ˆ' && !excel2007)
Result.Append("ˆ");
else if (text[i] == '&' && textRenderType == TextRenderType.Default)
Result.Append("&");
else if (text[i] == '"' && textRenderType == TextRenderType.Default)
Result.Append(""");
else if (text[i] == '<' && textRenderType == TextRenderType.Default)
Result.Append("<");
else if (text[i] == '>' && textRenderType == TextRenderType.Default)
Result.Append(">");
else if (text[i] == '\t' && excel2007)
continue;
else
Result.Append(text[i]);
}
}
return Result;
}
internal static string QuotedPrintable(byte[] Values)
{
FastString sb = new FastString((int)(Values.Length * 1.3));
int length = 0;
foreach (byte c in Values)
{
if (length > 73)
{
length = 0;
sb.Append("=").AppendLine();
}
if (c < 9 || c == 61 || c > 126)
{
sb.Append("=").Append(XCONV[(c >> 4)].ToString()).Append(XCONV[(c & 0xF)].ToString());
length += 3;
}
else
{
sb.Append((char)c);
length++;
}
}
return sb.ToString();
}
public static FastString HtmlString(string text, TextRenderType textRenderType, string fontSize = "13px;")
{
return HtmlString(text, textRenderType, CRLF.html, false, fontSize);
}
internal static string XmlString(string Str, TextRenderType textRenderType)
{
return HtmlString(Str, textRenderType, CRLF.xml, false).ToString();
}
internal static string Excel2007String(string Str, TextRenderType textRenderType)
{
return HtmlString(Str, textRenderType, CRLF.xml, true).ToString();
}
internal static string OdtString(string Str, TextRenderType textRenderType)
{
return HtmlString(Str, textRenderType, CRLF.odt, false).ToString();
}
///
///
///
///
///
internal static string HtmlURL(string Value)
{
FastString Result = new FastString();
#if DOTNET_4
foreach (char c in Value)
{
if (!char.IsLetterOrDigit(c) && !char.IsPunctuation(c))
Result.Append("%").Append(Convert.ToInt32(c).ToString("x"));
else
Result.Append(c);
}
#else
for (int i = 0; i < Value.Length; i++)
{
switch (Value[i])
{
case '\\':
Result.Append("/");
break;
case '&':
case '<':
case '>':
case '{':
case '}':
case ';':
case '?':
case ' ':
case '\'':
case '"':
Result.Append("%" + ExportUtils.ByteToHex((byte)Value[i]));
break;
default:
Result.Append(Value[i]);
break;
}
}
#endif
return Result.ToString();
}
internal static void Write(Stream stream, string value)
{
byte[] buf = Encoding.UTF8.GetBytes(value);
stream.Write(buf, 0, buf.Length);
}
internal static void WriteLn(Stream stream, string value)
{
byte[] buf = Encoding.UTF8.GetBytes(value);
stream.Write(buf, 0, buf.Length);
stream.WriteByte(13);
stream.WriteByte(10);
}
internal static void Write(Stream stream, byte value)
{
stream.WriteByte(value);
}
internal static void Write(Stream stream, StringBuilder value)
{
byte[] buf = Encoding.UTF8.GetBytes(value.ToString());
stream.Write(buf, 0, buf.Length);
}
internal static void WriteLn(Stream stream, StringBuilder value)
{
byte[] buf = Encoding.UTF8.GetBytes(value.ToString());
stream.Write(buf, 0, buf.Length);
stream.WriteByte(13);
stream.WriteByte(10);
}
internal static void ZLibDeflate(Stream src, Stream dst)
{
dst.WriteByte(0x78);
dst.WriteByte(0xDA);
src.Position = 0;
long adler = 1L;
using (DeflateStream compressor = new DeflateStream(dst, CompressionMode.Compress, true))
{
const int bufflength = 2048;
byte[] buff = new byte[bufflength];
int i;
while ((i = src.Read(buff, 0, bufflength)) > 0)
{
adler = Adler32(adler, buff, 0, i);
compressor.Write(buff, 0, i);
}
}
dst.WriteByte((byte)(adler >> 24 & 0xFF));
dst.WriteByte((byte)(adler >> 16 & 0xFF));
dst.WriteByte((byte)(adler >> 8 & 0xFF));
dst.WriteByte((byte)(adler & 0xFF));
}
internal static long Adler32(long adler, byte[] buf, int index, int len)
{
if (buf == null) { return 1L; }
long s1 = adler & 0xffff;
long s2 = (adler >> 16) & 0xffff;
int k;
while (len > 0)
{
k = len < NMAX ? len : NMAX;
len -= k;
while (k >= 16)
{
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
s1 += buf[index++] & 0xff; s2 += s1;
k -= 16;
}
if (k != 0)
{
do
{
s1 += buf[index++] & 0xff;
s2 += s1;
}
while (--k != 0);
}
s1 %= BASE;
s2 %= BASE;
}
return (s2 << 16) | s1;
}
internal static string ReverseString(string str)
{
FastString result = new FastString(str.Length);
int i, j;
for (j = 0, i = str.Length - 1; i >= 0; i--, j++)
result.Append(str[i]);
return result.ToString();
}
internal static string StrToHex(string s)
{
FastString sb = new FastString(s.Length * 2);
foreach (char c in s)
sb.Append(((byte)c).ToString("X2"));
return sb.ToString();
}
internal static FastString StrToHex2(string s)
{
FastString sb = new FastString(s.Length * 2);
foreach (char c in s)
sb.Append(((UInt16)c).ToString("X4"));
return sb;
}
internal static string GetID()
{
return SystemFake.Guid.NewGuid().ToString();
}
internal static byte[] StringToByteArray(string source)
{
byte[] result = new byte[source.Length];
for (int i = 0; i < source.Length; i++)
result[i] = (byte)source[i];
return result;
}
internal static byte[] StringTo2ByteArray(string source)
{
byte[] result = new byte[source.Length * 2];
for (int i = 0; i < source.Length; i++)
{
result[i] = (byte)(source[i] >> 8);
result[i + 1] = (byte)source[i];
}
return result;
}
internal static string StringFromByteArray(byte[] array)
{
FastString result = new FastString(array.Length);
foreach (byte b in array)
result.Append((char)b);
return result.ToString();
}
internal static Color GetColorFromFill(FillBase Fill)
{
if (Fill is SolidFill)
return (Fill as SolidFill).Color;
else if (Fill is GlassFill)
return (Fill as GlassFill).Color;
else if (Fill is HatchFill)
return (Fill as HatchFill).BackColor;
else if (Fill is PathGradientFill)
return (Fill as PathGradientFill).CenterColor;
else if (Fill is LinearGradientFill)
return GetMiddleColor((Fill as LinearGradientFill).StartColor, (Fill as LinearGradientFill).EndColor);
else
return Color.White;
}
private static Color GetMiddleColor(Color color1, Color color2)
{
return Color.FromArgb(255,
(color1.R + color2.R) / 2,
(color1.G + color2.G) / 2,
(color1.B + color2.B) / 2);
}
internal static string GetRFCDate(DateTime datetime)
{
FastString sb = new FastString();
sb.AppendFormat("{0:R}", datetime);
int hours = TimeZone.CurrentTimeZone.GetUtcOffset(datetime).Hours;
int minutes = TimeZone.CurrentTimeZone.GetUtcOffset(datetime).Minutes;
if (hours == 0 && minutes == 0)
return sb.ToString();
else
{
string offset = (hours >= 0 && minutes >= 0 ? "+" : "") + hours.ToString("00") + minutes.ToString("00");
return sb.ToString().Replace("GMT", offset);
}
}
internal static ImageCodecInfo GetCodec(string codec)
{
foreach (ImageCodecInfo ice in ImageCodecInfo.GetImageEncoders())
{
if (ice.MimeType == codec)
return ice;
}
return null;
}
internal static void SaveJpeg(System.Drawing.Image image, Stream buff, int quality)
{
ImageCodecInfo ici = ExportUtils.GetCodec("image/jpeg");
EncoderParameters ep = new EncoderParameters();
ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
image.Save(buff, ici, ep);
}
internal static string TruncLeadSlash(string line)
{
line = line.Replace("\\", "/");
if (line.StartsWith("/"))
return line.Remove(0, 1);
else
return line;
}
internal static string GetCellReference(int x, int y)
{
StringBuilder cellReference = new StringBuilder(4);
const int MAX_CHARS = 'Z' - 'A' + 1;
do
{
if (cellReference.Length > 0)
x--;
int digit = x % MAX_CHARS;
char cellDig = (char)((char)'A' + (char)digit);
cellReference.Append(cellDig);
x -= digit;
x /= (MAX_CHARS);
}
while (x > 0);
for (int i = 0; i < cellReference.Length / 2; i++)
{
char c = cellReference[i];
cellReference[i] = cellReference[cellReference.Length - i - 1];
cellReference[cellReference.Length - i - 1] = c;
}
cellReference.Append(y.ToString());
return cellReference.ToString();
}
private static readonly CultureInfo INVARIANT_CULTURE = CultureInfo.InvariantCulture;
internal static string StringFormat(string formatString, params object[] objects)
{
return String.Format(INVARIANT_CULTURE, formatString, objects);
}
///
/// Convert index to Excel column name.
///
/// Index of column
/// Column name
public static string IndexToName(int index)
{
bool firstSymbol = false;
string result = "";
for (; index > 0; index /= 26)
{
if (index < 26 && result != "")
firstSymbol = true;
var x = index % 26;
result = (char)(x + 'A' + (firstSymbol ? -1 : 0)) + result;
}
if (result == "")
result = "A";
return result;
}
static ExportUtils()
{
var defaultProvider = new NumberFormatInfo();
defaultProvider.NumberGroupSeparator = string.Empty;
defaultProvider.NumberDecimalSeparator = ".";
_provider = NumberFormatInfo.ReadOnly(defaultProvider);
}
}
}