C# 使用openxml解析PPTX中的文本内容

c#,使用,openxml,解析,pptx,文本,内容 · 浏览次数 : 51

小编点评

## 解析微软 Office 2007 PPTX 文档 本文针对微软 Office 2007 后面的 PPTX 文档进行解析,其中仅考虑了 Powerpoint 格式。 ### 1. 读取图片资源 * PPTX 文档可能包含图片资源,例如图表、图片等。 * 使用 `GetImageBySourceName()` 方法可以根据图片资源名称获取图片。 * 图片资源的访问权限取决于文档类型,可能需要通过文件路径或 URL 获取。 ### 2. 解析文本内容 * 使用 `Run()` 方法运行文档的 `Text` 属性。 * `Text` 属性包含文档中所有文字的字符串。 * 可以使用正则表达式或其他文本处理方法解析文本内容。 ### 3. 解析对象结构 * PPTX 文档可能包含对象结构,例如表格、列表等。 * 使用 `GetSection()` 和 `GetRow` 方法分别获取表格和列表对象。 * 可以遍历对象并访问其属性和方法。 ### 4. 解析数据格式 * PPTX 文档可能包含数据格式,例如日期、货币等。 * 使用 `GetFormattedString()` 或 `GetDoubleValue` 等方法获取数据格式的值。 * 可以格式化数据以符合特定格式要求。 ### 5. 解析引用关系 * PPTX 文档可能包含指向其他文档或图片的引用。 * 使用 `GetLinkBase()` 和 `GetTarget()` 方法获取引用关系。 * 可以根据引用关系访问相关文档或图片内容。 ### 6. 处理文本格式 * PPTX 文档可能包含文本格式,例如段落、字体等。 * 使用 `SetText` 或 `GetParagraphText` 等方法获取文本格式的内容。 * 可以格式化文本以符合特定格式要求。 ### 7. 其他操作 * 可以使用 `Save()` 方法将解析后的内容保存到其他文件格式中。 * 可以使用 `SaveAs` 方法根据文件类型保存内容。 ### 参考资料 * Office OpenXml SDK 使用 Fallback 图片显示 Ole 元素reading-compound-documents-in-c-sharp * 文档中对各种属性和方法的具体参数说明

正文

前言

      本文讨论的仅针对微软Office 2007以后的(OOXML定义)PowerPoint文档,Office 2007以前的用二进制格式定义的(ppt格式)文档不在本文讨论范围。

一、依赖类库

     本文需要依赖两个免费的第三方类库:DocumentFormat.OpenXml和FreeSpire.Doc。

     DocumentFormat.OpenXml用于加载解析pptx文档,FreeSpire.Doc用于解析pptx中嵌入的doc文档内容,详见解析嵌入的doc的文本。

二、解析步骤

1.引入库

通过Nuget引入类库

<packages>
  <package id="DocumentFormat.OpenXml" version="2.13.0" targetFramework="net452" />
  <package id="FreeSpire.Doc" version="7.11.0" targetFramework="net452" />
</packages>

2.读取数据

PPTX中的文本内容主要以三种形式存储。1、直接保存在slide*.xml文件的节点数据;2、以oleObject对象的形式存储在word文档中;3、以oleObject对象的形式存储在bin文件中。接下来针对这三种情况分别分析如何解析获取内容。

首先需要读取pptx文件,解析每一个页面的slide对象

using (var presentationDocument = PresentationDocument.Open(filePath, false))
{
    var presentationPart = presentationDocument.PresentationPart;
    var presentation = presentationPart.Presentation;
    // 先获取页面
    var slideIdList = presentation.SlideIdList;
    foreach (var slideId in slideIdList.ChildElements.OfType<SlideId>())
    {
        //TODO:解析页面的内容
    }

2.1 直接保存在slide*.xml文件的节点数据

直接保存在slide*.xml文件的文本数据只需遍历页面中的每一个paragraph对象即可,需要注意的是此处的用到的是DocumentFormat.OpenXml.Drawing.Paragraph

 foreach (var paragraph in
     slidePart.Slide.Descendants<DocumentFormat.OpenXml.Drawing.Paragraph>())
{
    contentText.Length = 0;
    foreach (var text in paragraph.Descendants<DocumentFormat.OpenXml.Drawing.Text>())
    {
        contentText.Append(text.InnerText);
    }
}

2.2 以oleObject对象的形式存储在word文档中

oleObject对象在slide*.xml文件中记录形式如下图:

progId的值为“Word.Document.8”表示嵌入的对象是Office 2007以前的数据格式,值为“Word.Document.12”表示嵌入的对象是Office 2007以后的OOXML定义的数据格式。通过r:id的值获取嵌入的文件对象及其ContentType。值为 "application/vnd.openxmlformats-officedocument.wordprocessingml.document"表示嵌入的对象是word文档,值为"application/vnd.openxmlformats-officedocument.oleObject"标识嵌入的是bin文件。

Office 2007以后的OOXML定义的数据格式直接通过DocumentFormat.OpenXml解析,需要注意的是在解析word中的段落需要用DocumentFormat.OpenXml.Wordprocessing.Paragraph

foreach (var choice in  
    slidePart.Slide.Descendants<DocumentFormat.OpenXml.AlternateContentChoice>())
{
    foreach (var oleobject in
        choice.Descendants<DocumentFormat.OpenXml.Presentation.OleObject>())
    {
        if (oleobject.ProgId.Value == "Word.Document.12")
        {
            var part = slidePart.GetPartById(oleobject.Id);
            if (part.ContentType == "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
            {
                 using (var stream = part.GetStream(FileMode.Open, FileAccess.Read))
                 {
                     using (var wordDocument = WordprocessingDocument.Open(stream, false))
                     {
                         var mainDocumentPart = wordDocument.MainDocumentPart;
                         var body = mainDocumentPart.Document.Body;
                         foreach (DocumentFormat.OpenXml.Wordprocessing.Paragraph paragraph in body.Descendants<DocumentFormat.OpenXml.Wordprocessing.Paragraph>())
                         { 
                             contentText.Length = 0;
                             foreach (var text in 
                     paragraph.Descendants<DocumentFormat.OpenXml.Wordprocessing.Text>())
                             {
                                  contentText.Append(text.InnerText);
                              }
                          }
                       }
                   }
               }
          }
      }
}

Office 2007以前的数据格式借助FreeSpire.Doc解析


else if (oleobject.ProgId.Value == "Word.Document.8")
{
    var part = slidePart.GetPartById(oleobject.Id);
    var stream = part.GetStream(FileMode.Open, FileAccess.Read);
    Spire.Doc.Document doc = new Spire.Doc.Document(stream);
    foreach (Spire.Doc.DocumentObject child in doc.Sections[0].Body.ChildObjects)
    {
        if (child is Spire.Doc.Documents.Paragraph)
            paraList.Add((child as Spire.Doc.Documents.Paragraph).Text);
        else if (child is Spire.Doc.Table)
        {
            Spire.Doc.Table table = child as Spire.Doc.Table;
            foreach (Spire.Doc.TableRow row in table.Rows)
            {
                foreach (Spire.Doc.TableCell cell in row.Cells)
                {
                    foreach (Spire.Doc.Documents.Paragraph paragraph in cell.Paragraphs)
                    {
                        paraList.Add(paragraph.Text);
                    }
                }
            }
        }
    }
    stream.Dispose();
}

2.3 以oleObject对象的形式存储在bin文件中

这种情况需要通过StgOpenStorage解析oleObject对象提取word数据的文件流:

[DllImport("ole32.dll")]
        private static extern int StgIsStorageFile(
            [MarshalAs(UnmanagedType.LPWStr)] string pwcsName);

[DllImport("ole32.dll")] static extern int StgOpenStorage( [MarshalAs(UnmanagedType.LPWStr)] string pwcsName, IStorage pstgPriority, STGM grfMode, IntPtr snbExclude, uint reserved, out IStorage ppstgOpen);
public MemoryStream parseOleObject(string fileName) { MemoryStream outBuffer=default(MemoryStream); if (StgIsStorageFile(fileName) == 0) { IStorage storage = null; if (StgOpenStorage( fileName, null, STGM.DIRECT | STGM.READ | STGM.SHARE_EXCLUSIVE, IntPtr.Zero, 0, out storage) == 0) { System.Runtime.InteropServices.ComTypes.STATSTG statstg; storage.Stat(out statstg, (uint)STATFLAG.STATFLAG_DEFAULT); IEnumSTATSTG pIEnumStatStg = null; storage.EnumElements(0, IntPtr.Zero, 0, out pIEnumStatStg); System.Runtime.InteropServices.ComTypes.STATSTG[] regelt = { statstg }; uint fetched = 0; uint res = pIEnumStatStg.Next(1, regelt, out fetched); if (res == 0) { while (res != 1) { string strNode = statstg.pwcsName; if (strNode == "Package") { switch (statstg.type) { case (int)STGTY.STGTY_STORAGE: { IStorage pIChildStorage; storage.OpenStorage(statstg.pwcsName, null, (uint)(STGM.READ | STGM.SHARE_EXCLUSIVE), IntPtr.Zero, 0, out pIChildStorage); } break; case (int)STGTY.STGTY_STREAM: { IStream pIStream; storage.OpenStream(statstg.pwcsName, IntPtr.Zero, (uint)(STGM.READ | STGM.SHARE_EXCLUSIVE), 0, out pIStream); outBuffer = pIStream.ReadToMemoryStream(); Marshal.FinalReleaseComObject(pIStream); Marshal.FinalReleaseComObject(pIEnumStatStg); Marshal.FinalReleaseComObject(storage); return outBuffer; } break; } } if ((res = pIEnumStatStg.Next(1, regelt, out fetched)) != 1) { statstg = regelt[0]; } } } } } return outBuffer; }

解析oleObject对象提取word数据的文件流后按照解析word对象的方式解析数据即可。


参考资料:

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

reading-compound-documents-in-c-sharp

与C# 使用openxml解析PPTX中的文本内容相似的内容:

C# 使用openxml解析PPTX中的文本内容

前言 本文讨论的仅针对微软Office 2007以后的(OOXML定义)PowerPoint文档,Office 2007以前的用二进制格式定义的(ppt格式)文档不在本文讨论范围。 一、依赖类库 本文需要依赖两个免费的第三方类库:DocumentFormat.OpenXml和FreeSpire.Do

C#使用MX Component实现三菱PLC软元件数据采集的完整步骤(仿真)

前言 本文介绍了如何使用三菱提供的MX Component插件实现对三菱PLC软元件数据的读写,记录了使用计算机仿真,模拟PLC,直至完成测试的详细流程,并重点介绍了在这个过程中的易错点,供参考。 用到的软件: 1. PLC开发编程环境GX Works2,GX Works2下载链接 https://

C#中使用CAS实现无锁算法

CAS 的基本概念 CAS(Compare-and-Swap)是一种多线程并发编程中常用的原子操作,用于实现多线程间的同步和互斥访问。 它操作通常包含三个参数:一个内存地址(通常是一个共享变量的地址)、期望的旧值和新值。 CompareAndSwap(内存地址,期望的旧值,新值) CAS 操作会比较

C# 使用SqlDataAdapter和DataSet来访问数据库

使用SqlDataAdapter和DataSet来访问数据库 报:已有打开的与此 Command 相关联的 DataReader,必须首先将它关闭 解决方法,使用 using 包住 connection using System; using System.Data; using System.Da

C#使用iKvm黑科技无缝接入JVM生态

前言 时间过得飞快,一转眼国庆假期也要过去了,再不更新博客就太咸鱼了…… 最近在开发AIHub的时候想找个C#能用的命名实体识别库,但一直没找到,AI生态方面C#确实不太丰富,这块还是得Python,但我又不想跟LLM一样用gRPC的方式来调用,感觉有点麻烦。 这时候发现好像JVM生态有不少这类NL

C# 使用ssh连接远程主机

c# 使用ssh连接远程主机(ssh.net演示) - axel10 - 博客园 (cnblogs.com)

.NET周报 【5月第4期 2023-05-27】

## 国内文章 ### C#使用词嵌入向量与向量数据库为大语言模型(LLM)赋能长期记忆实现私域问答机器人落地之openai接口平替 https://www.cnblogs.com/gmmy/p/17430613.html 在上一篇[文章](https://www.cnblogs.com/gmmy/

CefSharp自定义缓存实现

大家好,我是沙漠尽头的狼。 上文介绍了《C#使用CefSharp内嵌网页-并给出C#与JS的交互示例》,本文介绍CefSharp的缓存实现,先来说说添加缓存的好处: 提高页面加载加速:CefSharp缓存可以缓存已经加载过的页面和资源,当用户再次访问相同的页面时,可以直接从缓存中加载,而不需要重新下

2.1 C/C++ 使用数组与指针

C/C++语言是一种通用的编程语言,具有高效、灵活和可移植等特点。C语言主要用于系统编程,如操作系统、编译器、数据库等;C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统、图形用户界面、嵌入式系统等。C/C++语言具有很高的效率和控制能力,但也需要开发人员自行管理内存等底层资源,对于初学者来说可能会有一定的难度。

3.1 C/C++ 使用字符与指针

C/C++语言是一种通用的编程语言,具有高效、灵活和可移植等特点。C语言主要用于系统编程,如操作系统、编译器、数据库等;C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统、图形用户界面、嵌入式系统等。C/C++语言具有很高的效率和控制能力,但也需要开发人员自行管理内存等底层资源,对于初学者来说可能会有一定的难度。