本文来和大家聊聊在 PPT 形状使用了 Style 样式的颜色画刷读取方法

在开始之前,期望大家已了解如何在 dotnet 应用里面读取 PPT 文件,如果还不了解读取方法,请参阅 C# dotnet 使用 OpenXml 解析 PPT 文件

本文核心是来和大家聊聊 ECMA 376 文档里面,第 20.1.4.2.19 章的 lnRef (Line Reference) 内容里面没有提到的知识

在 Office 的 PowerPoint 添加默认的形状,在没有更改形状的填充和轮廓,形状使用的是默认的样式,如以下的默认矩形定义

<p:sp><p:nvSpPr><p:cNvPr id="6" name="矩形 1" /><p:cNvSpPr /><p:nvPr /></p:nvSpPr><p:spPr><a:xfrm><a:off x="3640346" y="1595887" /><a:ext cx="3804249" cy="3071004" /></a:xfrm><a:prstGeom prst="rect"><a:avLst /></a:prstGeom><a:ln w="76200" /></p:spPr><p:style><a:lnRef idx="2"><a:schemeClr val="accent1"><a:shade val="50000" /></a:schemeClr></a:lnRef><a:fillRef idx="1"><a:schemeClr val="accent1" /></a:fillRef><a:effectRef idx="0"><a:schemeClr val="accent1" /></a:effectRef><a:fontRef idx="minor"><a:schemeClr val="lt1" /></a:fontRef></p:style><p:txBody/>
</p:sp>

在 OpenXML 里面,通过 a:ln 表示 Outline 轮廓,也就是咱 WPF 形状元素的边框。包括定义了边框粗细和颜色画刷等

但是从上面文档内容可以看到,只是定义了边框的粗细,没有定义颜色。这就需要从 <p:style> 样式里面读取线条的样式。从 <a:lnRef idx="2"> 可以读取到采用的是序号为 2 的线条样式,这里有一个细节是给定的序号也许会超过定义,解决方法请看 dotnet OpenXML 读取形状轮廓线条样式序号超过主题样式列表数

接着读取 <a:schemeClr val="accent1"> 的内容,用来覆盖作为实际的颜色

下面我将给大家演示如何在 WPF 中读取 PPT 的形状 Style 边框颜色和在界面里面将此显示出来

先加上基础的库引用,以下代码写在 csproj 上,可在本文末尾找到全部源代码

  <ItemGroup><PackageReference Include="dotnetCampus.OpenXmlUnitConverter" Version="1.4.1"/><PackageReference Include="DocumentFormat.OpenXml" Version="2.13.1" /></ItemGroup>

接着读取包含上文的形状的 PPT 文档

            var file = new FileInfo("Test.pptx");using var presentationDocument = PresentationDocument.Open(file.FullName, false);

获取页面里面的形状,如以下代码

            var slide = presentationDocument.PresentationPart!.SlideParts.First().Slide;var shape = slide.CommonSlideData!.ShapeTree!.GetFirstChild<Shape>()!;

先从 ShapeProperties 里面获取形状的边框粗细,如下面代码

            ShapeProperties shapeProperties = shape.ShapeProperties!;// 虽然这个形状有轮廓,但是定义是 `<a:ln w="76200" />` 只有宽度,没有颜色Outline outline = shapeProperties.GetFirstChild<Outline>()!;Debug.Assert(outline.GetFirstChild<SchemeColor>() is null);var outlineWidth = new Emu(outline.Width!);

以上代码拿到的 outlineWidth 就是形状的边框粗细。此形状有轮廓,但是定义是 <a:ln w="76200" /> 只有宽度,没有颜色。颜色需要在 Style 里面读取。在 PPTX 文件里面的定义如下

    <a:lnRef idx="2"><a:schemeClr val="accent1"><a:shade val="50000" /></a:schemeClr></a:lnRef>

获取方法是先读取形状的样式,接着读取线条引用,请看代码

            // 实际的颜色应该从 `<a:lnRef idx="2">` 拿到var shapeStyle = shape.ShapeStyle!;var lineReference = shapeStyle.LineReference!;

拿到 LineReference 就可以读取里层的颜色,如下面代码

            // 读取里层的颜色var schemeColor = lineReference.GetFirstChild<SchemeColor>()!;

此颜色是 SchemeColor 颜色,按照 dotnet OpenXML 如何获取 schemeClr 颜色 文档的方法进行读取,读取时用到的辅助方法本文就不列出,还请参阅以上引用博客。当然,本文所有源代码都可以获取到,还请不用担心细节

以下是将 SchemeColor 转换为 System.Windows.Media.Color 的方法

            var colorMap = slide.GetColorMap()!;var colorScheme = slide.GetColorScheme()!;var value = schemeColor.Val!.Value; // accent1value = ColorHelper.SchemeColorMap(value, colorMap);var actualColor = ColorHelper.FindSchemeColor(value, colorScheme)!; // <a:srgbClr val="5B9BD5"/>RgbColorModelHex rgbColorModelHex = actualColor.RgbColorModelHex!;var color = (Color) ColorConverter.ConvertFromString($"#{rgbColorModelHex.Val!.Value}");

接下来按照其他形状的解析方法,读取坐标和宽度高度,在界面显示出来

            // 获取坐标var offset = shapeProperties.Transform2D!.Offset!;var x = new Emu(offset.X!);var y = new Emu(offset.Y!);var extents = shapeProperties.Transform2D.Extents!;var width = new Emu(extents.Cx!);var height = new Emu(extents.Cy!);// 创建元素var rectangle = new Rectangle(){Margin = new Thickness(x.ToPixel().Value, y.ToPixel().Value, 0, 0),Width = width.ToPixel().Value,Height = height.ToPixel().Value,StrokeThickness = outlineWidth.ToPixel().Value,Stroke = new SolidColorBrush(color)};Canvas.Children.Add(rectangle);

以上代码的 Canvas 是在 XAML 定义的,代码如下

  <Grid><Canvas x:Name="Canvas"></Canvas></Grid>

运行之后的效果如下

可以看到颜色其实有些差距,原因是以上使用的 SchemeColor 没有加上颜色特效,在 PPTX 文件定义的颜色代码如下

      <a:schemeClr val="accent1"><a:shade val="50000" /></a:schemeClr>

通过 dotnet OpenXML 颜色变换 文档可以了解到 Shade 是让颜色变暗,使用如下代码加上特效

            var shade = schemeColor.GetFirstChild<Shade>()!;// 让颜色变暗color = ColorTransform.HandleShade(color, shade);

此时的效果如下

本文以上所有测试文件和代码放在github 和 gitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin d06378705fcc1a1ff19ea7d1f2544757fb0777c7

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 Pptx 文件夹

虽然可以看到在 WPF 上的形状的边框颜色和在 PPT 上的形状颜色是相同的,然而以上逻辑却有漏洞在于以上是重新被定义了颜色。其实在 OpenXML 里面,按照的寻找属性的规则和 WPF 的资源是相同的逻辑,按照最近原则读取。也就是说读取顺序如下

  • 形状的 a:ln 定义的颜色
  • 形状的样式的 a:lnRef 引用的主题的颜色
  • 形状继承的样式

以上的测试文档是属于在形状的 a:ln 没有定义的颜色,而在形状的样式的 a:lnRef 里面定义的颜色,而且形状引用样式里面使用的是 <a:schemeClr val="phClr" /> 占位符颜色

如果在形状的 a:ln 和形状的样式的 a:lnRef 没有定义的颜色,只有在形状的样式的 a:lnRef 引用的主题的颜色,那么以上逻辑是不符合 OpenXML 定义的。或者说在形状的 a:ln 没有定义的颜色,而在形状的样式的 a:lnRef 里面有定义颜色,但是形状的样式的 a:lnRef 引用的主题的颜色不是 phClr (PlaceholderColor, a color used in theme definitions which means "use the color of the style")的颜色

如以下的文档内容

Slide1.xml:<p:sp><p:style><a:lnRef idx="2"><a:schemeClr val="accent1" /></a:lnRef></p:style>
</p:sp>Theme1:<a:lnStyleLst><a:ln w="6350" cap="flat" cmpd="sng" algn="ctr"></a:ln><a:ln w="12700" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:srgbClr val="999999" /></a:solidFill><a:prstDash val="solid" /><a:miter lim="800000" /></a:ln><a:ln w="19050" cap="flat" cmpd="sng" algn="ctr"></a:ln></a:lnStyleLst>

通过 <a:lnRef idx="2"> 可以了解到应该读取的是 LineStyleList 的第二项(从1开始)的颜色

和第一份文档不同的是,以上文档定义的是 <a:srgbClr val="999999" /> 而不是 <a:schemeClr val="phClr" /> 颜色。也就是说在 Slide1.xml 定义的 <a:schemeClr val="accent1" /> 需要被忽略

变更之后的逻辑如下,先读取引用,参阅 dotnet OpenXML 读取形状轮廓线条样式序号超过主题样式列表数 文档的写法

            var lineStyle = lineReference.Index!.Value;// idx="2"lineStyle--;// 获取主题var slidePart = slide.SlidePart!;var themeOverride = slidePart.ThemeOverridePart?.ThemeOverride?? slidePart.SlideLayoutPart!.ThemeOverridePart?.ThemeOverride;FormatScheme? formatScheme = themeOverride?.FormatScheme;if (formatScheme is null){formatScheme = slidePart.SlideLayoutPart!.SlideMasterPart!.ThemePart!.Theme!.ThemeElements!.FormatScheme;}var lineStyleList = formatScheme!.LineStyleList;var outlineList = lineStyleList!.Elements<Outline>().ToList(); // 其实,别用 ToList 的好,这里只是简化逻辑Outline themeOutline;if (lineStyle > outlineList.Count){themeOutline = outlineList[^1];}else{themeOutline = outlineList[(int)lineStyle];}

此时读取到的 themeOutline 就是如下的 OpenXML 文档内容

        <a:ln w="12700" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:srgbClr val="999999" /></a:solidFill><a:prstDash val="solid" /><a:miter lim="800000" /></a:ln>

先获取是否有定义 solidFill 的内容,如果有,那么继续获取里层颜色

var solidFill = themeOutline.GetFirstChild<SolidFill>()!;

在以上的文档里面,是存在 SolidFill 内容的,因此上面代码就不判断空了

获取里层的颜色,如果是 srgbClr (对应 OpenXML 的 RgbColorModelHex 类型)的话,那么计算颜色即可

            var solidFill = themeOutline.GetFirstChild<SolidFill>()!;var colorModelHex = solidFill.GetFirstChild<RgbColorModelHex>();if (colorModelHex is not null){// <a:srgbClr val="999999" />color = (Color)ColorConverter.ConvertFromString($"#{colorModelHex.Val!.Value}");}

而如果是读取到 SchemeColor 而且是 PhColor 方式的颜色,那么依然按照上文的方式读取形状样式里面的 LineReference 的颜色

    <a:lnRef idx="2"><a:schemeClr val="accent1"><a:shade val="50000" /></a:schemeClr></a:lnRef>

读取的逻辑如下

            var solidFill = themeOutline.GetFirstChild<SolidFill>()!;var colorModelHex = solidFill.GetFirstChild<RgbColorModelHex>();if (colorModelHex is not null){}else{var themeSchemeColor = solidFill.GetFirstChild<SchemeColor>()!;if (themeSchemeColor.Val!.Value == SchemeColorValues.PhColor){// 读取里层的颜色var schemeColor = lineReference.GetFirstChild<SchemeColor>()!;// 读取 SchemeColor 方法请参阅如下文档// [dotnet OpenXML 如何获取 schemeClr 颜色](https://blog.lindexi.com/post/dotnet-OpenXML-%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96-schemeClr-%E9%A2%9C%E8%89%B2.html )var colorMap = slide.GetColorMap()!;var colorScheme = slide.GetColorScheme()!;var value = schemeColor.Val!.Value; // accent1value = ColorHelper.SchemeColorMap(value, colorMap);var actualColor = ColorHelper.FindSchemeColor(value, colorScheme)!; // <a:srgbClr val="5B9BD5"/>RgbColorModelHex rgbColorModelHex = actualColor.RgbColorModelHex!;color = (Color)ColorConverter.ConvertFromString($"#{rgbColorModelHex.Val!.Value}");// 根据 [dotnet OpenXML 颜色变换](https://blog.lindexi.com/post/dotnet-OpenXML-%E9%A2%9C%E8%89%B2%E5%8F%98%E6%8D%A2.html ) 进行修改颜色var shade = schemeColor.GetFirstChild<Shade>()!;// 让颜色变暗color = ColorTransform.HandleShade(color, shade);}}

此时更换文档,执行的界面如下

本文以上更新的测试文件和代码放在github 和 gitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 868ad6c1a39853764167053e319b68a6db0a2c6b

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 Pptx 文件夹

更多的特殊逻辑:

如果在 Theme 里面定义的 LineStyleList 里面定义的轮廓没有设置颜色,如下面的 OpenXML 文档

      <a:lnStyleLst><a:ln w="6350" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr" /></a:solidFill><a:prstDash val="solid" /><a:miter lim="800000" /></a:ln><a:ln w="12700" cap="flat" cmpd="sng" algn="ctr"><a:prstDash val="solid" /><a:miter lim="800000" /></a:ln><a:ln w="19050" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr" /></a:solidFill><a:prstDash val="solid" /><a:miter lim="800000" /></a:ln></a:lnStyleLst>

在 Slide1.xml 里面的形状定义和上文的相同,引用了第二项的主题,如下面的 OpenXML 文档

    <a:lnRef idx="2"><a:schemeClr val="accent1"><a:shade val="50000" /></a:schemeClr></a:lnRef>

此时在 PPT 的运行效果就是没有边框,也就是说在 a:lnRef 定义的 <a:schemeClr val="accent1"> 颜色仅仅只是用来作为 PhColor 的替换

更多请看 Office 使用 OpenXML SDK 解析文档博客目录

我搭建了自己的博客 https://blog.lindexi.com/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新

如果在博客看到有任何不懂的,欢迎交流,我搭建了 dotnet 职业技术学院 欢迎大家加入

如有不方便在博客评论的问题,可以加我 QQ 2844808902 交流


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

dotnet OpenXML 读取 PPT 形状边框定义在 Style 的颜色画刷相关推荐

  1. dotnet OpenXML 读取 PPT 主序列进入退出强调动画

    本文告诉大家如何读取 PPT 文件里面,放在主动画序列 MainSequence 的进入和退出和强调的动画,和在 OpenXML 里面的存放方式 如以下的课件内容,给一个元素添加了进入强调退出的动画, ...

  2. dotnet OpenXML 读取 PPT 内嵌 ole 格式 Excel 表格的信息

    在 Office 中,可以在 PPT 里面插入表格,插入表格有好多不同的方法,对应 OpenXML 文档存储的更多不同的方式.本文来介绍如何读取 PPT 内嵌 ole 格式的 xls+ 表格的方法 在 ...

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

    在阅读本文之前,我期望你能了解基础的 PPT 解析内容,或看我的入门级博客.本文将告诉大家如何从 PPT 里面解析出通用元素的 x 和 y 的值,以及元素的宽度和高度的值 在开始之前请看 C# dot ...

  4. C# dotnet 使用 OpenXml 解析 PPT 里面的视频

    本文告诉大家如何从 PPTX 文件里面解析出视频 我期望看到本文的小伙伴是了解 OpenXML 的,如果想要解析 Office 的文档,我推荐使用使用 OpenXML SDK 这个开源的库,更多入门级 ...

  5. php ppt read_PHP读取PPT文件的方法

    本文实例讲述了PHP读取PPT文件的方法.分享给大家供大家参考,具体如下: 最近做一个和FLASH有关的东西,其中就要用到在网站上看PPT就像百度,豆丁网那样可以直接在网站上读,在网上搜了半天没搜到, ...

  6. 读取PPT模板替换数据生成PDF报表

    一.项目背景 因本人的公司是做短信通信服务的,所以客户需要将短信的发送量,按照他们给定的PPT样例模板,来生成PDF报表数据,即PPT的样式,数据如何展示都是规定好了的,而我需要做的就是将真实的数据, ...

  7. php ppt read_php怎么读取ppt文字内容并在浏览器中显示出来?

    具体代码麻烦我就不写了,可以提供给你一个思路:首先要求服务器上安装wps或者microsoft powerpoint软件,然后通过api接口来访问并读取ppt,最后将其发送到页面.当然你不能每次等别人 ...

  8. java 创建ppt文件怎么打开文件,JAVA读取PPT文件

    JAVA读取PPT文件 import java.io.InputStream; import org.apache.lucene.document.Document; import org.apach ...

  9. java 读取ppt_Java 读取PPT文本和图片

    本篇文章将介绍通过Java程序来读取PPT幻灯片中的文本及图片的方法.读取图片时,可读取文档中的所有图片,也可以读取指定幻灯片当中的图片. 工具:Free Spire.Presentation for ...

最新文章

  1. java wed登录面 代码_java web 登录界面
  2. ‘numpy.float64‘ object cannot be interpreted as an integer
  3. Linux 打开/关闭CPU命令
  4. 2017年 第08届 蓝桥杯 Java B组 决赛真题详解及小结
  5. vs.net 2003 错误:无法生成项目输出组“内容文件来自stuschool(活动)”
  6. NetworkX系列教程(1)-创建graph
  7. 图解elasticsearch原理转载自
  8. linux查看正在运行的窗口,获取linux中打开的应用程序窗口的数量
  9. 【OpenCV学习笔记】【函数学习】十七(imread()函数)
  10. java代码生成UUID以及在线UUID生成器
  11. Android 开发神器系列(工具篇)之 Android 屏幕共享工具
  12. 机器学习基础(二)——训练集和测试集的划分
  13. R Shiny module学习笔记
  14. [转]Linux面试题(2020最新版)
  15. eclipse hana xs 开发环境搭建
  16. 如何导入和导出Maven项目(分享给别人源码)
  17. 滑雪(广搜+记忆数组+STL)
  18. 网络篇 网络设备的基本配置09
  19. 基于FPGA的高空坠物跟踪和预警系统
  20. c语言ATM机文件储存账号密码,C语言ATM(有文件输入输出).doc

热门文章

  1. Win10系统Intellij IDEA-2020下载、安装、注册、基础设置,傻瓜教程没手都行
  2. 测试自己美丑的软件,分享:测试你的美丑(超准哦!)
  3. 帮你抢小游戏流量红利——bilibili小游戏入指南
  4. 知识图谱下一站:认知智能
  5. 嵌入式Linux系统工程师都学些什么?
  6. 第一章 Linux操作系统概述
  7. DVWA全套学习打靶通关教程
  8. 初探强化学习(13)DQN的Pytorch代码解析,逐行解析,每一行都不漏
  9. NR/5G ~ R16 dormant BWP
  10. STM8S PD7 TLI外部高级中断