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 ...
随机推荐
- iozone测试方法-20191008
iozone 一.简介 磁盘设备之上是文件系统,测试磁盘的工具往往就是调用驱动块设备驱动的接口进行读写测试.而文件系统的测试软件就是,针对文件系统层提供的功能进行测试,包括文件的打开关闭速度以及顺 ...
- 如何在我的EC2实例状态更改时获取自定义电子邮件通知
具体详情,请参见: https://amazonaws-china.com/cn/premiumsupport/knowledge-center/ec2-email-instance-state-ch ...
- MyBatis 开启 Log4j 日志调试信息开关
Log4j 是什么 Log4j 是由 Apache 提供的开源日志框架,用于帮助用户处理日志信息. Log4j 能将日志信息分级打印和存储,而且提供了日志不同的存储方式,我们可以将日志发送到控制台,或 ...
- Hibernate使用原生SQL语句进行无关联多表查询
背景:有两个表:CpCg与CpGg需要进行多表查询 因为CpGg表设计到与另外的表也有联系,因此师兄没有关联此两个表,只是用字段进行逻辑关联,CpGg表的cp字段与CpCg表的id字段逻辑关联
- Ubuntu下安装与卸载Nginx
1.Ubuntu下安装Nginx比较简单 敲入下列命令即可: sudo apt-get update sudo apt-get install nginx 2.Ubuntu下卸载,稍不注意就会入坑 s ...
- linux 安装配置NFS服务器
一.Ubuntu安装配置NFS 1.安装NFS服务器 sudo apt-get install nfs-kernel-server 安装nfs-kernel-server时,apt会自动安装nfs-c ...
- Python+Selenium自动化总结
Python+Selenium自动化总结 1.环境搭建 1.1.安装selenium模块文件 pip install selenium 1.2.安装ChromeDriver驱动 [1]下载安装Chro ...
- 元素定位工具ChroPath - Chrome浏览器插件
一 ChroPath的作用 可以自动识别元素定位表达式,对于系统需要定位元素多时,可使用这种方法减轻定位工作量,但需要验证 二 ChroPath的安装 下载ChroPath -> 在谷歌浏览器访 ...
- 异步编程CompletableFuture
多线程优化性能,串行操作并行化 串行操作 // 以下2个都是耗时操作 doBizA(); doBizB(); 修改变为并行化 new Thread(() -> doBizA()).start() ...
- java IO教程《四》
properties使用 什么是Properties? Properties(Java.util.Properties),该类主要用于读取Java的配置文件,不同的编程语言有自己所支持的配置文件,配置 ...