ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Pptx的多路径形状转为WPF的Path

2021-07-08 19:34:12  阅读:211  来源: 互联网

标签:Pptx 多路径 currentPoint Shape new var Path stringPath


本文是将演示如何解析pptx文件的多路径的形状转换到WPF,绘制多个Shape的Path

Shape Path

这是Pptx的【标注:弯曲曲线(无边框)】形状的OpenXml定义部分:

  <callout2>
    <avLst xmlns="http://schemas.openxmlformats.org/drawingml/2006/main">
      <gd name="adj1" fmla="val 18750" />
      <gd name="adj2" fmla="val -8333" />
      <gd name="adj3" fmla="val 18750" />
      <gd name="adj4" fmla="val -16667" />
      <gd name="adj5" fmla="val 112500" />
      <gd name="adj6" fmla="val -46667" />
    </avLst>
    <gdLst xmlns="http://schemas.openxmlformats.org/drawingml/2006/main">
      <gd name="y1" fmla="*/ h adj1 100000" />
      <gd name="x1" fmla="*/ w adj2 100000" />
      <gd name="y2" fmla="*/ h adj3 100000" />
      <gd name="x2" fmla="*/ w adj4 100000" />
      <gd name="y3" fmla="*/ h adj5 100000" />
      <gd name="x3" fmla="*/ w adj6 100000" />
    </gdLst>
    <pathLst xmlns="http://schemas.openxmlformats.org/drawingml/2006/main">
      <path stroke="false" extrusionOk="false">
        <moveTo>
          <pt x="l" y="t" />
        </moveTo>
        <lnTo>
          <pt x="r" y="t" />
        </lnTo>
        <lnTo>
          <pt x="r" y="b" />
        </lnTo>
        <lnTo>
          <pt x="l" y="b" />
        </lnTo>
        <close />
      </path>
      <path fill="none" extrusionOk="false">
        <moveTo>
          <pt x="x1" y="y1" />
        </moveTo>
        <lnTo>
          <pt x="x2" y="y2" />
        </lnTo>
        <lnTo>
          <pt x="x3" y="y3" />
        </lnTo>
      </path>
    </pathLst>
  </callout2>

然后以下OpenXml Shape Path的子属性:

属性 类型 备注
extrusionOk (3D Extrusion Allowed) bool 指定使用 3D 拉伸可能在此路径,默认false或0
fill (Path Fill) PathFillMode 路径填充模式:Norm(默认)、None、Lighten、LightenLess、Darken、DarkenLess
stroke (Path Stroke) bool 是否存在轮廓:默认false
h (Path Height) int 指定框架的高度或在路径坐标系统中应在使用的最大的 y 坐标
w (Path Width) int 指定的宽度或在路径坐标系统中应在使用的最大的 x 坐标

首先为什么是要转为多个Shape呢?因为OpenXml每条路径,都能设置是否有轮廓、填充等属性,而该属性设置只能在Shape层,而不能在Geometry层,就算是通过PathGeometry的PathFigure也只能设置IsFilled(是否填充),不能设置IsStroke(是否有轮廓)

解析Pptx形状

首先我们来创建对应Shape Path的类:

    public readonly struct ShapePath
    {
        public ShapePath(string path, FillMode fillMode = FillMode.Norm, bool isStroke = true)
        {
            Path = path;
            IsStroke = isStroke;
            FillMode = fillMode;
            IsFilled = fillMode is not FillMode.None;
        }

        /// <summary>
        /// 是否填充
        /// </summary>
        public bool IsFilled { get; }

        /// <summary>
        /// 是否有边框
        /// </summary>
        public bool IsStroke { get; }

        public FillMode FillMode { get; }

        /// <summary>
        /// Geometry的Path
        /// </summary>
        public string Path { get; }
    }

    public enum FillMode
    {
        /// <summary>
        ///Darken Path Fill
        /// </summary>
        Darken,

        /// <summary>
        /// Darken Path Fill Less
        /// </summary>
        DarkenLess,

        /// <summary>
        /// Lighten Path Fill
        /// </summary>
        Lighten,

        /// <summary>
        /// Lighten Path Fill Less
        /// </summary>
        LightenLess,

        /// <summary>
        /// None Path Fill
        /// </summary>
        None,

        /// <summary>
        /// Normal Path Fill
        /// </summary>
        Norm
    }

解析pptx形状的关键代码:

private void PptxMultiPathToGeometry(string filePath)
{
    if (!File.Exists(filePath) || !filePath.EndsWith(".pptx", StringComparison.OrdinalIgnoreCase))
    {
        MessageBox.Show("请输入正确的pptx文件路径");
        return;
    }
    using (var presentationDocument = PresentationDocument.Open(filePath, false))
    {
        var presentationPart = presentationDocument.PresentationPart;
        var presentation = presentationPart?.Presentation;
        var slideIdList = presentation?.SlideIdList;
        if (slideIdList == null)
        {
            return;
        }
        foreach (var slideId in slideIdList.ChildElements.OfType<SlideId>())
        {
            var slidePart = (SlidePart)presentationPart.GetPartById(slideId.RelationshipId);
            var slide = slidePart.Slide;
            foreach (var shapeProperties in slide.Descendants<ShapeProperties>())
            {
                var presetGeometry = shapeProperties.GetFirstChild<PresetGeometry>();
                if (presetGeometry != null && presetGeometry.Preset.HasValue)
                {
                    if (presetGeometry.Preset == ShapeTypeValues.BorderCallout2)
                    {
                        var transform2D = shapeProperties.GetFirstChild<Transform2D>();
                        var extents = transform2D?.GetFirstChild<Extents>();
                        if (extents != null)
                        {
                            var width = extents.Cx;
                            var height = extents.Cy;
                            if (width.HasValue && height.HasValue)
                            {
                                var geometryPaths = GetGeometryPathFromCallout2(new Emu(width).EmuToPixel().Value, new Emu(height).EmuToPixel().Value);
                                RenderGeometry(geometryPaths);
                            }
                        }
                   }
               }
         }
     }
   }
}

根据openxml的定义算出Shape Path:

        /// <summary>
        /// 获取【标注:弯曲线形】的Shape Path
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        public static List<ShapePath> GetGeometryPathFromCallout2(double width, double height)
        {
            var (h, w, l, r, t, b, hd2, hd4, hd5, hd6, hd8, ss, hc, vc, ls, ss2, ss4, ss6, ss8, wd2, wd4, wd5, wd6, wd8, wd10, cd2, cd4, cd8) = GetFormulaProperties(width, height);
            //<avLst xmlns="http://schemas.openxmlformats.org/drawingml/2006/main">
            //  <gd name="adj1" fmla="val 18750" />
            //  <gd name="adj2" fmla="val -8333" />
            //  <gd name="adj3" fmla="val 18750" />
            //  <gd name="adj4" fmla="val -16667" />
            //  <gd name="adj5" fmla="val 112500" />
            //  <gd name="adj6" fmla="val -46667" />
            //</avLst>
            var adj1 = 18750d;
            var adj2 = -8333d;
            var adj3 = 18750d;
            var adj4 = -16667d;
            var adj5 = 112500d;
            var adj6 = -46667;

            //<gdLst xmlns="http://schemas.openxmlformats.org/drawingml/2006/main">
            //  <gd name="y1" fmla="*/ h adj1 100000" />
            //  <gd name="x1" fmla="*/ w adj2 100000" />
            //  <gd name="y2" fmla="*/ h adj3 100000" />
            //  <gd name="x2" fmla="*/ w adj4 100000" />
            //  <gd name="y3" fmla="*/ h adj5 100000" />
            //  <gd name="x3" fmla="*/ w adj6 100000" />
            //</gdLst>

            //  <gd name="y1" fmla="*/ h adj1 100000" />
            var y1 = h * adj1 / 100000;
            //  <gd name="x1" fmla="*/ w adj2 100000" />
            var x1 = w * adj2 / 100000;
            //  <gd name="y2" fmla="*/ h adj3 100000" />
            var y2 = h * adj3 / 100000;
            //  <gd name="x2" fmla="*/ w adj4 100000" />
            var x2 = w * adj4 / 100000;
            //  <gd name="y3" fmla="*/ h adj5 100000" />
            var y3 = h * adj5 / 100000;
            //  <gd name="x3" fmla="*/ w adj6 100000" />
            var x3 = w * adj6 / 100000;

            // <pathLst xmlns="http://schemas.openxmlformats.org/drawingml/2006/main">
            //  <path extrusionOk="false">
            //    <moveTo>
            //      <pt x="l" y="t" />
            //    </moveTo>
            //    <lnTo>
            //      <pt x="r" y="t" />
            //    </lnTo>
            //    <lnTo>
            //      <pt x="r" y="b" />
            //    </lnTo>
            //    <lnTo>
            //      <pt x="l" y="b" />
            //    </lnTo>
            //    <close />
            //  </path>
            //  <path fill="none" extrusionOk="false">
            //    <moveTo>
            //      <pt x="x1" y="y1" />
            //    </moveTo>
            //    <lnTo>
            //      <pt x="x2" y="y2" />
            //    </lnTo>
            //    <lnTo>
            //      <pt x="x3" y="y3" />
            //    </lnTo>
            //  </path>
            //</pathLst>

            var pathLst = new List<ShapePath>();

            //  <path stroke="false" extrusionOk="false">
            //    <moveTo>
            //      <pt x="l" y="t" />
            //    </moveTo>
            var currentPoint = new EmuPoint(l, t);
            var stringPath = new StringBuilder();
            stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} ");
            //    <lnTo>
            //      <pt x="r" y="t" />
            //    </lnTo>
            currentPoint = LineToToString(stringPath, r, t);
            //    <lnTo>
            //      <pt x="r" y="b" />
            //    </lnTo>
            currentPoint = LineToToString(stringPath, r, b);
            //    <lnTo>
            //      <pt x="l" y="b" />
            //    </lnTo>
            currentPoint = LineToToString(stringPath, l, b);
            //    <close />
            stringPath.Append("z ");

            pathLst.Add(new ShapePath(stringPath.ToString(),isStroke:false));



            //  <path fill="none" extrusionOk="false">
            //    <moveTo>
            //      <pt x="x1" y="y1" />
            //    </moveTo>
            stringPath.Clear();
            currentPoint = new EmuPoint(x1, y1);
            stringPath.Append($"M {EmuToPixelString(currentPoint.X)},{EmuToPixelString(currentPoint.Y)} ");
            //    <lnTo>
            //      <pt x="x2" y="y2" />
            //    </lnTo>
            currentPoint = LineToToString(stringPath, x2, y2);
            //    <lnTo>
            //      <pt x="x3" y="y3" />
            //    </lnTo>
            _ = LineToToString(stringPath, x3, y3);

            pathLst.Add(new ShapePath(stringPath.ToString(), FillMode.None));


            return pathLst;

        }

将解析好的Shape Path转为WPF的形状Path:

         /// <summary>
        /// 将解析好的Shape Path转为Path的形状集合
        /// </summary>
        /// <param name="geometryPaths"></param>
        /// <returns></returns>
        private List<System.Windows.Shapes.Path> CreatePathLst(List<ShapePath> geometryPaths)
        {
            var pathLst = new List<System.Windows.Shapes.Path>();
            foreach (var geometryPath in geometryPaths)
            {
                var geometry = Geometry.Parse(geometryPath.Path);
                var path = new System.Windows.Shapes.Path
                {
                    Data = geometry,
                    Fill = geometryPath.IsFilled ? new SolidColorBrush(Color.FromRgb(68, 114, 196)) : null,
                    Stroke = geometryPath.IsStroke ? new SolidColorBrush(Color.FromRgb(47, 82, 143)) : null,
                };
                pathLst.Add(path);
            }
            return pathLst;
        }

然后渲染到界面:

        /// <summary>
        /// 渲染形状到界面
        /// </summary>
        /// <param name="geometryPaths"></param>
        private void RenderGeometry(List<ShapePath> geometryPaths)
        {
            if (PathGrid.Children.Count > 0)
            {
                PathGrid.Children.Clear();
            }
            var pathLst = CreatePathLst(geometryPaths);
            foreach (var path in pathLst)
            {
                PathGrid.Children.Add(path);
            }
        }

效果演示

pptx和WPF渲染结果对比:

我们会发现,pptx的形状和wpf的形状是一模一样的,同样的左边线条的Path是无填充的,而右边的矩形则是无轮廓有填充的

源码

源码地址

标签:Pptx,多路径,currentPoint,Shape,new,var,Path,stringPath
来源: https://www.cnblogs.com/ryzen/p/14987788.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有