PreparedPagePostprocessor.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. using FastReport.Utils;
  2. using System;
  3. using System.Collections.Generic;
  4. namespace FastReport.Preview
  5. {
  6. internal class PreparedPagePostprocessor
  7. {
  8. private Dictionary<string, List<TextObjectBase>> duplicates;
  9. private Dictionary<string, List<TextObject>> mergedTextObjects;
  10. private Dictionary<int, Base> bands;
  11. int iBand;
  12. private void ProcessDuplicates(TextObjectBase obj)
  13. {
  14. if (duplicates.ContainsKey(obj.Name))
  15. {
  16. List<TextObjectBase> list = duplicates[obj.Name];
  17. TextObjectBase lastObj = list[list.Count - 1];
  18. bool isDuplicate = true;
  19. // compare Text
  20. if (obj.Text != lastObj.Text)
  21. isDuplicate = false;
  22. else
  23. {
  24. float lastObjBottom = (lastObj.Parent as ReportComponentBase).Bottom;
  25. float objTop = (obj.Parent as ReportComponentBase).Top;
  26. if (Math.Abs(objTop - lastObjBottom) > 0.5f)
  27. isDuplicate = false;
  28. }
  29. if (isDuplicate)
  30. {
  31. list.Add(obj);
  32. }
  33. else
  34. {
  35. // close duplicates
  36. CloseDuplicates(list);
  37. // add new obj
  38. list.Clear();
  39. list.Add(obj);
  40. }
  41. }
  42. else
  43. {
  44. List<TextObjectBase> list = new List<TextObjectBase>();
  45. list.Add(obj);
  46. duplicates.Add(obj.Name, list);
  47. }
  48. }
  49. private void CloseDuplicates()
  50. {
  51. foreach (List<TextObjectBase> list in duplicates.Values)
  52. {
  53. CloseDuplicates(list);
  54. }
  55. }
  56. private void CloseDuplicates(List<TextObjectBase> list)
  57. {
  58. if (list.Count == 0)
  59. return;
  60. Duplicates duplicates = list[0].Duplicates;
  61. switch (duplicates)
  62. {
  63. case Duplicates.Clear:
  64. CloseDuplicatesClear(list);
  65. break;
  66. case Duplicates.Hide:
  67. CloseDuplicatesHide(list);
  68. break;
  69. case Duplicates.Merge:
  70. CloseDuplicatesMerge(list);
  71. break;
  72. }
  73. }
  74. private void CloseDuplicatesClear(List<TextObjectBase> list)
  75. {
  76. for (int i = 0; i < list.Count; i++)
  77. {
  78. if (i > 0)
  79. list[i].Text = "";
  80. }
  81. }
  82. private void CloseDuplicatesHide(List<TextObjectBase> list)
  83. {
  84. for (int i = 0; i < list.Count; i++)
  85. {
  86. if (i > 0)
  87. list[i].Dispose();
  88. }
  89. }
  90. private void CloseDuplicatesMerge(List<TextObjectBase> list)
  91. {
  92. float top = list[0].AbsTop;
  93. // dispose all objects except the last one
  94. for (int i = 0; i < list.Count - 1; i++)
  95. {
  96. list[i].Dispose();
  97. }
  98. // stretch the last object
  99. TextObjectBase lastObj = list[list.Count - 1];
  100. float delta = lastObj.AbsTop - top;
  101. lastObj.Top -= delta;
  102. lastObj.Height += delta;
  103. }
  104. private void CollectMergedTextObjects(TextObject obj)
  105. {
  106. if (mergedTextObjects.ContainsKey(obj.Band.Name))
  107. {
  108. List<TextObject> list = mergedTextObjects[obj.Band.Name];
  109. list.Add(obj);
  110. }
  111. else
  112. {
  113. List<TextObject> list = new List<TextObject>() { obj };
  114. mergedTextObjects.Add(obj.Band.Name, list);
  115. }
  116. }
  117. private void MergeTextObjects
  118. ()
  119. {
  120. foreach (var band in mergedTextObjects)
  121. {
  122. band.Value.Sort(delegate (TextObject txt, TextObject txt2)
  123. {
  124. if (txt.AbsLeft.CompareTo(txt2.AbsLeft) == 0)
  125. return txt.AbsTop.CompareTo(txt2.AbsTop);
  126. return txt.AbsLeft.CompareTo(txt2.AbsLeft);
  127. });
  128. //Vertical merge
  129. MergeTextObjectsInBand(band.Value);
  130. //May be horizontal merge
  131. MergeTextObjectsInBand(band.Value);
  132. }
  133. }
  134. private void MergeTextObjectsInBand(List<TextObject> band)
  135. {
  136. for (int i = 0; i < band.Count; i++)
  137. {
  138. for (int j = i + 1; j < band.Count; j++)
  139. {
  140. if (Merge(band[j], band[i]))
  141. {
  142. TextObject removeObj = band[j];
  143. band.Remove(removeObj);
  144. removeObj.Dispose();
  145. if (j > 0)
  146. j--;
  147. }
  148. }
  149. }
  150. }
  151. private bool Merge(TextObject obj, TextObject obj2)
  152. {
  153. if (obj2.Text != obj.Text)
  154. return false;
  155. var bounds = obj.AbsBounds;
  156. if (bounds.Width < 0 || bounds.Height < 0)
  157. Validator.NormalizeBounds(ref bounds);
  158. var bounds2 = obj2.AbsBounds;
  159. if (bounds2.Width < 0 || bounds2.Height < 0)
  160. Validator.NormalizeBounds(ref bounds2);
  161. if (obj.MergeMode.HasFlag(MergeMode.Vertical) && obj2.MergeMode.HasFlag(MergeMode.Vertical)
  162. && IsEqualWithInaccuracy(bounds2.Width, bounds.Width) && IsEqualWithInaccuracy(bounds2.Left, bounds.Left))
  163. {
  164. if (IsEqualWithInaccuracy(bounds2.Bottom, bounds.Top))
  165. {
  166. obj2.Height += bounds.Height;
  167. return true;
  168. }
  169. else if (IsEqualWithInaccuracy(bounds2.Top, bounds.Bottom))
  170. {
  171. obj2.Height += bounds.Height;
  172. obj2.Top -= bounds.Height;
  173. return true;
  174. }
  175. }
  176. else if (obj.MergeMode.HasFlag(MergeMode.Horizontal) && obj2.MergeMode.HasFlag(MergeMode.Horizontal)
  177. && IsEqualWithInaccuracy(bounds2.Height, bounds.Height) && IsEqualWithInaccuracy(bounds2.Top, bounds.Top))
  178. {
  179. if (IsEqualWithInaccuracy(bounds2.Right, bounds.Left))
  180. {
  181. obj2.Width += bounds.Width;
  182. return true;
  183. }
  184. else if (IsEqualWithInaccuracy(bounds2.Left, bounds.Right))
  185. {
  186. obj2.Width += bounds.Width;
  187. obj2.Left -= bounds.Width;
  188. return true;
  189. }
  190. }
  191. return false;
  192. }
  193. private bool IsEqualWithInaccuracy(float value1, float value2)
  194. {
  195. return Math.Abs(value1 - value2) < 0.01;
  196. }
  197. public void Postprocess(ReportPage page)
  198. {
  199. page.ExtractMacros();
  200. ObjectCollection allObjects = page.AllObjects;
  201. for (int i = 0; i < allObjects.Count; i++)
  202. {
  203. Base c = allObjects[i];
  204. if (c.Report == null)
  205. c.SetReport(page.Report);
  206. c.ExtractMacros();
  207. if (c is BandBase band)
  208. band.UpdateWidth();
  209. if (c is TextObjectBase txt && txt.Duplicates != Duplicates.Show)
  210. ProcessDuplicates(txt);
  211. if (c is TextObject text && text.MergeMode != MergeMode.None)
  212. CollectMergedTextObjects(text);
  213. }
  214. MergeTextObjects();
  215. CloseDuplicates();
  216. }
  217. public PreparedPagePostprocessor()
  218. {
  219. duplicates = new Dictionary<string, List<TextObjectBase>>();
  220. mergedTextObjects = new Dictionary<string, List<TextObject>>();
  221. bands = new Dictionary<int, Base>();
  222. iBand = 0;
  223. }
  224. public void PostprocessUnlimited(PreparedPage preparedPage, ReportPage page)
  225. {
  226. bool flag = false;
  227. int i = 0;
  228. foreach (Base b in preparedPage.GetPageItems(page, true))
  229. {
  230. foreach (Base c in b.AllObjects)
  231. {
  232. if (c is TextObjectBase txt && txt.Duplicates != Duplicates.Show)
  233. {
  234. ProcessDuplicates(txt);
  235. flag = true; //flag for keep in dictionary
  236. }
  237. if (c is TextObject text && text.MergeMode != MergeMode.None)
  238. {
  239. CollectMergedTextObjects(text);
  240. flag = true;
  241. }
  242. }
  243. i++;
  244. if (flag)
  245. {
  246. b.ExtractMacros();
  247. bands[i - 1] = b;
  248. }
  249. else
  250. {
  251. b.Dispose();
  252. }
  253. }
  254. MergeTextObjects();
  255. CloseDuplicates();
  256. }
  257. public Base PostProcessBandUnlimitedPage(Base band)
  258. {
  259. if (bands.ContainsKey(iBand))
  260. {
  261. Base replaceBand = bands[iBand];
  262. Base parent = band.Parent;
  263. band.Parent = null;
  264. replaceBand.Parent = parent;
  265. band.Dispose();
  266. iBand++;
  267. return replaceBand;
  268. }
  269. band.ExtractMacros();
  270. iBand++;
  271. return band;
  272. }
  273. }
  274. }