123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- 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
- {
- /// <summary>
- /// Represents the export to DBF.
- /// </summary>
- 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<byte> header;
- private List<Record> records;
- private Record record;
- private StringBuilder field;
- private List<int> maxFieldsSize;
- private List<string> fieldNameList;
- private List<Record.FieldType> fieldTypes;
- #endregion // Fields
- #region Properties
- /// <summary>
- /// Gets or sets the encoding.
- /// </summary>
- public Encoding Encoding
- {
- get { return encoding; }
- set { encoding = value; }
- }
- /// <summary>
- /// Gets or sets a value that determines whether to export the databand rows only.
- /// </summary>
- public bool DataOnly
- {
- get { return dataOnly; }
- set { dataOnly = value; }
- }
- /// <summary>
- /// Gets or sets a value that determines whether to export the types of fields.
- /// </summary>
- public bool ExportTypes
- {
- get { return exportTypes; }
- set { exportTypes = value; }
- }
- /// <summary>
- /// Gets or sets the list of field names.
- /// </summary>
- /// <remarks>
- /// The field names must be separated by ";" symbol, for example: Column1;Column2;Column3
- /// </remarks>
- public string FieldNames
- {
- get { return fieldNames; }
- set { fieldNames = value; }
- }
- #endregion // Properties
- #region Constructors
- /// <summary>
- /// Initializes a new instance of the <see cref="DBFExport"/> class.
- /// </summary>
- public DBFExport()
- {
- encoding = Encoding.Default;
- fieldNames = "";
- fieldTypes = new List<Record.FieldType>();
- 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<int> GetMaxFieldsSize()
- {
- List<int> maxFieldsSize = new List<int>();
- 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<byte> 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<byte> builder = new List<byte>();
- 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
- /// <inheritdoc/>
- protected override void Start()
- {
- base.Start();
- header = new List<byte>();
- field = new StringBuilder("");
- record = new Record();
- records = new List<Record>();
- maxFieldsSize = new List<int>();
- fieldNameList = new List<string>();
- 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;
- }
- /// <inheritdoc/>
- protected override void ExportPageBegin(ReportPage page)
- {
- base.ExportPageBegin(page);
- matrix.AddPageBegin(page);
- }
- /// <inheritdoc/>
- protected override void ExportBand(BandBase band)
- {
- base.ExportBand(band);
- matrix.AddBand(band, this);
- }
- /// <inheritdoc/>
- protected override void ExportPageEnd(ReportPage page)
- {
- matrix.AddPageEnd(page);
- }
- /// <inheritdoc/>
- protected override void Finish()
- {
- matrix.Prepare();
- Write(Stream);
- }
- /// <inheritdoc/>
- protected override string GetFileFilter()
- {
- return new MyRes("FileFilters").Get("DbfFile");
- }
- #endregion // Protected Methods
- #region Public Methods
- /// <inheritdoc/>
- public override void Serialize(FRWriter writer)
- {
- base.Serialize(writer);
- writer.WriteStr("FieldNames", FieldNames);
- writer.WriteBool("DataOnly", DataOnly);
- writer.WriteBool("ExportTypes", ExportTypes);
- }
- #endregion // Public Methods
- }
- }
|