HtexTable.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. using DocumentFormat.OpenXml;
  2. using DocumentFormat.OpenXml.Drawing;
  3. using DocumentFormat.OpenXml.Drawing.Diagrams;
  4. using DocumentFormat.OpenXml.Packaging;
  5. using DocumentFormat.OpenXml.Presentation;
  6. using HTEXLib.Helpers.ShapeHelpers;
  7. using HTEXLib.Models.Inner;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Drawing;
  11. using System.Linq;
  12. using System.Text;
  13. namespace HTEXLib.Models.HTEX
  14. {
  15. public class HtexTable: HtexElement
  16. {
  17. public HtexTable(string id, double rot, double width, double height,
  18. double top, double left, bool invisible,
  19. bool animatable, int index, DocumentFormat.OpenXml.Drawing.Table table, PPTSlide slide, string partForm) {
  20. base.slide = slide;
  21. this.rot = rot;
  22. this.Table = table;
  23. base.id = id;
  24. base.top = top;
  25. base.left = left;
  26. base.width = width;
  27. base.height = height;
  28. base.invisible = invisible;
  29. base.animatable = animatable;
  30. base.index = index;
  31. base.type = "Table";
  32. base.partForm = partForm;
  33. }
  34. public override List<Item> DrawElement() {
  35. Position position = new Position { cx = width, cy = height, x = left, y = top, rot = rot };
  36. TbStyle tbStyle = PPTXHelper.DoTableProperties(Table.TableProperties, slide, type, partForm);
  37. List<GridColumn> columns = Table.TableGrid.Elements<GridColumn>().ToList();
  38. tbStyle.ColumnWidth = columns.Select(x => x.Width * Globals.px96 * 1.0 / Globals.px914400).ToList();
  39. List<TableRow> rows = Table.Elements<TableRow>().ToList();
  40. var trs = DoTableRow(rows, slide, type, partForm);
  41. Table table = new Table() { tr = trs, style = tbStyle, position = position, type =type, index = index, animatable = animatable, invisible = invisible };
  42. var elmt = new List<Item>();
  43. elmt.Add(table);
  44. return elmt;
  45. }
  46. public DocumentFormat.OpenXml.Drawing.Table Table { get; set; }
  47. public List<Tr> DoTableRow(List<TableRow> rows, PPTSlide slide, string type, string partForm)
  48. {
  49. List<Tr> trs = new List<Tr>();
  50. foreach (TableRow row in rows)
  51. {
  52. Tr tr = new Tr { height = row.Height };
  53. var r = row.Elements<TableCell>();
  54. foreach (TableCell cell in r)
  55. {
  56. CellStyle cellStyle = PPTXHelper.DoTableCellProperties(cell.TableCellProperties, slide, type, partForm);
  57. Td td = new Td();
  58. if (cell != null)
  59. {
  60. if (cell.RowSpan != null)
  61. {
  62. td.rowspan = cell.RowSpan;
  63. }
  64. if (cell.GridSpan != null)
  65. {
  66. td.colspan = cell.GridSpan;
  67. }
  68. if (cell.HorizontalMerge != null)
  69. {
  70. td.hmerge = cell.HorizontalMerge;
  71. }
  72. if (cell.VerticalMerge != null)
  73. {
  74. td.vmerge = cell.VerticalMerge;
  75. }
  76. }
  77. td.style = cellStyle;
  78. LinkedList<PPTParagraph> PPTParagraphs = DoTextBody(cell.TextBody, slide.SlidePart, slide.shapeListStyleMaster, slide.shapeListStyleLayout);
  79. List<Paragraph> paragraphs = DrawText(PPTParagraphs);
  80. td.paragraphs = paragraphs;
  81. tr.td.Add(td);
  82. }
  83. trs.Add(tr);
  84. }
  85. return trs;
  86. }
  87. public List<Paragraph> DrawText(LinkedList<PPTParagraph> Texts)
  88. {
  89. List<Paragraph> Paragraphs = new List<Paragraph>();
  90. foreach (var par in Texts)
  91. {
  92. double paragraphTop = par.getSpaceBeforePoints();
  93. Paragraph paragraph = new HTEXLib.Paragraph { animatable = par.Animatable, invisible = par.Invisible };
  94. int newTop = par.getSpaceBeforePoints();
  95. int left = 0;
  96. List<HtexText> textElements = new List<HtexText>();
  97. if (par.RunPropList == null || par.RunPropList.Count == 0 && par.defaultRunProperties != null) //Only paragraph!
  98. {
  99. float points = float.Parse(par.defaultRunProperties.FontSize.ToString()) * 72.0F / 96.0F;
  100. System.Drawing.Font font = new System.Drawing.Font(par.defaultRunProperties.FontFamily.ToString(), points);
  101. newTop = font.Height;
  102. }
  103. List<HtexText> processedElements = new List<HtexText>();
  104. IEnumerable<PPTRunProperties> pPTRunProperties = breakTextsToShape(par);
  105. foreach (var text in pPTRunProperties)
  106. {
  107. float points = float.Parse(text.FontSize.ToString()) * 72.0F / 96.0F;
  108. System.Drawing.Font font = new System.Drawing.Font(text.FontFamily.ToString(), points);
  109. if (text.Bold) font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Bold);
  110. else if (text.Italic)
  111. font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Italic);
  112. else if (text.Underline != null && text.Underline.Equals("Single"))
  113. font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Underline);
  114. newTop = font.Height > newTop ? font.Height : newTop;
  115. newTop = par.getLineSpacingInPointsFromFont(newTop);
  116. if (text.isBreak)
  117. {
  118. top += newTop;
  119. left = 0;
  120. fixLeftSpacingForAlignment(processedElements, par, font);
  121. processedElements.Clear();
  122. continue;
  123. }
  124. String currentString = text.Text.TrimEnd() + getStringFromTextElements(processedElements);
  125. HtexText t1 = new HtexText(left: left,
  126. top: top,
  127. fontFamily: text.FontFamily,
  128. fontColor: text.FontColor,
  129. fontSize: text.FontSize,
  130. isBullet: text.isBullet,
  131. bold: text.Bold,
  132. italic: text.Italic,
  133. underline: text.Underline,
  134. id: id,
  135. slideIndex: slide.slideIndex)
  136. {
  137. Rotate = this.rot
  138. };
  139. t1.width = MeasureString(text.Text, font);
  140. if (text.isBullet && text.Text != null && text.Text.Contains("rId"))
  141. {
  142. t1.PictureBullet = true;
  143. t1.width = text.bulletSize;
  144. t1.bulletSize = text.bulletSize;
  145. newTop = text.bulletSize;
  146. }
  147. t1.Text = text.Text;
  148. textElements.Add(t1);
  149. processedElements.Add(t1);
  150. }
  151. fixLeftSpacingForAlignment(processedElements, par);
  152. HtexText lastTxt = null;
  153. List<HtexText> mergedTextElements = new List<HtexText>();
  154. foreach (HtexText textElement in textElements)
  155. {
  156. if (lastTxt == null || !lastTxt.sameProps(textElement))
  157. mergedTextElements.Add(textElement);
  158. else
  159. mergedTextElements[mergedTextElements.Count - 1].Text += textElement.Text;
  160. lastTxt = textElement;
  161. }
  162. //foreach (HtexText textElement in mergedTextElements) {
  163. // shapeBuilder.Append(textElement.DrawElement());
  164. //}
  165. top += newTop;
  166. top += par.getSpaceAfterPoints(newTop);
  167. top += par.getSpaceBeforePoints(newTop);
  168. paragraph.texts = textElements;
  169. Paragraphs.Add(paragraph);
  170. }
  171. return Paragraphs;
  172. }
  173. private String getStringFromTextElements(List<HtexText> elements)
  174. {
  175. if (elements == null || elements.Count == 0)
  176. return "";
  177. StringBuilder result = new StringBuilder();
  178. foreach (HtexText el in elements)
  179. {
  180. result.Append(el.Text);
  181. }
  182. return result.ToString();
  183. }
  184. //We have two similar methods - this one is better because it measures the whole string with the font.
  185. private void fixLeftSpacingForAlignment(List<HtexText> textElements, PPTParagraph par, System.Drawing.Font font)
  186. {
  187. int combinedWidth = 0;
  188. StringBuilder combinedText = new StringBuilder();
  189. foreach (HtexText textElement in textElements)
  190. {
  191. if (textElement.PictureBullet)
  192. combinedWidth += textElement.bulletSize;
  193. else
  194. combinedText.Append(textElement.Text);
  195. }
  196. int bulletOffset = 0;
  197. if (par.bullet != null && textElements.Count > 0 && !textElements[0].isBullet)
  198. {
  199. bulletOffset = par.bullet.bulletSize;
  200. combinedWidth += par.bullet.bulletSize;
  201. }
  202. combinedWidth += MeasureString(combinedText.ToString(), font);
  203. double firstLeft = 0;
  204. if ("Center".Equals(par.Align))
  205. firstLeft = ((this.width - par.Indent - bulletOffset - par.marginLeft - par.marginRight) - combinedWidth) / 2;
  206. else if ("Right".Equals(par.Align))
  207. firstLeft = (this.width - par.Indent - bulletOffset - par.marginLeft - par.marginRight) - combinedWidth;
  208. combinedText = new StringBuilder();
  209. combinedWidth = 0; //Now used only for picture bullets!
  210. foreach (HtexText textElement in textElements)
  211. {
  212. textElement.setLeft(firstLeft + par.Indent + bulletOffset + par.marginLeft + combinedWidth + MeasureString(combinedText.ToString(), font));
  213. if (textElement.PictureBullet)
  214. combinedWidth += textElement.bulletSize;
  215. else
  216. combinedText.Append(textElement.Text);
  217. }
  218. }
  219. //We have two similar methods. This is worse because it uses the Width property of each element instead of measuring the whole string.
  220. //There is mistake in the calculations coming from that and the difference is bigger when there are more elements.
  221. private void fixLeftSpacingForAlignment(List<HtexText> textElements, PPTParagraph par)
  222. {
  223. int combinedWidth = 0;
  224. foreach (HtexText textElement in textElements)
  225. combinedWidth += textElement.width;
  226. int bulletOffset = 0;
  227. if (par.bullet != null && textElements.Count > 0 && !textElements[0].isBullet)
  228. {
  229. combinedWidth += par.bullet.bulletSize;
  230. bulletOffset = par.bullet.bulletSize;
  231. }
  232. double currentLeft = 0;
  233. if ("Center".Equals(par.Align))
  234. currentLeft = ((this.width - par.Indent - bulletOffset - par.marginLeft - par.marginRight) - combinedWidth) / 2;
  235. else if ("Right".Equals(par.Align))
  236. currentLeft = (this.width - par.Indent - bulletOffset - par.marginLeft - par.marginRight) - combinedWidth;
  237. foreach (HtexText textElement in textElements)
  238. {
  239. textElement.setLeft(currentLeft + par.Indent + bulletOffset + par.marginLeft);
  240. currentLeft += textElement.width;
  241. }
  242. }
  243. private IEnumerable<PPTRunProperties> breakTextsToShape(PPTParagraph par)
  244. {
  245. List<PPTRunProperties> list = par.RunPropList;
  246. List<PPTRunProperties> result = new List<PPTRunProperties>();
  247. String previousToken = null;
  248. int bulletSize = 0;
  249. foreach (var text in list)
  250. {
  251. float points = float.Parse(text.FontSize.ToString()) * 72.0F / 96.0F;
  252. System.Drawing.Font font = new System.Drawing.Font(text.FontFamily.ToString(), points);
  253. if (text.Bold) font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Bold);
  254. else if (text.Italic) font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Italic);
  255. else if (text.Underline != null && text.Underline.Equals("Single"))
  256. font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Underline);
  257. int size = 0;
  258. if (text.isBullet && text.Text != null && text.Text.Contains("rId"))
  259. bulletSize = text.bulletSize;
  260. else
  261. size = MeasureString((previousToken == null ? "" : previousToken + " ") + text.Text, font);
  262. if (text.isBreak || size + bulletSize < this.width - par.Indent - par.marginLeft - par.marginRight)//- Shape.LeftInset - Shape.RightInset)
  263. {
  264. if (text.Text != null && text.Text.Trim() != "" && !(text.isBullet && text.Text.Contains("rId")))
  265. {
  266. previousToken = (previousToken == null ? "" : previousToken) + text.Text;
  267. }
  268. if (text.isBreak)
  269. previousToken = null;
  270. result.Add(text);
  271. continue;
  272. }
  273. // previousToken = null;
  274. string[] tokens = text.Text.Split(' ');
  275. int index = 0;
  276. foreach (string token in tokens)
  277. {
  278. index++;
  279. int combinedSize = MeasureString((previousToken == null ? "" : previousToken + " ") + token, font);
  280. if (combinedSize + bulletSize > this.width - par.Indent - par.marginLeft - par.marginRight)//- Shape.LeftInset - Shape.RightInset)
  281. {
  282. PPTRunProperties temp = new PPTRunProperties(text);
  283. temp.Text = "";
  284. temp.isBreak = true;
  285. result.Add(temp);
  286. temp = new PPTRunProperties(text);
  287. temp.Text = index < tokens.Length ? token + " " : token;
  288. result.Add(temp);
  289. previousToken = token;
  290. }
  291. else
  292. {
  293. PPTRunProperties temp = new PPTRunProperties(text);
  294. temp.Text = index < tokens.Length ? token + " " : token;
  295. result.Add(temp);
  296. previousToken = (previousToken == null ? "" : previousToken + " ") + token;
  297. }
  298. }
  299. }
  300. return result;
  301. }
  302. public static int MeasureString(string s, System.Drawing.Font font)
  303. {
  304. s = s.Replace("\t", "aaaa");//TODO the replace is dirty hack for measuring tabulations
  305. StringFormat stringFormat = new StringFormat(StringFormat.GenericTypographic);
  306. CharacterRange[] rng = { new CharacterRange(0, s.Length) };
  307. stringFormat.SetMeasurableCharacterRanges(rng);
  308. Graphics g = Graphics.FromImage(new Bitmap(100, 100));
  309. //Use measure character ranges with a big box because we used this for measurement only
  310. //Later we might better use this for drawing the text.
  311. Region[] regions = g.MeasureCharacterRanges(s, font, new System.Drawing.Rectangle(0, 0, 10000, 3000), stringFormat);
  312. foreach (Region region in regions)
  313. {
  314. RectangleF rect = region.GetBounds(g);
  315. return (int)System.Math.Round(rect.Width);
  316. }
  317. return 0;
  318. //
  319. // SizeF result;
  320. // using (var image = new Bitmap(1, 1))
  321. // {
  322. // using (var g = Graphics.FromImage(image))
  323. // {
  324. // result = g.MeasureString(s, font);
  325. // }
  326. // }
  327. //
  328. // return result.ToSize();
  329. }
  330. public LinkedList<PPTParagraph> DoTextBody(DocumentFormat.OpenXml.Drawing.TextBody TextBody, OpenXmlPart slidePart, ListStyle shapeListStyleMaster, ListStyle shapeListStyleLayout)
  331. {
  332. LinkedList<PPTParagraph> Texts = new LinkedList<PPTParagraph>();
  333. bool IsText = true;
  334. int fontScale = 100000;
  335. if (TextBody == null)
  336. {
  337. IsText = false;
  338. return null;
  339. }
  340. if (TextBody.BodyProperties != null)
  341. {
  342. if (TextBody.BodyProperties.Anchor != null)
  343. {
  344. DocumentFormat.OpenXml.Drawing.TextAnchoringTypeValues VerticalAlign = TextBody.BodyProperties.Anchor;
  345. }
  346. if (TextBody.BodyProperties.GetFirstChild<NormalAutoFit>() != null &&
  347. TextBody.BodyProperties.GetFirstChild<NormalAutoFit>().FontScale != null)
  348. {
  349. fontScale = TextBody.BodyProperties.GetFirstChild<NormalAutoFit>().FontScale.Value;
  350. }
  351. }
  352. int index = 0;
  353. foreach (var paragraph in TextBody.Descendants<DocumentFormat.OpenXml.Drawing.Paragraph>())
  354. {
  355. PlaceholderShape placeholder = null;
  356. var par = new PPTParagraph(slide, placeholder)
  357. {
  358. Paragraph = index++
  359. };
  360. if (paragraph.ParagraphProperties != null)
  361. {
  362. int level = paragraph.ParagraphProperties.Level == null ?
  363. -1 : paragraph.ParagraphProperties.Level.Value;
  364. par.Level = level;
  365. }
  366. par.SetParagraphProperties(paragraph, slidePart,
  367. shapeListStyleMaster, shapeListStyleLayout);
  368. bool hasText = false;
  369. foreach (var obj in paragraph.ChildElements)
  370. {
  371. hasText = GetParagraphChildElements(TextBody, par, hasText, obj, fontScale);
  372. }
  373. //This is because when we set paragraph properties we add the bullet to the text runs.
  374. //If we don't have text it still outputs the bullet character.
  375. if (par.bullet != null && hasText)
  376. {
  377. par.RunPropList.Insert(0, par.bullet);
  378. }
  379. Texts.AddLast(par);
  380. }
  381. return Texts;
  382. }
  383. private bool GetParagraphChildElements(DocumentFormat.OpenXml.Drawing.TextBody shape, PPTParagraph par, bool hasText, OpenXmlElement obj, int fontScale)
  384. {
  385. LinkedList<string> effectShapes = new LinkedList<string>();
  386. if (obj is Run)
  387. {
  388. Run run = (Run)obj;
  389. hasText = true;
  390. PPTRunProperties runProp = new PPTRunProperties(par.defaultRunProperties);
  391. runProp.Text = run.Text.Text;
  392. runProp.SetRunProperties(run.RunProperties, shape, ref effectShapes);
  393. runProp.FontSize = System.Math.Round(fontScale * runProp.FontSize / Globals.PercentageConstant);
  394. par.RunPropList.Add(runProp);
  395. }
  396. else if (obj is Field)
  397. {
  398. Field run = (Field)obj;
  399. hasText = true;
  400. PPTRunProperties runProp = new PPTRunProperties(par.defaultRunProperties);
  401. runProp.Text = run.Text.Text;
  402. runProp.SetRunProperties(run.RunProperties, shape, ref effectShapes);
  403. runProp.FontSize = System.Math.Round(fontScale * runProp.FontSize / Globals.PercentageConstant);
  404. par.RunPropList.Add(runProp);
  405. }
  406. else if (obj is Break)
  407. {
  408. Break aBreak = (Break)obj;
  409. PPTRunProperties runProp = new PPTRunProperties(par.defaultRunProperties);
  410. runProp.SetRunProperties(aBreak.RunProperties, shape, ref effectShapes);
  411. runProp.FontSize = System.Math.Round(fontScale * runProp.FontSize / Globals.PercentageConstant);
  412. runProp.isBreak = true;
  413. par.RunPropList.Add(runProp);
  414. }
  415. return hasText;
  416. }
  417. }
  418. }