Pptx的多路径形状转为WPF的Path
本文是将演示如何解析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的更多相关文章
- Pptx的形状转为WPF的Geometry
本文是将演示如何解析pptx文件的形状到WPF当中,并且绘制显示出来 安装Openxml sdk 首先,我们先安装nuget的openxml sdk,下面两种方式都可以安装: nuget包管理器控制台 ...
- WPF使用PATH来画圆
WPF使用Path来画圆, 在 WPF 中可以使用 Path (路径) 来画圆,而 Path 支持两种写法:xaml 代码格式.标记格式,这里介绍的是标记格式: 例子: <Path Data=& ...
- Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- WPF 中 Path 使用虚线
效果如下: 上图由两个圆弧组成,代码如下: <!--红色的实线圆弧,旋转200度,顺时针,获取大圆弧--> <Path Data="M 50,200 A 100,100 2 ...
- 【OpenXml】Pptx的边框虚线转为WPF的边框虚线
安装Openxml sdk 首先,我们先安装nuget的需要的有关的Openxml sdk,我们开源了解析pptx的Openxml拍平层,下面两种方式都可以安装: nuget包管理器控制台: Inst ...
- [原]Wpf应用Path路径绘制圆弧
1. 移动指令:Move Command(M):M 起始点 或者:m 起始点比如:M 100,240或m 100,240使用大写M时,表示绝对值; 使用小写m时; 表示相对于前一点的值,如果前一点没 ...
- 使用不安全代码将 Bitmap 位图转为 WPF 的 ImageSource 以获得高性能和持续小的内存占用
在 WPF 中将一个现成的 Bitmap 位图转换成 ImageSource 用于显示一个麻烦的事儿,因为 WPF 并没有提供多少可以转过来的方法.不过产生 Bitmap 来源却非常多,比如屏幕截图. ...
- WPF系列 Path表示语法详解(Path之Data属性语法)
示例: XAML(代码A): <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ...
- 【wpf】Path画扇形以及Path的Data属性的理解
<Path x:Name="PathFillColor" Fill="{TemplateBinding Property=Button.Background}&qu ...
随机推荐
- python3.7[列表] 索引切片
python3.7[列表] 索引 切片 排序 #### 列表.sort 永久排序 sorted(列表) 临时排序 ### >>> print(sorted(a))[ ...
- 马哥Linux SysAdmin学习笔记(二)
Linux网络属性管理: 局域网:以太网,令牌环网 Ethernet:CSMA/CD 冲突域 广播域 MAC:media access control地址 48bit: 24bits 24bits ...
- 1.4 重置root用户密码
图1-45 系统的欢迎界面 1.4 重置root用户密码 平日里让运维人员头疼的事情已经很多了,因此偶尔把Linux系统的密码忘记了并不用慌,只需简单几步就可以完成密码的重置工作.但是,如果您是第一 ...
- Linux_日志管理理论概述
一.日志系统 1.kernel -->物理终端(/dev/console) --> /var/log/dmesg(系统启动时信息(包括错误信息)记录到该文件) 或者:# dmesg 或 # ...
- 分布式锁中的王者方案-Redisson
上篇讲解了如何用 Redis 实现分布式锁的五种方案,但我们还是有更优的王者方案,就是用 Redisson. 缓存系列文章: 缓存实战(一):20 图 |6 千字|缓存实战(上篇) 缓存实战(二):R ...
- Go语言之main包
Go语言的代码通过包(package)组织,包类似于其他语言里的库(libraries)或者模块(modules).一个包由位于单个目录下的一个或多个go源文件组成,目录定义包的作用.每个源文件都以一 ...
- .NET Core中插件式开发实现
前言: 之前在文章- AppDomain实现[插件式]开发 中介绍了在 .NET Framework 中,通过AppDomain实现动态加载和卸载程序集的效果. 但是.NET Core 仅支持单个默认 ...
- 对狂神说java的springboot中spring security的总结
1.spring security的环境搭建 首先新建一个springboot项目,只够选web中的spring web依赖 然后在pom.xml导入相关依赖 <!--thymeleaf模块-- ...
- RedisPool
class RedisPool(object): instance = None def __init__(self, host='192.168.2.129', port=7000, passwor ...
- NVIDIA GPU上的随机数生成
NVIDIA GPU上的随机数生成 NVIDIA CUDA随机数生成库(cuRAND)提供高性能的GPU加速的随机数生成(RNG).cuRAND库使用NVIDIA GPU中提供的数百个处理器内核,将质 ...