DbfExport.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.IO;
  5. using FastReport.Utils;
  6. using FastReport.Format;
  7. using System.Globalization;
  8. using System.Collections;
  9. using System.Runtime.InteropServices;
  10. namespace FastReport.Export.Dbf
  11. {
  12. /// <summary>
  13. /// Represents the export to DBF.
  14. /// </summary>
  15. public partial class DBFExport : ExportBase
  16. {
  17. #region Constants
  18. private const byte Ox00 = 0x00;
  19. private const byte SIMPLE_TABLE = 0x03;
  20. private const char FIELD_TYPE = 'C';
  21. private const byte FILE_HEADER_END = 0x0D;
  22. private const byte RECORD_NOT_REMOVED = 0x20;
  23. private const byte RECORD_REMOVED = 0x2A;
  24. private const byte DBF_FILE_END = 0x1A;
  25. private const int RECORDS_COUNT_LENGTH = 4;
  26. private const int HEADER_NULL_FILL = 20;
  27. private const int FIELD_NULL_FILL = 15;
  28. private const int FILE_HEADER_SIZE = 32;
  29. private const int FIELD_HEADER_SIZE = 32;
  30. private const char SPACE = ' ';
  31. private const char UNDERSCORE = '_';
  32. private const string DEFAULT_FIELD_NAME = "FIELD";
  33. #endregion // Constants
  34. #region Fields
  35. private ExportMatrix matrix;
  36. private Encoding encoding;
  37. private bool dataOnly;
  38. private bool exportTypes;
  39. private string fieldNames;
  40. private List<byte> header;
  41. private List<Record> records;
  42. private Record record;
  43. private StringBuilder field;
  44. private List<int> maxFieldsSize;
  45. private List<string> fieldNameList;
  46. private List<Record.FieldType> fieldTypes;
  47. #endregion // Fields
  48. #region Properties
  49. /// <summary>
  50. /// Gets or sets the encoding.
  51. /// </summary>
  52. public Encoding Encoding
  53. {
  54. get { return encoding; }
  55. set { encoding = value; }
  56. }
  57. /// <summary>
  58. /// Gets or sets a value that determines whether to export the databand rows only.
  59. /// </summary>
  60. public bool DataOnly
  61. {
  62. get { return dataOnly; }
  63. set { dataOnly = value; }
  64. }
  65. /// <summary>
  66. /// Gets or sets a value that determines whether to export the types of fields.
  67. /// </summary>
  68. public bool ExportTypes
  69. {
  70. get { return exportTypes; }
  71. set { exportTypes = value; }
  72. }
  73. /// <summary>
  74. /// Gets or sets the list of field names.
  75. /// </summary>
  76. /// <remarks>
  77. /// The field names must be separated by ";" symbol, for example: Column1;Column2;Column3
  78. /// </remarks>
  79. public string FieldNames
  80. {
  81. get { return fieldNames; }
  82. set { fieldNames = value; }
  83. }
  84. #endregion // Properties
  85. #region Constructors
  86. /// <summary>
  87. /// Initializes a new instance of the <see cref="DBFExport"/> class.
  88. /// </summary>
  89. public DBFExport()
  90. {
  91. encoding = Encoding.Default;
  92. fieldNames = "";
  93. fieldTypes = new List<Record.FieldType>();
  94. dataOnly = false;
  95. exportTypes = false;
  96. }
  97. #endregion // Constructors
  98. #region Private Methods
  99. private void PrepareRecords()
  100. {
  101. int i, x, y;
  102. ExportIEMObject obj;
  103. for (y = 0; y < matrix.Height - 1; y++)
  104. {
  105. for (x = 0; x < matrix.Width; x++)
  106. {
  107. i = matrix.Cell(x, y);
  108. if (i != -1)
  109. {
  110. obj = matrix.ObjectById(i);
  111. if (obj.Counter == 0)
  112. {
  113. obj.Counter = 1;
  114. if (exportTypes)
  115. AddField(obj);
  116. else
  117. {
  118. record.Add(new StringBuilder(obj.Text), Record.FieldType.String);
  119. }
  120. }
  121. }
  122. }
  123. if (record.Count != 0)
  124. {
  125. records.Add(record);
  126. record = new Record();
  127. }
  128. }
  129. }
  130. private void AddField(ExportIEMObject obj)
  131. {
  132. Record.FieldType type = Record.FieldType.String;
  133. field = new StringBuilder(obj.Text);
  134. if (obj.IsNumeric)
  135. {
  136. if (obj.Style.Format is CurrencyFormat)
  137. {
  138. float number;
  139. CurrencyFormat formatInfo = obj.Style.Format as CurrencyFormat;
  140. // parse to float for save decimal part of a number
  141. float.TryParse(obj.Text, NumberStyles.Currency, formatInfo.GetNumberFormatInfo(), out number);
  142. type = Record.FieldType.Currency;
  143. field = new StringBuilder((number * 10000).ToString()); // max number accuracy is 4
  144. }
  145. else if (obj.Style.Format is NumberFormat && (obj.Style.Format as NumberFormat).DecimalDigits == 0)
  146. {
  147. type = Record.FieldType.Integer;
  148. }
  149. else
  150. type = Record.FieldType.Decimal;
  151. }
  152. else if (obj.IsDateTime)
  153. {
  154. type = Record.FieldType.DataTime;
  155. DateTime dt;
  156. if (DateTime.TryParse(obj.Text, out dt))
  157. field = new StringBuilder(dt.ToString("yyyyMMddHHmmss"));
  158. }
  159. else if (obj.Style.Format is BooleanFormat)
  160. {
  161. BooleanFormat format = obj.Style.Format as BooleanFormat;
  162. field = new StringBuilder(obj.Text == format.TrueText ? "T" : "F");
  163. type = Record.FieldType.Boolean;
  164. }
  165. record.Add(field, type);
  166. }
  167. private int GetRecordSize()
  168. {
  169. int maxSize = 0;
  170. foreach (int m in maxFieldsSize)
  171. {
  172. maxSize += m;
  173. }
  174. return ++maxSize;
  175. }
  176. private int GetMaxRecordCount()
  177. {
  178. int maxCount = 0;
  179. foreach (Record rec in records)
  180. {
  181. if (rec.Count > maxCount)
  182. {
  183. maxCount = rec.Count;
  184. fieldTypes.Clear();
  185. for (int i = 0; i < rec.Count; i++)
  186. fieldTypes.Add(rec[i].Type);
  187. }
  188. }
  189. return maxCount;
  190. }
  191. private List<int> GetMaxFieldsSize()
  192. {
  193. List<int> maxFieldsSize = new List<int>();
  194. int maxCount = GetMaxRecordCount();
  195. for (int i = 0; i < maxCount; i++)
  196. {
  197. int maxFieldSize = 0;
  198. foreach (Record rec in records)
  199. {
  200. if (rec.Count < i + 1)
  201. {
  202. continue;
  203. }
  204. if (rec[i].Data.Length > maxFieldSize)
  205. {
  206. if (rec[i].Type == Record.FieldType.DataTime)
  207. maxFieldSize = 14;
  208. else if (rec[i].Type == Record.FieldType.Integer)
  209. maxFieldSize = 4; // bytes
  210. else if (rec[i].Type == Record.FieldType.Currency)
  211. maxFieldSize = 8; // bytes
  212. else
  213. maxFieldSize = rec[i].Data.Length;
  214. }
  215. }
  216. maxFieldsSize.Add(maxFieldSize);
  217. }
  218. return maxFieldsSize;
  219. }
  220. private int GetHeaderSize()
  221. {
  222. return (FILE_HEADER_SIZE + FIELD_HEADER_SIZE * maxFieldsSize.Count + 1);
  223. }
  224. private void CompleteRecordsEmptyFields()
  225. {
  226. maxFieldsSize = GetMaxFieldsSize();
  227. int maxCount = maxFieldsSize.Count;
  228. StringBuilder emptyField = new StringBuilder();
  229. foreach (Record rec in records)
  230. {
  231. for (int i = 0; i < maxCount; i++)
  232. {
  233. if (i < rec.Count)
  234. {
  235. if (rec[i].Data.Length < maxFieldsSize[i])
  236. {
  237. rec[i].Data.Append(SPACE, maxFieldsSize[i] - rec[i].Data.Length);
  238. }
  239. }
  240. else
  241. {
  242. emptyField.Append(SPACE, maxFieldsSize[i]);
  243. rec.Add(emptyField, Record.FieldType.String);
  244. emptyField = new StringBuilder();
  245. }
  246. }
  247. }
  248. }
  249. private void CompleteByteListNulls(List<byte> list, int n)
  250. {
  251. for (int i = 0; i < n; i++)
  252. {
  253. list.Add(Ox00);
  254. }
  255. }
  256. private void PrepareFileHeader()
  257. {
  258. header.Clear();
  259. header.Add(SIMPLE_TABLE);
  260. DateTime date = SystemFake.DateTime.Now;
  261. int year = date.Year % 100;
  262. header.Add((byte)year);
  263. header.Add((byte)date.Month);
  264. header.Add((byte)date.Day);
  265. byte[] bytes = BitConverter.GetBytes(records.Count);
  266. if (!BitConverter.IsLittleEndian)
  267. {
  268. Array.Reverse(bytes);
  269. }
  270. header.AddRange(bytes);
  271. bytes = BitConverter.GetBytes((short)GetHeaderSize());
  272. if (!BitConverter.IsLittleEndian)
  273. {
  274. Array.Reverse(bytes);
  275. }
  276. header.AddRange(bytes);
  277. bytes = BitConverter.GetBytes((short)(GetRecordSize()));
  278. if (!BitConverter.IsLittleEndian)
  279. {
  280. Array.Reverse(bytes);
  281. }
  282. header.AddRange(bytes);
  283. CompleteByteListNulls(header, HEADER_NULL_FILL);
  284. }
  285. private void PrepareFieldNames()
  286. {
  287. fieldNameList.Clear();
  288. string[] fieldNamesArr = FieldNames.Split(';');
  289. for (int i = 0; i < maxFieldsSize.Count; i++)
  290. {
  291. string name = "";
  292. if (i >= fieldNamesArr.Length || String.IsNullOrEmpty(fieldNamesArr[i]))
  293. {
  294. name = DEFAULT_FIELD_NAME + i.ToString();
  295. }
  296. else
  297. {
  298. name = fieldNamesArr[i];
  299. name = name.Replace(SPACE, UNDERSCORE);
  300. if (name.Length > Record.MAX_FIELD_NAME_CHARS)
  301. name = name.Remove(Record.MAX_FIELD_NAME_CHARS);
  302. }
  303. fieldNameList.Add(name);
  304. }
  305. }
  306. private void PrepareFieldHeaders()
  307. {
  308. for (int i = 0; i < maxFieldsSize.Count; i++)
  309. {
  310. string fieldName = fieldNameList[i];
  311. byte[] bytes = encoding.GetBytes(fieldName);
  312. header.AddRange(bytes);
  313. CompleteByteListNulls(header, Record.MAX_FIELD_NAME_CHARS - fieldName.Length + 1);
  314. header.Add((byte)fieldTypes[i]);
  315. CompleteByteListNulls(header, 4);
  316. header.Add((byte)maxFieldsSize[i]);
  317. CompleteByteListNulls(header, FIELD_NULL_FILL);
  318. }
  319. header.Add(FILE_HEADER_END);
  320. }
  321. private void WriteHeader(Stream stream)
  322. {
  323. byte[] bytes = header.ToArray();
  324. stream.Write(bytes, 0, header.Count);
  325. }
  326. private void WriteRecords(Stream stream)
  327. {
  328. List<byte> builder = new List<byte>();
  329. foreach (Record rec in records)
  330. {
  331. for (int i = 0; i < rec.Count; i++)
  332. {
  333. switch (fieldTypes[i])
  334. {
  335. case Record.FieldType.Integer:
  336. {
  337. int number;
  338. int.TryParse(rec[i].Data.ToString(), out number);
  339. byte b0 = (byte)number;
  340. byte b1 = (byte)(number >> 8);
  341. byte b2 = (byte)(number >> 16);
  342. byte b3 = (byte)(number >> 24);
  343. builder.Add(b0);
  344. builder.Add(b1);
  345. builder.Add(b2);
  346. builder.Add(b3);
  347. break;
  348. }
  349. case Record.FieldType.Currency:
  350. {
  351. long number;
  352. long.TryParse(rec[i].Data.ToString(), NumberStyles.Float, NumberFormatInfo.CurrentInfo, out number);
  353. byte b0 = (byte)number;
  354. byte b1 = (byte)(number >> 8);
  355. byte b2 = (byte)(number >> 16);
  356. byte b3 = (byte)(number >> 24);
  357. byte b4 = (byte)(number >> 32);
  358. byte b5 = (byte)(number >> 40);
  359. byte b6 = (byte)(number >> 48);
  360. byte b7 = (byte)(number >> 56);
  361. builder.Add(b0);
  362. builder.Add(b1);
  363. builder.Add(b2);
  364. builder.Add(b3);
  365. builder.Add(b4);
  366. builder.Add(b5);
  367. builder.Add(b6);
  368. builder.Add(b7);
  369. break;
  370. }
  371. default:
  372. builder.AddRange(encoding.GetBytes(rec[i].Data.ToString()));
  373. break;
  374. }
  375. }
  376. stream.WriteByte(RECORD_NOT_REMOVED);
  377. stream.Write(builder.ToArray(), 0, builder.ToArray().Length);
  378. builder.Clear();
  379. }
  380. }
  381. private void Write(Stream stream)
  382. {
  383. PrepareRecords();
  384. CompleteRecordsEmptyFields();
  385. PrepareFileHeader();
  386. PrepareFieldNames();
  387. PrepareFieldHeaders();
  388. WriteHeader(Stream);
  389. WriteRecords(Stream);
  390. Stream.WriteByte(DBF_FILE_END);
  391. }
  392. #endregion // Private Methods
  393. #region Protected Methods
  394. /// <inheritdoc/>
  395. protected override void Start()
  396. {
  397. base.Start();
  398. header = new List<byte>();
  399. field = new StringBuilder("");
  400. record = new Record();
  401. records = new List<Record>();
  402. maxFieldsSize = new List<int>();
  403. fieldNameList = new List<string>();
  404. matrix = new ExportMatrix();
  405. matrix.Inaccuracy = 0.5f;
  406. matrix.PlainRich = true;
  407. matrix.AreaFill = false;
  408. matrix.CropAreaFill = true;
  409. matrix.Report = Report;
  410. matrix.Images = false;
  411. matrix.WrapText = false;
  412. matrix.DataOnly = dataOnly;
  413. matrix.ShowProgress = ShowProgress;
  414. }
  415. /// <inheritdoc/>
  416. protected override void ExportPageBegin(ReportPage page)
  417. {
  418. base.ExportPageBegin(page);
  419. matrix.AddPageBegin(page);
  420. }
  421. /// <inheritdoc/>
  422. protected override void ExportBand(BandBase band)
  423. {
  424. base.ExportBand(band);
  425. matrix.AddBand(band, this);
  426. }
  427. /// <inheritdoc/>
  428. protected override void ExportPageEnd(ReportPage page)
  429. {
  430. matrix.AddPageEnd(page);
  431. }
  432. /// <inheritdoc/>
  433. protected override void Finish()
  434. {
  435. matrix.Prepare();
  436. Write(Stream);
  437. }
  438. /// <inheritdoc/>
  439. protected override string GetFileFilter()
  440. {
  441. return new MyRes("FileFilters").Get("DbfFile");
  442. }
  443. #endregion // Protected Methods
  444. #region Public Methods
  445. /// <inheritdoc/>
  446. public override void Serialize(FRWriter writer)
  447. {
  448. base.Serialize(writer);
  449. writer.WriteStr("FieldNames", FieldNames);
  450. writer.WriteBool("DataOnly", DataOnly);
  451. writer.WriteBool("ExportTypes", ExportTypes);
  452. }
  453. #endregion // Public Methods
  454. }
  455. }