HtexTable.cs 23 KB

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