前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Office OpenXml SDK 使用 Fallback 图片显示 Ole 元素

Office OpenXml SDK 使用 Fallback 图片显示 Ole 元素

作者头像
林德熙
发布于 2020-08-25 06:41:06
发布于 2020-08-25 06:41:06
1K00
代码可运行
举报
文章被收录于专栏:林德熙的博客林德熙的博客
运行总次数:0
代码可运行

我在写一个 WinForms 程序用来读取 Word 里面的图片显示,在解析 Word 等 Office 文档,会看到一些 ole object 元素,而有些 ole object 会有 Fallback 图片,用这些备用的图片可以显示 ole 元素

其实有很多 Office 插件公司在开发,而特殊的元素如何在其他版本打开?或者我用插件做了一个复杂的元素,在没有插件的设备如何让用户看到?在 Office 的一个做法是通过 Fallback 元素,在里面放一张图片

因为我的 Word 文档写了很多逗比的话,就不开放给大家。除了 Word 在 PPT 解析上也差不多,解析 PPT 里面的 Ole 元素,使用 Fallback 元素显示图片是本文的例子。这份文档也不能给大家,我不觉得你没事干会看本文,应该是你遇到了 Office 解析 ole 元素如何显示或 oleobj 如何转换等问题会看本文 ,也就是你其实有一份 Office 文档了

我将这个文档放在 “F:\林德熙是逗比” 文件夹,也就是你拿到我的代码也许需要更改一下代码里面的路径,才能跑起来

先安装 DocumentFormat.OpenXml 库,这是一个完全开源的官方的全平台的库

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  <ItemGroup>
    <PackageReference Include="DocumentFormat.OpenXml" Version="2.10.1" />
  </ItemGroup>

是不是觉得我上面代码安装库很奇特,其实这是 SDK Style 格式的 csporj 的写法,可以瞬间安装完成一个 NuGet 库。如何使用这个格式请看 从以前的项目格式迁移到 VS2017 新项目格式

通过下面代码可以打开解析 Office 文件,本文打开的是一个 PPT 文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            using (var doc = PresentationDocument.Open(pptxFilePath, false))

我推荐这部分可以放在后台代码,因为 PresentationDocument.Open 需要做的内容会比较多

上面如何打开 PPT 请看 C# dotnet 使用 OpenXml 解析 PPT 文件

我假定只有一个页面,因为我传入的PPT文件就只有一个页面,这个需要根据你的实际代码更改

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
                // 我假定你只有一个页面
                var slidePart = doc.PresentationPart.SlideParts.First();

元素是放在页面里面,也就是需要拿到页面

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
                // 拿到页面
                var slide = slidePart.Slide;

很多 ole 元素都放在 p:graphicframe 里面,先尝试遍历所有 GraphicFrame 请看代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
                // 找到所有也许是 ole 的元素
                foreach (var frame in slide.CommonSlideData.ShapeTree
                    .OfType<DocumentFormat.OpenXml.Presentation.GraphicFrame>())

在原文的写法是

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<p:graphicframe>
    <p:nvgraphicframepr>
        <p:cnvpr id="8" name="Object 25">
        </p:cnvpr>
        <p:cnvgraphicframepr>
            <a:graphicframelocks nochangeaspect="1">
            </a:graphicframelocks>
        </p:cnvgraphicframepr>
        <p:nvpr>
            <p:extlst>
            </p:extlst>
        </p:nvpr>
    </p:nvgraphicframepr>
    <a:graphic>
        <a:graphicdata uri="http://schemas.openxmlformats.org/presentationml/2006/ole">
            <mc:alternatecontent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
                <mc:choice requires="v" xmlns:v="urn:schemas-microsoft-com:vml">
                    <p:oleobj imgh="2535890" imgw="5253016" name="文档" progid="Word.Document.12" r:id="rId3" spid="_x0000_s1026">
                        <p:embed>
                        </p:embed>
                    </p:oleobj>
                </mc:choice>
                忽略一些代码
            </mc:alternatecontent>
        </a:graphicdata>
    </a:graphic>
</p:graphicframe>

也就是 ole 元素 p:oleobj 放在 graphic 里面,不过在 OpenXML SDK 可以使用 Linq 的方式快速读取到对应的值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var oleElement = frame.Descendants<DocumentFormat.OpenXml.Presentation.OleObject>()
    .FirstOrDefault();
if (oleElement != null)
{
}

这里的 Ole Object 在 ECMA 全称 Global Element for Embedded objects and Controls 元素

然后尝试读取 oleElement 的 Fallback 是否有图片

不是所有的 ole element 都有备用的图,需要看你的文档里面是否有 mc:fallback 元素,同时这个元素是 p:pic 图片元素

在Office的图片填充用的是 p:blipFill 而这个元素需要用到依赖的元素

简单的图片是

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<p:pic><p:blipFill>  
    <a:blip r:embed="rId2"/>  
    <a:stretch>  
      <a:fillRect/>  
    </a:stretch>  
  </p:blipFill></p:pic>

核心在于 r:embed 的值,这个值可以从 xml.rel 里面读取,但是这里的读取逻辑很复杂。不过 OpenXML SDK 已经封装了

那么如何从拿到 OleObject 返回备用图片,先拿到对应的页面,所有资源放在页面的 SlidePart 元素

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        private static (bool isSuccess, FileInfo file) TryGetFallbackImage(OleObject oleElement)
        {
            var slide = oleElement.Ancestors<Slide>().First();

            var slidePart = slide.SlidePart;

            // 忽略代码
        }

在 OpenXML 用的类是继承 XmlDocument 也就是可以通过 oleElement 向上找到 p:graphicframe 元素

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            var frame = oleElement.Ancestors<GraphicFrame>().First();

            var frameGraphic = frame.Graphic.GraphicData;

先补充 mc:fallback 的文档

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<a:graphicdata uri="http://schemas.openxmlformats.org/presentationml/2006/ole">
    <mc:alternatecontent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
        <mc:choice requires="v" xmlns:v="urn:schemas-microsoft-com:vml">
            <p:oleobj imgh="2535890" imgw="5253016" name="文档" progid="Word.Document.12" r:id="rId3" spid="_x0000_s1026">
                <p:embed>
                </p:embed>
            </p:oleobj>
        </mc:choice>
        <mc:fallback>
            <p:oleobj imgh="2535890" imgw="5253016" name="文档" progid="Word.Document.12" r:id="rId3">
                <p:embed>
                </p:embed>
                <p:pic>
                    <p:nvpicpr>
                        <p:cnvpr id="0" name="">
                        </p:cnvpr>
                        <p:cnvpicpr>
                            <a:piclocks nochangearrowheads="1" nochangeaspect="1">
                            </a:piclocks>
                        </p:cnvpicpr>
                        <p:nvpr>
                        </p:nvpr>
                    </p:nvpicpr>
                    <p:blipfill>
                        <a:blip r:embed="rId4">
                            <a:extlst>
                                <a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
                                    <a14:uselocaldpi val="0" xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main">
                                    </a14:uselocaldpi>
                                </a:ext>
                            </a:extlst>
                        </a:blip>
                        <a:srcrect>
                        </a:srcrect>
                        <a:stretch>
                            <a:fillrect>
                            </a:fillrect>
                        </a:stretch>
                    </p:blipfill>
                    </p:sppr>
                </p:pic>
            </p:oleobj>
        </mc:fallback>
    </mc:alternatecontent>
</a:graphicdata>

从上面文档代码可以看到 mc:fallback 可以通过 frameGraphic.Descendants<DocumentFormat.OpenXml.AlternateContentFallback>().FirstOrDefault() 拿到

而对应的图片可以使用下面代码拿到

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            var fallback = frameGraphic.Descendants<DocumentFormat.OpenXml.AlternateContentFallback>().FirstOrDefault();
            if (fallback == null)
            {
                return (false, null);
            }

            var picture = fallback.Descendants<DocumentFormat.OpenXml.Presentation.Picture>().FirstOrDefault();

            if (picture == null)
            {
                return (false, null);
            }

重要的代码是如何拿到 picture 的 a:blip r:embed="rId4" 的 rId4 的图片

在 OpenXML SDK 定义好了 BlipFill 可以通过下面代码拿到 rId 的值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            var embed = picture.BlipFill.Blip.Embed.Value;

而拿到 embed 就可以拿到对应的 Stream 可以写入文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
            var part = slidePart.GetPartById(embed);

            if (part is ImagePart imagePart)
            {
                if (imagePart.ContentType == "image/x-wmf" || imagePart.ContentType == "image/x-emf")
                {
                    var stream = part.GetStream(FileMode.Open, FileAccess.Read);
                    var fileName = Path.GetFileName(imagePart.Uri.OriginalString);
                    var file = Path.Combine(@"F:\林德熙是逗比", fileName);
                    File.WriteAllBytes(file, ReadAllBytes(stream));

                    return (true, new FileInfo(file));
                }
            }

上面代码写入文件是 “F:\林德熙是逗比” 小伙伴需要按照你的需求更改,上面代码也没有释放资源

这里的 ReadAllBytes 通过将 Stream 转 byte[] 方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        private static byte[] ReadAllBytes(Stream stream)
        {
            using (var memoryStream = new MemoryStream())
            {
                stream.CopyTo(memoryStream);
                return memoryStream.ToArray();
            }
        }

在 OpenbXML SDK 可以方便拿到资源,通过 var part = slidePart.GetPartById(embed) 方法,此时返回的是 part 可以用 GetStream 返回压缩包里面的资源

其实在 WinForms 可以通过更简单的代码创建图片

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
System.Drawing.Image img = System.Drawing.Image.FromStream(imagePart.GetStream());

这样就能完成在 Office 文件解析 ole 元素,但是只要 ole 元素没有写 Fallback 本文方法也没有用

如果我只有 ole 元素,我能否显示,有大神写了 The DotNet Heaven: Read OLE Object type image field in C#.net

本文代码放在 github 欢迎小伙伴访问,如果无法下载源代码,请到 gitee 下载

How to parse embedded file(OLE obejct) in pptx/docx · Issue #644 · OfficeDev/Open-XML-SDK

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
dotnet OpenXML 文本 BodyProperties 的 FontScale 与文本字号缩放
本文来告诉大家,在 OpenXML 的 BodyProperties 的 NormalAutoFit 的 FontScale 属性缩放文本框的文本字号的方法
林德熙
2021/12/31
6820
dotnet OpenXML 文本 BodyProperties 的 FontScale 与文本字号缩放
C# dotnet 使用 OpenXml 解析 PPT 元素的坐标和宽度高度
在阅读本文之前,我期望你能了解基础的 PPT 解析内容,或看我的入门级博客。本文将告诉大家如何从 PPT 里面解析出通用元素的 x 和 y 的值,以及元素的宽度和高度的值
林德熙
2020/03/20
1.7K0
dotnet OpenXML 元素 cNvPr NonVisual Drawing Properties 重复 id 标识处理
使用 OpenXML 的格式的 Office 文档的元素,使用 p:cNvPr Non-Visual Drawing Properties 的 Id 属性作为标识,在标准协议这个标识是唯一的,但实际很多文档都存在重复的标识。本文告诉大家在使用 Office 2016 版本测试重复 id 的行为
林德熙
2020/09/29
5740
dotnet OpenXML 元素 cNvPr NonVisual Drawing Properties 重复 id 标识处理
dotnet OpenXML 读取 PPT 动画进入退出强调动画类型
根据 ECMA-376 文档可以了解到,在 PPT 动画中,通过 cTn 也就是 OpenXML sdk 定义的 CommonTimeNode 类型的 PresetClass 属性,即可用来判断当前的动画类型
林德熙
2021/12/24
4510
dotnet OpenXML PPT 动画框架入门
本文将从 OpenXML 方面聊 PPT 的动画框架,本文是属于编程方面而不是 PPT 动画制作教程
林德熙
2021/12/23
9810
dotnet OpenXML PPT 动画框架入门
dotnet OpenXML 解析 PPT 文本字体获取详解
在 OpenXML 的 PPT 的文本的字体是一个比较复杂的概念,即使在 OpenXML SDK 的辅助下,也需要自己写很多的逻辑。通过 ECMA 376 文档里面散落在各地的描述,和 dotnetCampus.OfficeDocumentZipper 工具的实验帮助之下,我摸到了本文将要告诉大家的规则,以及逻辑的实现
林德熙
2020/08/12
1.6K0
dotnet OpenXML 解析 PPT 文本字体获取详解
dotnet OpenXML 转换 PathFillModeValues 为颜色特效
在 OpenXml 预设形状,有一些形状设置了 PathFillModeValues 枚举,此枚举提供了亮暗的蒙层特效。具体的特效是让形状选择一个画刷,在画刷上加上特效。如立体几何 Cube 形状,在 Cube 不同的面有不同的颜色,颜色的亮度不同
林德熙
2021/11/08
8670
dotnet OpenXML 解析 PPT 里表格的样式
在 PPT 里面的表格可以通过表格样式配置决定表格的样式,本文将和大家介绍如何获取和解析表格的样式
林德熙
2023/04/07
6000
dotnet OpenXML 读取 PPT 内嵌 xlsx 格式 Excel 表格的信息
在 Office 中,可以在 PPT 里面插入表格,插入表格有好多不同的方法,对应 OpenXML 文档存储的更多不同的方式。本文来介绍如何读取 PPT 内嵌 xlsx 格式的表格的方法
林德熙
2021/11/15
1.1K0
dotnet OpenXML 读取 PPT 内嵌 ole 格式 Excel 表格的信息
在 Office 中,可以在 PPT 里面插入表格,插入表格有好多不同的方法,对应 OpenXML 文档存储的更多不同的方式。本文来介绍如何读取 PPT 内嵌 ole 格式的 xls+ 表格的方法
林德熙
2021/09/08
1.4K0
dotnet OpenXML 为什么资源使用 Relationship 引用
在 OpenXML 文档格式里面,所有的资源以及页面之间的引用等,都是通过 Relationship 的引用,如资源需要通过 GetReferenceRelationship 的方法才能拿到。那为什么要这样设计呢
林德熙
2020/07/28
5330
dotnet OpenXML 解析 WPS 不规范的 PPT 文件的 cNvPr 重复 id 问题
在收到了反馈说有一份课件,打开解析就发现替换的元素不对,原因是这个课件里面的 Slide Master 里面存在一个元素的 id 和某个页面的元素 id 是相同的,这不符合 ECMA 376 的规范。通过读取文档的内容,发现这是 WPS 制作出来的 PPT 文件。本文做一个存档,用来告诉大家有这个坑
林德熙
2020/12/22
9970
C# dotnet 使用 OpenXml 解析 PPT 里面的视频
我期望看到本文的小伙伴是了解 OpenXML 的,如果想要解析 Office 的文档,我推荐使用使用 OpenXML SDK 这个开源的库,更多入门级博客请看 C# dotnet 使用 OpenXml 解析 PPT 文件
林德熙
2020/03/20
6810
dotnet OpenXML 的 Slide Master 和 Slide Layout 是什么
本文来告诉大家在解析 PPT 文档的时候,元素继承以及占位符需要用到的 Slide Master 和 Slide Layout 是什么
林德熙
2020/07/07
8780
dotnet OpenXML 元素 cNvPr NonVisual Drawing Properties 的属性作用
本文收集元素属性 cNvPr Non-Visual Drawing Properties 的属性的作用
林德熙
2020/09/29
9950
dotnet OpenXML 元素 cNvPr NonVisual Drawing Properties 的属性作用
dotnet OpenXML 解析 PPT 图表 面积图入门
本文告诉大家如何使用 OpenXML 解析 PPT 的图表,以面积图为入门例子告诉大家 OpenXML 的存储
林德熙
2023/04/07
1K0
dotnet OpenXML 解析 PPT 图表 面积图入门
dotnet OpenXML 读取形状轮廓线条样式序号超过主题样式列表数
在 OpenXML 中,默认的形状可以通过指定 LineReference 让形状使用文档主题里面的样式。文档主题里面包含多个样式,在形状里面指定样式通过的是序号的方法,如果在形状里面指定的序号超过了主题的数量,那么将会使用最后一项样式
林德熙
2021/05/31
5210
C# 使用openxml解析PPTX中的文本内容
      本文讨论的仅针对微软Office 2007以后的(OOXML定义)PowerPoint文档,Office 2007以前的用二进制格式定义的(ppt格式)文档不在本文讨论范围。
czwy
2023/10/22
5030
C# 使用openxml解析PPTX中的文本内容
dotnet OpenXML SDK 形状的 rect Shape Text Rectangle 属性对文本框的坐标影响
如下图,在形状里面的文本会先通过形状的 a:rect 拿到文本框相对于形状的矩形范围,然后文本又相对于文本框有一定的边距
林德熙
2021/02/04
6090
dotnet OpenXML 使用 MAUI 渲染 PPT 的面积图图表
我在做一个图表工具软件,这个软件使用 MAUI 开发。我的需求是图表的内容需要和 PPT 的图表对接,需要用到 OpenXML 解析 PPT 内容,读取到 PPT 图表元素的内容,接着使用 MAUI 渲染层绘制图表元素。图表工具软件需要在 Windows 平台和 Linux 平台上运行。在 Windows 下,我采用 WPF 应用,用来辟谣说 MAUI 不支持 WPF 应用。 在 Linux 选用 Ubuntu 系统,采用 GTKSharp 应用加上 Skia 渲染对接 MAUI 框架
林德熙
2023/04/07
2.1K0
dotnet OpenXML 使用 MAUI 渲染 PPT 的面积图图表
推荐阅读
相关推荐
dotnet OpenXML 文本 BodyProperties 的 FontScale 与文本字号缩放
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文