using System; using System.Collections.Generic; using System.Text; using System.IO; using FastReport.Utils; using FastReport.Format; using System.Globalization; using System.Collections; using System.Runtime.InteropServices; namespace FastReport.Export.Dbf { /// /// Represents the export to DBF. /// public partial class DBFExport : ExportBase { #region Constants private const byte Ox00 = 0x00; private const byte SIMPLE_TABLE = 0x03; private const char FIELD_TYPE = 'C'; private const byte FILE_HEADER_END = 0x0D; private const byte RECORD_NOT_REMOVED = 0x20; private const byte RECORD_REMOVED = 0x2A; private const byte DBF_FILE_END = 0x1A; private const int RECORDS_COUNT_LENGTH = 4; private const int HEADER_NULL_FILL = 20; private const int FIELD_NULL_FILL = 15; private const int FILE_HEADER_SIZE = 32; private const int FIELD_HEADER_SIZE = 32; private const char SPACE = ' '; private const char UNDERSCORE = '_'; private const string DEFAULT_FIELD_NAME = "FIELD"; #endregion // Constants #region Fields private ExportMatrix matrix; private Encoding encoding; private bool dataOnly; private bool exportTypes; private string fieldNames; private List header; private List records; private Record record; private StringBuilder field; private List maxFieldsSize; private List fieldNameList; private List fieldTypes; #endregion // Fields #region Properties /// /// Gets or sets the encoding. /// public Encoding Encoding { get { return encoding; } set { encoding = value; } } /// /// Gets or sets a value that determines whether to export the databand rows only. /// public bool DataOnly { get { return dataOnly; } set { dataOnly = value; } } /// /// Gets or sets a value that determines whether to export the types of fields. /// public bool ExportTypes { get { return exportTypes; } set { exportTypes = value; } } /// /// Gets or sets the list of field names. /// /// /// The field names must be separated by ";" symbol, for example: Column1;Column2;Column3 /// public string FieldNames { get { return fieldNames; } set { fieldNames = value; } } #endregion // Properties #region Constructors /// /// Initializes a new instance of the class. /// public DBFExport() { encoding = Encoding.Default; fieldNames = ""; fieldTypes = new List(); dataOnly = false; exportTypes = false; } #endregion // Constructors #region Private Methods private void PrepareRecords() { int i, x, y; ExportIEMObject obj; for (y = 0; y < matrix.Height - 1; y++) { for (x = 0; x < matrix.Width; x++) { i = matrix.Cell(x, y); if (i != -1) { obj = matrix.ObjectById(i); if (obj.Counter == 0) { obj.Counter = 1; if (exportTypes) AddField(obj); else { record.Add(new StringBuilder(obj.Text), Record.FieldType.String); } } } } if (record.Count != 0) { records.Add(record); record = new Record(); } } } private void AddField(ExportIEMObject obj) { Record.FieldType type = Record.FieldType.String; field = new StringBuilder(obj.Text); if (obj.IsNumeric) { if (obj.Style.Format is CurrencyFormat) { float number; CurrencyFormat formatInfo = obj.Style.Format as CurrencyFormat; // parse to float for save decimal part of a number float.TryParse(obj.Text, NumberStyles.Currency, formatInfo.GetNumberFormatInfo(), out number); type = Record.FieldType.Currency; field = new StringBuilder((number * 10000).ToString()); // max number accuracy is 4 } else if (obj.Style.Format is NumberFormat && (obj.Style.Format as NumberFormat).DecimalDigits == 0) { type = Record.FieldType.Integer; } else type = Record.FieldType.Decimal; } else if (obj.IsDateTime) { type = Record.FieldType.DataTime; DateTime dt; if (DateTime.TryParse(obj.Text, out dt)) field = new StringBuilder(dt.ToString("yyyyMMddHHmmss")); } else if (obj.Style.Format is BooleanFormat) { BooleanFormat format = obj.Style.Format as BooleanFormat; field = new StringBuilder(obj.Text == format.TrueText ? "T" : "F"); type = Record.FieldType.Boolean; } record.Add(field, type); } private int GetRecordSize() { int maxSize = 0; foreach (int m in maxFieldsSize) { maxSize += m; } return ++maxSize; } private int GetMaxRecordCount() { int maxCount = 0; foreach (Record rec in records) { if (rec.Count > maxCount) { maxCount = rec.Count; fieldTypes.Clear(); for (int i = 0; i < rec.Count; i++) fieldTypes.Add(rec[i].Type); } } return maxCount; } private List GetMaxFieldsSize() { List maxFieldsSize = new List(); int maxCount = GetMaxRecordCount(); for (int i = 0; i < maxCount; i++) { int maxFieldSize = 0; foreach (Record rec in records) { if (rec.Count < i + 1) { continue; } if (rec[i].Data.Length > maxFieldSize) { if (rec[i].Type == Record.FieldType.DataTime) maxFieldSize = 14; else if (rec[i].Type == Record.FieldType.Integer) maxFieldSize = 4; // bytes else if (rec[i].Type == Record.FieldType.Currency) maxFieldSize = 8; // bytes else maxFieldSize = rec[i].Data.Length; } } maxFieldsSize.Add(maxFieldSize); } return maxFieldsSize; } private int GetHeaderSize() { return (FILE_HEADER_SIZE + FIELD_HEADER_SIZE * maxFieldsSize.Count + 1); } private void CompleteRecordsEmptyFields() { maxFieldsSize = GetMaxFieldsSize(); int maxCount = maxFieldsSize.Count; StringBuilder emptyField = new StringBuilder(); foreach (Record rec in records) { for (int i = 0; i < maxCount; i++) { if (i < rec.Count) { if (rec[i].Data.Length < maxFieldsSize[i]) { rec[i].Data.Append(SPACE, maxFieldsSize[i] - rec[i].Data.Length); } } else { emptyField.Append(SPACE, maxFieldsSize[i]); rec.Add(emptyField, Record.FieldType.String); emptyField = new StringBuilder(); } } } } private void CompleteByteListNulls(List list, int n) { for (int i = 0; i < n; i++) { list.Add(Ox00); } } private void PrepareFileHeader() { header.Clear(); header.Add(SIMPLE_TABLE); DateTime date = SystemFake.DateTime.Now; int year = date.Year % 100; header.Add((byte)year); header.Add((byte)date.Month); header.Add((byte)date.Day); byte[] bytes = BitConverter.GetBytes(records.Count); if (!BitConverter.IsLittleEndian) { Array.Reverse(bytes); } header.AddRange(bytes); bytes = BitConverter.GetBytes((short)GetHeaderSize()); if (!BitConverter.IsLittleEndian) { Array.Reverse(bytes); } header.AddRange(bytes); bytes = BitConverter.GetBytes((short)(GetRecordSize())); if (!BitConverter.IsLittleEndian) { Array.Reverse(bytes); } header.AddRange(bytes); CompleteByteListNulls(header, HEADER_NULL_FILL); } private void PrepareFieldNames() { fieldNameList.Clear(); string[] fieldNamesArr = FieldNames.Split(';'); for (int i = 0; i < maxFieldsSize.Count; i++) { string name = ""; if (i >= fieldNamesArr.Length || String.IsNullOrEmpty(fieldNamesArr[i])) { name = DEFAULT_FIELD_NAME + i.ToString(); } else { name = fieldNamesArr[i]; name = name.Replace(SPACE, UNDERSCORE); if (name.Length > Record.MAX_FIELD_NAME_CHARS) name = name.Remove(Record.MAX_FIELD_NAME_CHARS); } fieldNameList.Add(name); } } private void PrepareFieldHeaders() { for (int i = 0; i < maxFieldsSize.Count; i++) { string fieldName = fieldNameList[i]; byte[] bytes = encoding.GetBytes(fieldName); header.AddRange(bytes); CompleteByteListNulls(header, Record.MAX_FIELD_NAME_CHARS - fieldName.Length + 1); header.Add((byte)fieldTypes[i]); CompleteByteListNulls(header, 4); header.Add((byte)maxFieldsSize[i]); CompleteByteListNulls(header, FIELD_NULL_FILL); } header.Add(FILE_HEADER_END); } private void WriteHeader(Stream stream) { byte[] bytes = header.ToArray(); stream.Write(bytes, 0, header.Count); } private void WriteRecords(Stream stream) { List builder = new List(); foreach (Record rec in records) { for (int i = 0; i < rec.Count; i++) { switch (fieldTypes[i]) { case Record.FieldType.Integer: { int number; int.TryParse(rec[i].Data.ToString(), out number); byte b0 = (byte)number; byte b1 = (byte)(number >> 8); byte b2 = (byte)(number >> 16); byte b3 = (byte)(number >> 24); builder.Add(b0); builder.Add(b1); builder.Add(b2); builder.Add(b3); break; } case Record.FieldType.Currency: { long number; long.TryParse(rec[i].Data.ToString(), NumberStyles.Float, NumberFormatInfo.CurrentInfo, out number); byte b0 = (byte)number; byte b1 = (byte)(number >> 8); byte b2 = (byte)(number >> 16); byte b3 = (byte)(number >> 24); byte b4 = (byte)(number >> 32); byte b5 = (byte)(number >> 40); byte b6 = (byte)(number >> 48); byte b7 = (byte)(number >> 56); builder.Add(b0); builder.Add(b1); builder.Add(b2); builder.Add(b3); builder.Add(b4); builder.Add(b5); builder.Add(b6); builder.Add(b7); break; } default: builder.AddRange(encoding.GetBytes(rec[i].Data.ToString())); break; } } stream.WriteByte(RECORD_NOT_REMOVED); stream.Write(builder.ToArray(), 0, builder.ToArray().Length); builder.Clear(); } } private void Write(Stream stream) { PrepareRecords(); CompleteRecordsEmptyFields(); PrepareFileHeader(); PrepareFieldNames(); PrepareFieldHeaders(); WriteHeader(Stream); WriteRecords(Stream); Stream.WriteByte(DBF_FILE_END); } #endregion // Private Methods #region Protected Methods /// protected override void Start() { base.Start(); header = new List(); field = new StringBuilder(""); record = new Record(); records = new List(); maxFieldsSize = new List(); fieldNameList = new List(); matrix = new ExportMatrix(); matrix.Inaccuracy = 0.5f; matrix.PlainRich = true; matrix.AreaFill = false; matrix.CropAreaFill = true; matrix.Report = Report; matrix.Images = false; matrix.WrapText = false; matrix.DataOnly = dataOnly; matrix.ShowProgress = ShowProgress; } /// protected override void ExportPageBegin(ReportPage page) { base.ExportPageBegin(page); matrix.AddPageBegin(page); } /// protected override void ExportBand(BandBase band) { base.ExportBand(band); matrix.AddBand(band, this); } /// protected override void ExportPageEnd(ReportPage page) { matrix.AddPageEnd(page); } /// protected override void Finish() { matrix.Prepare(); Write(Stream); } /// protected override string GetFileFilter() { return new MyRes("FileFilters").Get("DbfFile"); } #endregion // Protected Methods #region Public Methods /// public override void Serialize(FRWriter writer) { base.Serialize(writer); writer.WriteStr("FieldNames", FieldNames); writer.WriteBool("DataOnly", DataOnly); writer.WriteBool("ExportTypes", ExportTypes); } #endregion // Public Methods } }