123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- using DocumentFormat.OpenXml.Drawing;
- using HTEXLib.Helpers.ShapeHelpers;
- using HTEXLib.Models.Inner;
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- namespace HTEXLib.Models.HTEX
- {
- public class HtexShape : HtexElement
- {
- public PPTShape Shape { get; set; }
- // public double Rotate { get; set; }
- public string HyperLink { get; set; }
- private int slideIndex;
- public HtexShape(string id, double rot, double width, double height,
- double top, double left, bool invisible,
- bool animatable, int slideIndex, int index, PPTShape shape, PPTSlide slide, string partForm)
- {
- base.slide = slide;
- this.rot = rot;
- this.Shape = shape;
- base.id = id;
- base.top = top;
- base.left = left;
- base.width = width;
- base.height = height;
- base.invisible = invisible;
- base.animatable = animatable;
- this.slideIndex = slideIndex;
- base.index = index;
- base.type = "Sp";
- base.partForm = partForm;
- }
-
- public override List<Item> DrawElement()
- {
- if (Shape.placeholder != null && partForm== "layout") {
- return null;
- }
- // string style = invisible ? "DC0" : "DC1";
-
- Position position = new Position { cx= width, cy= height, x=left,y=top,rot=rot};
- var ShapeStyle =PPTXHelper.DoShapeProperties(Shape.element.ShapeProperties, slide,type,partForm);
- //position = position ,mediaType = "image",type = type,index = index,animatable = animatable ,invisible = invisible
- Shape shape = new Shape() { paragraph = DrawText() ,type=type,index=index,animatable=animatable,invisible=invisible };
- shape.style.position = position;
- var shapeTypeNode = Shape.element.ShapeProperties.GetFirstChild<PresetGeometry>();
- var shapeTypeCustom = Shape.element.ShapeProperties.GetFirstChild<CustomGeometry>();
-
- SlideColor slideColor = PPTXHelper.DoShapeStyle(Shape.element.ShapeStyle, slide,type);
- //从ShapeProperties 获取 p:spPr
- //再从 ShapeStyle 获取 p:spPr
- if (ShapeStyle.border == null || ShapeStyle.border.color == null || ShapeStyle.border.color.type == -1)
- {
- if (slideColor != null)
- {
- shape.style.border.color.solidFill = slideColor.LineColor;
- shape.style.border.color.type = 2;
- }
- }
- else {
- shape.style.border = ShapeStyle.border;
- }
- if (ShapeStyle.fill == null || ShapeStyle.fill.type == -1)
- {
- if (slideColor != null)
- {
- shape.style.fill.solidFill = slideColor.FillColor;
- shape.style.fill.type = 2;
- }
- }
- else {
- shape.style.fill = ShapeStyle.fill;
- }
-
- if (shape.paragraph != null) {
- shape.paragraph.ForEach(x => {
- if (x.texts != null) {
- x.texts.ForEach(y => {
- if (y.style.color == null) {
- if (slideColor != null && slideColor.FontColor != null)
- {
- y.style.color = slideColor.FontColor;
- }
- else {
- y.style.color = "#000000";
- }
- }
- });
- }
- });
- }
- if (shapeTypeNode != null && shapeTypeNode.Preset!=null && !string.IsNullOrEmpty(shapeTypeNode.Preset.InnerText)) {
- var shapeType = shapeTypeNode.Preset.InnerText;
- Svg svg = PPTXSvg.GenShapeSvg(Shape.element, index, shapeType, position, shape.style.border);
- shape.svg = svg;
- shape.shapeType = shapeType;
- }
- if (shapeTypeCustom!=null) {
- var pathlist= shapeTypeCustom.GetFirstChild<PathList>();
- var path= pathlist.GetFirstChild<DocumentFormat.OpenXml.Drawing.Path>();
- var pathChildren = path.ChildElements;
- if (pathChildren != null) {
- var start= pathChildren.Where(x =>x is MoveTo).FirstOrDefault();
- var close= pathChildren.Where(x => x is CloseShapePath).FirstOrDefault();
- var spX = 96.0;
- var spY = 96.0;
- if (start != null) {
- var point = start.GetFirstChild<DocumentFormat.OpenXml.Drawing.Point>();
- spX = System.Math.Round(double.Parse(point.X.Value )* Globals.px96 *1.0/ Globals.px914400, Globals. degree);
- spY = System.Math.Round(double.Parse(point.Y.Value) * Globals.px96 * 1.0 / Globals.px914400, Globals.degree);
- }
- var d = "M" + spX + "," + spY;
- foreach (var child in pathChildren) {
- if (child is LineTo lineTo) {
- var elm= lineTo.GetFirstChild<DocumentFormat.OpenXml.Drawing.Point>();
- if (elm != null) {
- var Lx = System.Math.Round(double.Parse(elm.X.Value) * Globals.px96 * 1.0 / Globals.px914400, Globals.degree);
- var Ly = System.Math.Round(double.Parse(elm.Y.Value) * Globals.px96 * 1.0 / Globals.px914400, Globals.degree);
- d += " L" + Lx + "," + Ly;
- }
- }
- if (child is ArcTo arcTo)
- {
- var WidthRadius = arcTo.WidthRadius;
- var HeightRadius = arcTo.HeightRadius;
- var StartAngle = arcTo.StartAngle;
- var SwingAngle = arcTo.SwingAngle;
- var wR= System.Math.Round(double.Parse(WidthRadius.Value) * Globals.px96 * 1.0 / Globals.px914400, Globals.degree);
- var hR = System.Math.Round(double.Parse(HeightRadius.Value) * Globals.px96 * 1.0 / Globals.px914400, Globals.degree);
- var stAng = System.Math.Round(double.Parse(StartAngle.Value) * Globals.px96 * 1.0 / Globals.px914400, Globals.degree);
- var swAng = System.Math.Round(double.Parse(SwingAngle.Value) * Globals.px96 * 1.0 / Globals.px914400, Globals.degree);
- var endAng = stAng + swAng;
- d +=PPTXSvg .ShapeArc(wR, hR, wR, hR, stAng, endAng, false);
- }
- if (child is QuadraticBezierCurveTo quadraticBezierCurveTo)
- {
- var elms= quadraticBezierCurveTo.Elements<DocumentFormat.OpenXml.Drawing.Point>();
- if (elms != null) {
- var list = elms.ToList();
- var Cx1 = double.Parse(list[0].X.Value) * Globals.px96 * 1.0 / Globals.px914400;
- var Cy1 = double.Parse(list[0].Y.Value) * Globals.px96 * 1.0 / Globals.px914400;
- var Cx2 = double.Parse(list[1].X.Value) * Globals.px96 * 1.0 / Globals.px914400;
- var Cy2 = double.Parse(list[1].Y.Value) * Globals.px96 * 1.0 / Globals.px914400;
- var Cx3 = double.Parse(list[2].X.Value) * Globals.px96 * 1.0 / Globals.px914400;
- var Cy3 = double.Parse(list[2].Y.Value) * Globals.px96 * 1.0 / Globals.px914400;
- d += " C" + System.Math.Round(Cx1, Globals.degree) + "," + System.Math.Round(Cy1, Globals.degree) + " " + System.Math.Round(Cx2, Globals.degree) + "," + System.Math.Round(Cy2, Globals.degree) + " " + System.Math.Round(Cx3, Globals.degree) + "," + System.Math.Round(Cy3, Globals.degree);
- }
- }
- if (child is CubicBezierCurveTo cubicBezierCurveTo)
- {
- var elms = cubicBezierCurveTo.Elements<DocumentFormat.OpenXml.Drawing.Point>();
- if (elms != null)
- {
- var list = elms.ToList();
- var Cx1 = double.Parse(list[0].X.Value) * Globals.px96 * 1.0 / Globals.px914400;
- var Cy1 = double.Parse(list[0].Y.Value) * Globals.px96 * 1.0 / Globals.px914400;
- var Cx2 = double.Parse(list[1].X.Value) * Globals.px96 * 1.0 / Globals.px914400;
- var Cy2 = double.Parse(list[1].Y.Value) * Globals.px96 * 1.0 / Globals.px914400;
- d += " Q" + System.Math.Round(Cx1, Globals.degree) + "," + System.Math.Round(Cy1, Globals.degree) + " " + System.Math.Round(Cx2, Globals.degree) + "," + System.Math.Round(Cy2, Globals.degree);
- }
- }
- }
- //是否关闭svg
- if (close != null)
- {
- d += " z";
- }
- List<SvgShape> shapes = new List<SvgShape>() {
- new SvgPath {
- type = "path",
- // Fill = (!imgFillFlg ? (grndFillFlg ? "url(#linGrd_" + order + ")" : Fill.Color) : "url(#imgPtrn_" + order + ")"),
- //Stroke = "#" + shapeBorder.Color,
- //StrokeWidth = shapeBorder.Width + "",
- //StrokeDasharray = shapeBorder.Stroke,
- d=d,
- start=shape.style.border.headEnd!=null?shape.style.border.headEnd:null,
- end=shape.style.border.tailEnd!=null?shape.style.border.tailEnd:null,
- }
- };
- shape.svg = new Svg {
- id=index+"",
- svgShape=shapes
- };
- shape.shapeType = "custom";
- }
- }
- var elmt = new List<Item> ();
- shape.uid = Shape.suid;
- elmt.Add(shape);
- return elmt;
- }
- public List<Paragraph> DrawText() {
- double top = getTopForCurrentAnchoring(this.Shape.VerticalAlign, this.Shape.Texts);
- List<Paragraph> Paragraphs = new List<Paragraph>();
- foreach (var par in Shape.Texts)
- {
- double paragraphTop = this.top + (this.Shape.VerticalAlign.Equals(DocumentFormat.OpenXml.Drawing.TextAnchoringTypeValues.Top) ? par.getSpaceBeforePoints() : 0);
- Paragraph paragraph= new HTEXLib.Paragraph { animatable = par.Animatable ,invisible=par.Invisible};
- paragraph.style.position.y = paragraphTop;
- paragraph.style.position.x =this.left;
- paragraph.style.position.cx = width;
- paragraph.style.position.cy = height;
- paragraph.style.position.rot = rot;
- paragraph.style.vert = this.Shape.VerticalAlign.ToString();
- paragraph.style.hori = par.Align;
- paragraph.style.writing = Shape.WritingMode;
- if (par.bullet != null) {
- var bullet = par.bullet;
- paragraph.buChar = new BuChar
- {
- type = bullet.BulletType,
- left = bullet.Left ,
- buchar=bullet.Text,
- color=bullet.FontColor,
- typeface=bullet.FontFamily,
- size=bullet.FontSize,
- };
- }
-
- int newTop = par.getSpaceBeforePoints();
- int left = 0;
- List<HtexText> textElements = new List<HtexText>();
- if (par.RunPropList == null || par.RunPropList.Count == 0 && par.defaultRunProperties != null) //Only paragraph!
- {
- float points = float.Parse(par.defaultRunProperties.FontSize.ToString()) * 72.0F / 96.0F;
- Font font = new System.Drawing.Font(par.defaultRunProperties.FontFamily.ToString(), points);
- newTop = font.Height;
- }
- List<HtexText> processedElements = new List<HtexText>();
- List<Text> texts = new List<Text>();
- IEnumerable<PPTRunProperties> pPTRunProperties = breakTextsToShape(par);
- foreach (var text in pPTRunProperties)
- {
- float points = float.Parse(text.FontSize.ToString()) * 72.0F / 96.0F;
- Font font = new System.Drawing.Font(text.FontFamily.ToString(), points);
- if (text.Bold) font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Bold);
- else if (text.Italic)
- font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Italic);
- else if (text.Underline != null && text.Underline.Equals("Single"))
- font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Underline);
- newTop = font.Height > newTop ? font.Height : newTop;
- newTop = par.getLineSpacingInPointsFromFont(newTop);
- if (text.isBreak)
- {
- top += newTop;
- left = 0;
- fixLeftSpacingForAlignment(processedElements, par, font);
- processedElements.Clear();
- continue;
- }
- String currentString = text.Text.TrimEnd() + getStringFromTextElements(processedElements);
- //Text must already be broken to lines
- //Size size = MeasureString(currentString, font);
- // if (size.Width > this.width - par.Indent - par.marginLeft - par.marginRight)
- // {
- // top += newTop;
- // left = 0;
- // fixLeftSpacingForAlignment(processedElements, par, font);
- // processedElements.Clear();
- // }
- HtexText t1 = new HtexText(left: left,
- top: top,
- fontFamily: text.FontFamily,
- fontColor: text.FontColor,
- fontSize: text.FontSize,
- isBullet: text.isBullet,
- bold: text.Bold,
- italic: text.Italic,
- underline: text.Underline,
- id: id,
- slideIndex: slideIndex)
- {
- Rotate = this.rot
- };
- t1.width = MeasureString(text.Text, font);
- if (text.isBullet && text.Text != null && text.Text.Contains("rId"))
- {
- t1.PictureBullet = true;
- t1.width = text.bulletSize;
- t1.bulletSize = text.bulletSize;
- newTop = text.bulletSize;
- }
-
- t1.Text = text.Text;
- textElements.Add(t1);
- texts.Add(new Text
- {
- content = t1.Text,
- link = text.link,
- linkType = text.linkType,
- style = new FontStyle
- {
- align=par.FontAlign,
- spacing=par.defTabSize,
- color = text.FontColor,
- family = text.FontFamily,
- top = top,
- left = left,
- size = text.FontSize,
- isBullet = text.isBullet,
- underline = t1.underline,
- italic = text.Italic,
- bold = text.Bold,
- rot = this.rot,
- width = t1.width,
- pictureBullet = t1.PictureBullet,
- bulletSize = t1.bulletSize
- }
- });
- processedElements.Add(t1);
- }
- fixLeftSpacingForAlignment(processedElements, par);
- HtexText lastTxt = null;
- List<HtexText> mergedTextElements = new List<HtexText>();
- foreach (HtexText textElement in textElements)
- {
- if (lastTxt == null || !lastTxt.sameProps(textElement))
- mergedTextElements.Add(textElement);
- else
- mergedTextElements[mergedTextElements.Count - 1].Text += textElement.Text;
- lastTxt = textElement;
- }
- //foreach (HtexText textElement in mergedTextElements) {
- // shapeBuilder.Append(textElement.DrawElement());
- //}
- top += newTop;
- top += par.getSpaceAfterPoints(newTop);
- top += par.getSpaceBeforePoints(newTop);
- paragraph.texts = texts;
- Paragraphs.Add(paragraph);
- }
- return Paragraphs;
- }
- private String getStringFromTextElements(List<HtexText> elements)
- {
- if (elements == null || elements.Count == 0)
- return "";
- StringBuilder result = new StringBuilder();
- foreach (HtexText el in elements)
- {
- result.Append(el.Text);
- }
- return result.ToString();
- }
- //We have two similar methods. This is worse because it uses the Width property of each element instead of measuring the whole string.
- //There is mistake in the calculations coming from that and the difference is bigger when there are more elements.
- private void fixLeftSpacingForAlignment(List<HtexText> textElements, PPTParagraph par)
- {
- int combinedWidth = 0;
- foreach (HtexText textElement in textElements)
- combinedWidth += textElement.width;
- int bulletOffset = 0;
- if (par.bullet != null && textElements.Count > 0 && !textElements[0].isBullet)
- {
- combinedWidth += par.bullet.bulletSize;
- bulletOffset = par.bullet.bulletSize;
- }
- double currentLeft = 0;
- if ("Center".Equals(par.Align))
- currentLeft = ((this.width - par.Indent - bulletOffset - par.marginLeft - par.marginRight - Shape.LeftInset - Shape.RightInset) - combinedWidth) / 2;
- else if ("Right".Equals(par.Align))
- currentLeft = (this.width - par.Indent - bulletOffset - par.marginLeft - par.marginRight - Shape.LeftInset - Shape.RightInset) - combinedWidth;
- foreach (HtexText textElement in textElements)
- {
- textElement.setLeft(currentLeft + par.Indent + bulletOffset + par.marginLeft + Shape.LeftInset);
- currentLeft += textElement.width;
- }
- }
- //We have two similar methods - this one is better because it measures the whole string with the font.
- private void fixLeftSpacingForAlignment(List<HtexText> textElements, PPTParagraph par, Font font)
- {
- int combinedWidth = 0;
- StringBuilder combinedText = new StringBuilder();
- foreach (HtexText textElement in textElements)
- {
- if (textElement.PictureBullet)
- combinedWidth += textElement.bulletSize;
- else
- combinedText.Append(textElement.Text);
- }
- int bulletOffset = 0;
- if (par.bullet != null && textElements.Count > 0 && !textElements[0].isBullet)
- {
- bulletOffset = par.bullet.bulletSize;
- combinedWidth += par.bullet.bulletSize;
- }
- combinedWidth += MeasureString(combinedText.ToString(), font);
- double firstLeft = 0;
- if ("Center".Equals(par.Align))
- firstLeft = ((this.width - par.Indent - bulletOffset - par.marginLeft - par.marginRight - Shape.LeftInset - Shape.RightInset) - combinedWidth) / 2;
- else if ("Right".Equals(par.Align))
- firstLeft = (this.width - par.Indent - bulletOffset - par.marginLeft - par.marginRight - Shape.LeftInset - Shape.RightInset) - combinedWidth;
- combinedText = new StringBuilder();
- combinedWidth = 0; //Now used only for picture bullets!
- foreach (HtexText textElement in textElements)
- {
- textElement.setLeft(firstLeft + par.Indent + bulletOffset + par.marginLeft + combinedWidth + Shape.LeftInset + MeasureString(combinedText.ToString(), font));
- if (textElement.PictureBullet)
- combinedWidth += textElement.bulletSize;
- else
- combinedText.Append(textElement.Text);
- }
- }
- private double getTopForCurrentAnchoring(DocumentFormat.OpenXml.Drawing.TextAnchoringTypeValues anchoring, LinkedList<PPTParagraph> paragraphList)
- {
- if (anchoring.Equals(DocumentFormat.OpenXml.Drawing.TextAnchoringTypeValues.Top))
- return Shape.TopInset;
- double combinedHeight = 0;
- foreach (PPTParagraph par in paragraphList)
- {
- int newTop = 0;
- IEnumerable<PPTRunProperties> splitText = breakTextsToShape(par);
- foreach (var text in splitText)
- {
- float points = float.Parse(text.FontSize.ToString()) * 72.0F / 96.0F;
- Font font = new System.Drawing.Font(text.FontFamily.ToString(), points);
- if (text.Bold) font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Bold);
- else if (text.Italic) font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Italic);
- else if (text.Underline != null && text.Underline.Equals("Single"))
- font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Underline);
- if (text.isBreak)
- {
- combinedHeight += newTop;
- newTop = font.Height;
- continue;
- }
- newTop = font.Height > newTop ? font.Height : newTop;
- }
- combinedHeight += par.getLineSpacingInPointsFromFont(newTop);
- combinedHeight += par.getSpaceBeforePoints(newTop);
- combinedHeight += par.getSpaceAfterPoints(newTop);
- }
- combinedHeight += Shape.TopInset + Shape.BottomInset;
- if (anchoring.Equals(DocumentFormat.OpenXml.Drawing.TextAnchoringTypeValues.Bottom))
- return this.height - combinedHeight;
- return (this.height - combinedHeight) / 2; //Center align
- }
- private IEnumerable<PPTRunProperties> breakTextsToShape(PPTParagraph par)
- {
- List<PPTRunProperties> list = par.RunPropList;
- List<PPTRunProperties> result = new List<PPTRunProperties>();
- String previousToken = null;
- int bulletSize = 0;
- foreach (var text in list)
- {
- float points = float.Parse(text.FontSize.ToString()) * 72.0F / 96.0F;
- Font font = new System.Drawing.Font(text.FontFamily.ToString(), points);
- if (text.Bold) font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Bold);
- else if (text.Italic) font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Italic);
- else if (text.Underline != null && text.Underline.Equals("Single"))
- font = new System.Drawing.Font(text.FontFamily.ToString(), points, System.Drawing.FontStyle.Underline);
- int size = 0;
- if (text.isBullet && text.Text != null && text.Text.Contains("rId"))
- bulletSize = text.bulletSize;
- else
- size = MeasureString((previousToken == null ? "" : previousToken + " ") + text.Text, font);
- if (text.isBreak || size + bulletSize < this.width - par.Indent - par.marginLeft - par.marginRight - Shape.LeftInset - Shape.RightInset)
- {
- if (text.Text != null && text.Text.Trim() != "" && !(text.isBullet && text.Text.Contains("rId")))
- {
- previousToken = (previousToken == null ? "" : previousToken) + text.Text;
- }
- if (text.isBreak)
- previousToken = null;
- result.Add(text);
- continue;
- }
- // previousToken = null;
- string[] tokens = text.Text.Split(' ');
- int index = 0;
- foreach (string token in tokens)
- {
- index++;
- int combinedSize = MeasureString((previousToken == null ? "" : previousToken + " ") + token, font);
- if (combinedSize + bulletSize > this.width - par.Indent - par.marginLeft - par.marginRight - Shape.LeftInset - Shape.RightInset)
- {
- PPTRunProperties temp = new PPTRunProperties(text);
- temp.Text = "";
- temp.isBreak = true;
- result.Add(temp);
- temp = new PPTRunProperties(text);
- temp.Text = index < tokens.Length ? token + " " : token;
- result.Add(temp);
- previousToken = token;
- }
- else
- {
- PPTRunProperties temp = new PPTRunProperties(text);
- temp.Text = index < tokens.Length ? token + " " : token;
- result.Add(temp);
- previousToken = (previousToken == null ? "" : previousToken + " ") + token;
- }
- }
- }
- return result;
- }
- public static int MeasureString(string s, Font font)
- {
- s = s.Replace("\t", "aaaa");//TODO the replace is dirty hack for measuring tabulations
- StringFormat stringFormat = new StringFormat(StringFormat.GenericTypographic);
- CharacterRange[] rng = { new CharacterRange(0, s.Length) };
- stringFormat.SetMeasurableCharacterRanges(rng);
- Graphics g = Graphics.FromImage(new Bitmap(100, 100));
- //Use measure character ranges with a big box because we used this for measurement only
- //Later we might better use this for drawing the text.
- Region[] regions = g.MeasureCharacterRanges(s, font, new System.Drawing.Rectangle(0, 0, 10000, 3000), stringFormat);
- foreach (Region region in regions)
- {
- RectangleF rect = region.GetBounds(g);
- return (int)System.Math.Round(rect.Width);
- }
- return 0;
- //
- // SizeF result;
- // using (var image = new Bitmap(1, 1))
- // {
- // using (var g = Graphics.FromImage(image))
- // {
- // result = g.MeasureString(s, font);
- // }
- // }
- //
- // return result.ToSize();
- }
- }
- }
|