HtexTable.cs 23 KB

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