本文是将演示如何解析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的多路径形状转为WPF的Path的更多相关文章

  1. Pptx的形状转为WPF的Geometry

    本文是将演示如何解析pptx文件的形状到WPF当中,并且绘制显示出来 安装Openxml sdk 首先,我们先安装nuget的openxml sdk,下面两种方式都可以安装: nuget包管理器控制台 ...

  2. WPF使用PATH来画圆

    WPF使用Path来画圆, 在 WPF 中可以使用 Path (路径) 来画圆,而 Path 支持两种写法:xaml 代码格式.标记格式,这里介绍的是标记格式: 例子: <Path Data=& ...

  3. Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  4. WPF 中 Path 使用虚线

    效果如下: 上图由两个圆弧组成,代码如下: <!--红色的实线圆弧,旋转200度,顺时针,获取大圆弧--> <Path Data="M 50,200 A 100,100 2 ...

  5. 【OpenXml】Pptx的边框虚线转为WPF的边框虚线

    安装Openxml sdk 首先,我们先安装nuget的需要的有关的Openxml sdk,我们开源了解析pptx的Openxml拍平层,下面两种方式都可以安装: nuget包管理器控制台: Inst ...

  6. [原]Wpf应用Path路径绘制圆弧

    1. 移动指令:Move Command(M):M 起始点  或者:m 起始点比如:M 100,240或m 100,240使用大写M时,表示绝对值; 使用小写m时; 表示相对于前一点的值,如果前一点没 ...

  7. 使用不安全代码将 Bitmap 位图转为 WPF 的 ImageSource 以获得高性能和持续小的内存占用

    在 WPF 中将一个现成的 Bitmap 位图转换成 ImageSource 用于显示一个麻烦的事儿,因为 WPF 并没有提供多少可以转过来的方法.不过产生 Bitmap 来源却非常多,比如屏幕截图. ...

  8. WPF系列 Path表示语法详解(Path之Data属性语法)

    示例: XAML(代码A): <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ...

  9. 【wpf】Path画扇形以及Path的Data属性的理解

    <Path x:Name="PathFillColor" Fill="{TemplateBinding Property=Button.Background}&qu ...

随机推荐

  1. 强哥HTML学习笔记

    html 浏览器的选择:1.火狐2.ie3.chrome4.mac5.opera 安装两款插件:1.firebug2.web develope html页面元素:1.doctype2.htmlhead ...

  2. SSH自动断开连接的原因-20200323

    SSH自动断开连接的原因   方法一: 用putty/SecureCRT连续3分钟左右没有输入, 就自动断开, 然后必须重新登陆, 很麻烦. 在网上查了很多资料, 发现原因有多种, 环境变量TMOUT ...

  3. 007.kubernets的headless service配置和ingress的简单配置

    前面配置了servcie的nodepoint和clusterIP附在均衡 一 headless service配置 1.1 默认下的DNS配置 [root@docker-server1 deploym ...

  4. Linux Socket编程-(转自吴秦(Tyler))

    "一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信 ...

  5. 【遥感数字图像处理实验】Erdas版详细图文实验教程(8实验全)

    @ 目录 1. 专栏简介 2. 专栏地址 3. 专栏目录 1. 专栏简介 遥感数字图像的处理,是对遥感数字图像的计算机处理.与工业和医学数字图像不同,遥感数字图像类型更为多样,内容更为复杂.因此,遥感 ...

  6. Locust性能测试工具核心技术@task和@events

    Tasks和Events是Locust性能测试工具的核心技术,有了它们,Locust才能称得上是一个性能工具. Tasks 从上篇文章知道,locustfile里面必须要有一个类,继承User类,当性 ...

  7. 80个Python练手项目列表

    80个Python练手项目列表   我若将死,给孩子留遗言,只留一句话:Repetition is the mother of all learning重复是学习之母.他们将来长大,学知识,技巧.爱情 ...

  8. Nginx 配置实例-配置负载均衡

    Nginx 配置实例-配置负载均衡 0. 实例效果 1. 两个 tomcat 的安装(可选) 1.1 tomcat8081 的安装 1.1.1 tomcat8081 安装包的装备 1.1.2 tomc ...

  9. python 导入同级目录文件、上级目录文件以及下级目录数据集和模块包

    划重点: 其中dataset_path = ''表示在Python工作文件夹 dataset_path = '..'表示在Python工作文件夹的上级文件夹 dataset_path = '某某文件夹 ...

  10. 适用于Linux 2的Windows子系统上的CUDA

    适用于Linux 2的Windows子系统上的CUDA Announcing CUDA on Windows Subsystem for Linux 2 为了响应大众的需求,微软在2020年5月的构建 ...