前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C# 使用openxml解析PPTX中的文本内容

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

作者头像
czwy
发布2023-10-22 15:06:17
3750
发布2023-10-22 15:06:17
举报
文章被收录于专栏:czwy的博客

前言

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

一、依赖类库

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

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

二、解析步骤

1.引入库

通过Nuget引入类库

代码语言:javascript
复制
<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对象

代码语言:javascript
复制
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。

代码语言:javascript
复制
 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。

代码语言:javascript
复制
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解析

代码语言:javascript
复制
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数据的文件流:

代码语言:javascript
复制
[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

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、依赖类库
  • 二、解析步骤
    • 1.引入库
      • 2.读取数据
        • 2.1 直接保存在slide*.xml文件的节点数据
        • 2.2 以oleObject对象的形式存储在word文档中
        • 2.3 以oleObject对象的形式存储在bin文件中
    • 参考资料:
    相关产品与服务
    对象存储
    对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档