ZipCodeObject.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Drawing.Drawing2D;
  6. using FastReport.Utils;
  7. using System.Drawing.Design;
  8. namespace FastReport
  9. {
  10. /// <summary>
  11. /// Represents a zip code object.
  12. /// </summary>
  13. /// <remarks>
  14. /// This object is mainly used in Russia to print postal index on envelopes. It complies with the
  15. /// GOST R 51506-99.
  16. /// </remarks>
  17. public partial class ZipCodeObject : ReportComponentBase
  18. {
  19. #region Fields
  20. private float segmentWidth;
  21. private float segmentHeight;
  22. private float spacing;
  23. private int segmentCount;
  24. private bool showMarkers;
  25. private bool showGrid;
  26. private string dataColumn;
  27. private string expression;
  28. private string text;
  29. private static List<Point[]> FDigits;
  30. #endregion
  31. #region Properties
  32. /// <summary>
  33. /// Gets or sets the width of a single zipcode segment, in pixels.
  34. /// </summary>
  35. [Category("Layout")]
  36. [TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")]
  37. public float SegmentWidth
  38. {
  39. get { return segmentWidth; }
  40. set { segmentWidth = value; }
  41. }
  42. /// <summary>
  43. /// Gets or sets the height of a single zipcode segment, in pixels.
  44. /// </summary>
  45. [Category("Layout")]
  46. [TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")]
  47. public float SegmentHeight
  48. {
  49. get { return segmentHeight; }
  50. set { segmentHeight = value; }
  51. }
  52. /// <summary>
  53. /// Gets or sets the spacing between origins of segments, in pixels.
  54. /// </summary>
  55. [Category("Layout")]
  56. [TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")]
  57. public float Spacing
  58. {
  59. get { return spacing; }
  60. set { spacing = value; }
  61. }
  62. /// <summary>
  63. /// Gets or sets the number of segments in zipcode.
  64. /// </summary>
  65. [Category("Layout")]
  66. [DefaultValue(6)]
  67. public int SegmentCount
  68. {
  69. get { return segmentCount; }
  70. set { segmentCount = value; }
  71. }
  72. /// <summary>
  73. /// Gets or sets a value indicating whether the reference markers should be drawn.
  74. /// </summary>
  75. /// <remarks>
  76. /// Reference markers are used by postal service to automatically read the zipcode.
  77. /// </remarks>
  78. [Category("Behavior")]
  79. [DefaultValue(true)]
  80. public bool ShowMarkers
  81. {
  82. get { return showMarkers; }
  83. set { showMarkers = value; }
  84. }
  85. /// <summary>
  86. /// Gets or sets a value indicating whether the segment grid should be drawn.
  87. /// </summary>
  88. [Category("Behavior")]
  89. [DefaultValue(true)]
  90. public bool ShowGrid
  91. {
  92. get { return showGrid; }
  93. set { showGrid = value; }
  94. }
  95. /// <summary>
  96. /// Gets or sets a data column name bound to this control.
  97. /// </summary>
  98. /// <remarks>
  99. /// Value must be in the form "Datasource.Column".
  100. /// </remarks>
  101. [Category("Data")]
  102. [Editor("FastReport.TypeEditors.DataColumnEditor, FastReport", typeof(UITypeEditor))]
  103. public string DataColumn
  104. {
  105. get { return dataColumn; }
  106. set { dataColumn = value; }
  107. }
  108. /// <summary>
  109. /// Gets or sets an expression that contains the zip code.
  110. /// </summary>
  111. [Category("Data")]
  112. [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))]
  113. public string Expression
  114. {
  115. get { return expression; }
  116. set { expression = value; }
  117. }
  118. /// <summary>
  119. /// Gets or sets the zip code.
  120. /// </summary>
  121. [Category("Data")]
  122. public string Text
  123. {
  124. get { return text; }
  125. set { text = value; }
  126. }
  127. #endregion
  128. #region Private Methods
  129. private void DrawSegmentGrid(FRPaintEventArgs e, float offsetX, float offsetY)
  130. {
  131. IGraphics g = e.Graphics;
  132. SmoothingMode saveSmoothing = g.SmoothingMode;
  133. g.SmoothingMode = SmoothingMode.AntiAlias;
  134. Brush b = e.Cache.GetBrush(Border.Color);
  135. int[] grid = new int[] { 111111, 110001, 101001, 100101, 100011, 111111, 110001, 101001, 100101, 100011, 111111 };
  136. float ratioX = segmentWidth / (Units.Centimeters * 0.5f);
  137. float ratioY = segmentHeight / (Units.Centimeters * 1);
  138. float pointSize = Units.Millimeters * 0.25f;
  139. float y = AbsTop;
  140. foreach (int gridRow in grid)
  141. {
  142. int row = gridRow;
  143. float x = AbsLeft;
  144. while (row > 0)
  145. {
  146. if (row % 10 == 1)
  147. g.FillEllipse(b, (x + offsetX - pointSize / 2) * e.ScaleX, (y + offsetY - pointSize / 2) * e.ScaleY,
  148. pointSize * e.ScaleX, pointSize * e.ScaleY);
  149. row /= 10;
  150. x += Units.Millimeters * 1 * ratioX;
  151. }
  152. y += Units.Millimeters * 1 * ratioY;
  153. }
  154. g.SmoothingMode = saveSmoothing;
  155. }
  156. private void DrawReferenceLine(FRPaintEventArgs e, float offsetX)
  157. {
  158. IGraphics g = e.Graphics;
  159. Brush b = e.Cache.GetBrush(Border.Color);
  160. g.FillRectangle(b,
  161. new RectangleF((AbsLeft + offsetX) * e.ScaleX, AbsTop * e.ScaleY,
  162. Units.Millimeters * 7 * e.ScaleX, Units.Millimeters * 2 * e.ScaleY));
  163. // draw start line
  164. if (offsetX == 0)
  165. {
  166. g.FillRectangle(b,
  167. new RectangleF((AbsLeft + offsetX) * e.ScaleX, (AbsTop + Units.Millimeters * 3) * e.ScaleY,
  168. Units.Millimeters * 7 * e.ScaleX, Units.Millimeters * 1 * e.ScaleY));
  169. }
  170. }
  171. private void DrawSegment(FRPaintEventArgs e, int symbol, float offsetX)
  172. {
  173. IGraphics g = e.Graphics;
  174. float offsetY = 0;
  175. // draw marker
  176. if (showMarkers)
  177. {
  178. DrawReferenceLine(e, offsetX);
  179. if (offsetX == 0)
  180. return;
  181. offsetX += Units.Millimeters * 1;
  182. offsetY = Units.Millimeters * 4;
  183. }
  184. else
  185. {
  186. // draw inside the object's area - important when you export the object
  187. offsetX += Border.Width / 2;
  188. offsetY += Border.Width / 2;
  189. }
  190. // draw grid
  191. if (showGrid)
  192. DrawSegmentGrid(e, offsetX, offsetY);
  193. // draw symbol
  194. if (symbol != -1)
  195. {
  196. Point[] digit = FDigits[symbol];
  197. PointF[] path = new PointF[digit.Length];
  198. float ratioX = segmentWidth / (Units.Centimeters * 0.5f);
  199. float ratioY = segmentHeight / (Units.Centimeters * 1);
  200. for (int i = 0; i < digit.Length; i++)
  201. {
  202. path[i] = new PointF((AbsLeft + digit[i].X * Units.Millimeters * ratioX + offsetX) * e.ScaleX,
  203. (AbsTop + digit[i].Y * Units.Millimeters * ratioY + offsetY) * e.ScaleY);
  204. }
  205. using (Pen pen = new Pen(Border.Color, Border.Width * e.ScaleX))
  206. {
  207. pen.StartCap = LineCap.Round;
  208. pen.EndCap = LineCap.Round;
  209. pen.LineJoin = LineJoin.Round;
  210. g.DrawLines(pen, path);
  211. }
  212. }
  213. }
  214. #endregion
  215. #region Public Methods
  216. /// <inheritdoc/>
  217. public override void Assign(Base source)
  218. {
  219. base.Assign(source);
  220. ZipCodeObject src = source as ZipCodeObject;
  221. SegmentWidth = src.SegmentWidth;
  222. SegmentHeight = src.SegmentHeight;
  223. Spacing = src.Spacing;
  224. SegmentCount = src.SegmentCount;
  225. ShowMarkers = src.ShowMarkers;
  226. ShowGrid = src.ShowGrid;
  227. DataColumn = src.DataColumn;
  228. Expression = src.Expression;
  229. Text = src.Text;
  230. }
  231. /// <inheritdoc/>
  232. public override void Draw(FRPaintEventArgs e)
  233. {
  234. Width = ((showMarkers ? 1 : 0) + segmentCount) * spacing;
  235. Height = (showMarkers ? segmentHeight + Units.Millimeters * 4 : segmentHeight) + Border.Width;
  236. base.Draw(e);
  237. float offsetX = 0;
  238. if (showMarkers)
  239. {
  240. // draw starting marker
  241. DrawSegment(e, -1, 0);
  242. offsetX += spacing;
  243. }
  244. string text = Text.PadLeft(segmentCount, '0');
  245. text = text.Substring(0, segmentCount);
  246. foreach (char ch in text)
  247. {
  248. int symbol = -1;
  249. if (ch >= '0' && ch <= '9')
  250. symbol = (int)ch - (int)'0';
  251. DrawSegment(e, symbol, offsetX);
  252. offsetX += spacing;
  253. }
  254. }
  255. /// <inheritdoc/>
  256. public override void Serialize(FRWriter writer)
  257. {
  258. ZipCodeObject c = writer.DiffObject as ZipCodeObject;
  259. Border.SimpleBorder = true;
  260. base.Serialize(writer);
  261. if (FloatDiff(SegmentWidth, c.SegmentWidth))
  262. writer.WriteFloat("SegmentWidth", SegmentWidth);
  263. if (FloatDiff(SegmentHeight, c.SegmentHeight))
  264. writer.WriteFloat("SegmentHeight", SegmentHeight);
  265. if (FloatDiff(Spacing, c.Spacing))
  266. writer.WriteFloat("Spacing", Spacing);
  267. if (SegmentCount != c.SegmentCount)
  268. writer.WriteInt("SegmentCount", SegmentCount);
  269. if (ShowMarkers != c.ShowMarkers)
  270. writer.WriteBool("ShowMarkers", ShowMarkers);
  271. if (ShowGrid != c.ShowGrid)
  272. writer.WriteBool("ShowGrid", ShowGrid);
  273. if (DataColumn != c.DataColumn)
  274. writer.WriteStr("DataColumn", DataColumn);
  275. if (Expression != c.Expression)
  276. writer.WriteStr("Expression", Expression);
  277. if (Text != c.Text)
  278. writer.WriteStr("Text", Text);
  279. }
  280. #endregion
  281. #region Report Engine
  282. /// <inheritdoc/>
  283. public override string[] GetExpressions()
  284. {
  285. List<string> expressions = new List<string>();
  286. expressions.AddRange(base.GetExpressions());
  287. if (!String.IsNullOrEmpty(DataColumn))
  288. expressions.Add(DataColumn);
  289. if (!String.IsNullOrEmpty(Expression))
  290. expressions.Add(Expression);
  291. return expressions.ToArray();
  292. }
  293. /// <inheritdoc/>
  294. public override void GetData()
  295. {
  296. base.GetData();
  297. if (!String.IsNullOrEmpty(DataColumn))
  298. {
  299. object value = Report.GetColumnValue(DataColumn);
  300. Text = value == null ? "" : value.ToString();
  301. }
  302. else if (!String.IsNullOrEmpty(Expression))
  303. {
  304. object value = Report.Calc(Expression);
  305. Text = value == null ? "" : value.ToString();
  306. }
  307. }
  308. #endregion
  309. /// <summary>
  310. /// Initializes a new instance of the <see cref="ZipCodeObject"/> with the default settings.
  311. /// </summary>
  312. public ZipCodeObject()
  313. {
  314. segmentWidth = Units.Centimeters * 0.5f;
  315. segmentHeight = Units.Centimeters * 1;
  316. spacing = Units.Centimeters * 0.9f;
  317. segmentCount = 6;
  318. showMarkers = true;
  319. showGrid = true;
  320. text = "123456";
  321. dataColumn = "";
  322. expression = "";
  323. FlagSimpleBorder = true;
  324. Border.Width = 3;
  325. SetFlags(Flags.HasSmartTag, true);
  326. }
  327. static ZipCodeObject()
  328. {
  329. FDigits = new List<Point[]>();
  330. FDigits.Add(new Point[] { new Point(0, 0), new Point(5, 0), new Point(5, 10), new Point(0, 10), new Point(0, 0) });
  331. FDigits.Add(new Point[] { new Point(0, 5), new Point(5, 0), new Point(5, 10) });
  332. FDigits.Add(new Point[] { new Point(0, 0), new Point(5, 0), new Point(5, 5), new Point(0, 10), new Point(5, 10) });
  333. FDigits.Add(new Point[] { new Point(0, 0), new Point(5, 0), new Point(0, 5), new Point(5, 5), new Point(0, 10) });
  334. FDigits.Add(new Point[] { new Point(0, 0), new Point(0, 5), new Point(5, 5), new Point(5, 0), new Point(5, 10) });
  335. FDigits.Add(new Point[] { new Point(5, 0), new Point(0, 0), new Point(0, 5), new Point(5, 5), new Point(5, 10), new Point(0, 10) });
  336. FDigits.Add(new Point[] { new Point(5, 0), new Point(0, 5), new Point(0, 10), new Point(5, 10), new Point(5, 5), new Point(0, 5) });
  337. FDigits.Add(new Point[] { new Point(0, 0), new Point(5, 0), new Point(0, 5), new Point(0, 10) });
  338. FDigits.Add(new Point[] { new Point(0, 5), new Point(0, 0), new Point(5, 0), new Point(5, 10), new Point(0, 10), new Point(0, 5), new Point(5, 5) });
  339. FDigits.Add(new Point[] { new Point(5, 5), new Point(0, 5), new Point(0, 0), new Point(5, 0), new Point(5, 5), new Point(0, 10) });
  340. }
  341. }
  342. }