【OpenXml】Pptx的形状转为WPF的Geometry

本文是将演示如何解析pptx文件的形状到WPF当中,并且绘制显示出来

安装Openxml sdk#

首先,我们先安装nuget的openxml sdk,下面两种方式都可以安装:

  • nuget包管理器控制台:
Copy
Install-Package DocumentFormat.OpenXml -Version 2.13.0
  • csproj引用:
Copy
<PackageReference Include="DocumentFormat.OpenXml" Version="2.13.0" />

解析Pptx#

我打算解析pptx中的五边形来作为演示效果,直接上代码:

MainWindow.xaml:

Copy
<Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="2*"/> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center"> <TextBlock Text="pptx的文件路径:" VerticalAlignment="Center" FontSize="15" Margin="10"/> <TextBox x:Name="FilePathText" Height="50" Width="300" Margin="0,0,10,0" TextWrapping="Wrap"/> <Button x:Name="Button" Content="解析PPT" Click="Button_OnClick" Width="120" Height="40"/> </StackPanel> <Path x:Name="Path" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="Blue"/> </Grid>

MainWindow.xaml.cs:

Copy
private void PptxToGeometry(string filePath) { if (!File.Exists(filePath) || !filePath.EndsWith(".pptx", StringComparison.OrdinalIgnoreCase)) { 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.Pentagon) { 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 points = GetPentagonPoints(width.Value.EmuToPixel(), height.Value.EmuToPixel()); RenderGeometry(points); } } } } } } } } /// <summary> /// 获取五边形顶点坐标 /// </summary> /// <param name="width"></param> /// <param name="height"></param> /// <returns></returns> /// 该五边形定义出自ECMA-376-Fifth-Edition-Part-1-Fundamentals-And-Markup-Language-Reference /// \OfficeOpenXML-DrawingMLGeometries文档的presetShapeDefinitions.xml private List<Point> GetPentagonPoints(double width, double height) { var properties = new FormulaProperties(width, height); //<avLst xmlns="http://schemas.openxmlformats.org/drawingml/2006/main"> // <gd name="hf" fmla="val 105146" /> // <gd name="vf" fmla="val 110557" /> //</avLst> var hf = 105146d; var vf = 110557d; //<gdLst xmlns="http://schemas.openxmlformats.org/drawingml/2006/main"> // <gd name="swd2" fmla="*/ wd2 hf 100000" /> // <gd name="shd2" fmla="*/ hd2 vf 100000" /> // <gd name="svc" fmla="*/ vc vf 100000" /> // <gd name="dx1" fmla="cos swd2 1080000" /> // <gd name="dx2" fmla="cos swd2 18360000" /> // <gd name="dy1" fmla="sin shd2 1080000" /> // <gd name="dy2" fmla="sin shd2 18360000" /> // <gd name="x1" fmla="+- hc 0 dx1" /> // <gd name="x2" fmla="+- hc 0 dx2" /> // <gd name="x3" fmla="+- hc dx2 0" /> // <gd name="x4" fmla="+- hc dx1 0" /> // <gd name="y1" fmla="+- svc 0 dy1" /> // <gd name="y2" fmla="+- svc 0 dy2" /> // <gd name="it" fmla="*/ y1 dx2 dx1" /> //</gdLst> // <gd name="swd2" fmla="*/ wd2 hf 100000" /> var swd2 = properties.wd2 * hf / 100000; // <gd name="shd2" fmla="*/ hd2 vf 100000" /> var shd2 = properties.hd2 * vf / 100000; // <gd name="svc" fmla="*/ vc vf 100000" /> var svc = properties.vc * vf / 100000; // <gd name="dx1" fmla="cos swd2 1080000" /> var dx1 = Cos(swd2, 1080000); // <gd name="dx2" fmla="cos swd2 18360000" /> var dx2 = Cos(swd2, 18360000); // <gd name="dy1" fmla="sin shd2 1080000" /> var dy1 = Sin(shd2, 1080000); // <gd name="dy2" fmla="sin shd2 18360000" /> var dy2 = Sin(shd2, 18360000); // <gd name="x1" fmla="+- hc 0 dx1" /> var x1 = properties.hc - dx1; // <gd name="x2" fmla="+- hc 0 dx2" /> var x2 = properties.hc - dx2; // <gd name="x3" fmla="+- hc dx2 0" /> var x3 = properties.hc + dx2; // <gd name="x4" fmla="+- hc dx1 0" /> var x4 = properties.hc + dx1; // <gd name="y1" fmla="+- svc 0 dy1" /> var y1 = svc - dy1; // <gd name="y2" fmla="+- svc 0 dy2" /> var y2 = svc - dy2; // <gd name="it" fmla="*/ y1 dx2 dx1" /> // <pathLst xmlns="http://schemas.openxmlformats.org/drawingml/2006/main"> // <path> // <moveTo> // <pt x="x1" y="y1" /> // </moveTo> // <lnTo> // <pt x="hc" y="t" /> // </lnTo> // <lnTo> // <pt x="x4" y="y1" /> // </lnTo> // <lnTo> // <pt x="x3" y="y2" /> // </lnTo> // <lnTo> // <pt x="x2" y="y2" /> // </lnTo> // <close /> // </path> //</pathLst> var points = new List<Point>(5) { new Point(x1, y1), new Point(properties.hc,properties.t), new Point(x4, y1), new Point(x3, y2), new Point(x2, y2), }; return points; } private void RenderGeometry(List<Point> points) { if (points.Count > 0) { var streamGeometry = new StreamGeometry(); using var context = streamGeometry.Open(); context.BeginFigure(points[0], true, true); context.PolyLineTo(points, true, true); this.Path.Data = streamGeometry; } } private void Button_OnClick(object sender, RoutedEventArgs e) { var filePath = @"C:\Users\Ryzen\Desktop\测验\五边形.pptx"; if (!string.IsNullOrEmpty(FilePathText.Text)) { filePath = FilePathText.Text.Trim(); } PptxToGeometry(filePath); }

ShapeGeometryHelper.cs:

Copy
public static class ShapeGeometryHelper { public readonly struct FormulaProperties { public readonly double t; public readonly double h; public readonly double w; public readonly double hd2; public readonly double wd2; public readonly double vc; public readonly double hc; public FormulaProperties(double width, double height) { t = 0; w = width; h = height; hd2 = h / 2; wd2 = w / 2; vc = height / 2; hc = width / 2; } } public static long EmuToPixel(this long eum) { const long defaultDpi = 96; return eum / 914400 * defaultDpi; } /// <summary> /// OpenXml 三角函数的Sin函数:sin x y = (x * sin( y )) = (x * Math.Sin(y)) /// </summary> /// <param name="x">ppt的数值</param> /// <param name="y">ppt表示角度的值</param> /// <returns></returns> public static double Sin(double x, int y) { var angle = GetAngle(y); return x * Math.Sin(angle); } /// <summary> /// OpenXml 三角函数的Cos函数:cos x y = (x * cos( y )) = (x * Math.Cos(y)) /// </summary> /// <param name="x">ppt的数值</param> /// <param name="y">ppt表示角度的值</param> /// <returns></returns> public static double Cos(double x, int y) { var angle = GetAngle(y); return x * Math.Cos(angle); } /// <summary> /// OpenXml 三角函数的Tan函数:tan x y = (x * tan( y )) = (x * Math.Tan(y)) /// </summary> /// <param name="x">ppt的数值</param> /// <param name="y">ppt表示角度的值</param> /// <returns></returns> public static double Tan(double x, int y) { var angle = GetAngle(y); return x * Math.Tan(angle); } /// <summary> /// ppt的值转为角度 /// </summary> /// <param name="value">ppt表示角度的值</param> /// <returns></returns> private static double GetAngle(int value) { var degree = value / 60000.0; var angle = degree * Math.PI / 180; return angle; } }

效果如下:

源码#

BlogCodeSample/PptPolygonToWPFGeometry at main · ZhengDaoWang/BlogCodeSample

参考#

C# dontet Office Open XML Unit Converter

C# dotnet 使用 OpenXml 解析 PPT 元素的坐标和宽度高度

C# dotnet 使用 OpenXml 解析 PPT 文件

posted @   RyzenAdorer  阅读(813)  评论(3编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
历史上的今天:
2020-06-08 异步函数async await在wpf都做了什么?
CONTENTS
点击右上角即可分享
微信分享提示