前言

本文讨论的仅针对微软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中的文本内容的更多相关文章

  1. s:textarea中的文本内容在什么时候才能被赋值给Action中的属性?

    下面是jsp程序片段: <s:form id="startForm" name ="startForm" action="/hall/hall_ ...

  2. .net获取select控件中的文本内容

    .net获取select控件中的文本内容 2009-11-28 21:19小V古 | 分类:C#/.NET | 浏览1374次 <select id="SecType" st ...

  3. 【转载】 C#往文件中追加文本内容信息

    在C#的文件相关操作中,往已有的文件中追加相关内容信息也是常有的操作,例如我们在写网站日志文件的时候,很多时候是1天的日志存放在一个txt文件中,此时就涉及到往原有文件中追加写入文本内容信息,追加文本 ...

  4. 利用java从docx文档中提取文本内容

    利用java从docx文档中提取文本内容 使用Apache的第三方jar包,地址为https://poi.apache.org/ docx文档内容如图: 目录结构: 每个文件夹的名称为日期加上来源,例 ...

  5. 使用openxml提取word中的文本和图片并转为Html

    使用openxml提取word中的文本和图片 使用 openXml 提取 word 中的 Text 和 Drawing 使用 openXml 将 word 中的文本和图片转为Html 使用 openX ...

  6. C++中关于文本内容的实用操作集合(新)(添加一些关于文件流的介绍)

    首先先给大家一个链接:http://baike.baidu.com/view/1679747.htm 主要是关于ios的使用,头文件要include<ios>,然后就可以调用下面的一些操作 ...

  7. 用python解析pdf中的文本与表格【pdfplumber的安装与使用】

    我们接触到的很多文档资料都是以pdf格式存在的,比如:论文,技术文档,标准文件,书籍等.pdf格式使得用机器从中提取信息格外困难. 为了解决这个问题,我找到了几种解决方案,最后选择了python上的p ...

  8. 【编程技巧】JAVA读取url地址中的文本内容

    应用场景:最常见的是有自已的网站,在注册广告联盟的时候.都需要下载一个文本文件来验证网站的有效性.例如淘宝.京东等都有这一过程 实现代码://读url地址的内容        public void ...

  9. 4-4 Selector有一个方法可以获取Selector中的文本内容---extract()

    ####### 例如: response.xpath('//div[@class ="entry-header"]/h1/text()').extract()

  10. Jsoup解析Html中文文档

    jsoup 简介Java 程序在解析 HTML 文档时,相信大家都接触过 htmlparser 这个开源项目,我曾经在 IBM DW 上发表过两篇关于 htmlparser 的文章,分别是:从 HTM ...

随机推荐

  1. RMQ问题ST表

    稀疏表(Sparse Table表) 解决静态RMQ,区间最值查询问题的数据结构,树状数组(BIT)解决动态前缀和问题的数据结构: 例:https://www.luogu.org/problemnew ...

  2. 文心一言 VS chatgpt (12)-- 算法导论3.1 6~7题

    六.证明:一个算法的运行时间为θ(g(n))当且仅当其最坏情况运行时间为O(g(n)),且其最好情况运行时间为Ω(g(n)) . 文心一言: chatgpt: 要证明「一个算法的运行时间为θ(g(n) ...

  3. 2023-03-06:给定一个二维网格 grid ,其中: ‘.‘ 代表一个空房间 ‘#‘ 代表一堵 ‘@‘ 是起点 小写字母代表钥匙 大写字母代表锁 我们从起点开始出发,一次移动是指向四个基本方向之

    2023-03-06:给定一个二维网格 grid ,其中: '.' 代表一个空房间 '#' 代表一堵 '@' 是起点 小写字母代表钥匙 大写字母代表锁 我们从起点开始出发,一次移动是指向四个基本方向之 ...

  4. 2022-07-12:以下go语言代码输出什么?A:1 1;B:1.0 1.0;C:编译不通过;D:1.0 1。 package main import “fmt“ func main() {

    2022-07-12:以下go语言代码输出什么?A:1 1:B:1.0 1.0:C:编译不通过:D:1.0 1. package main import "fmt" func ma ...

  5. 2021-10-13:单词接龙。字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:序列中第一个单词是 beginWord 。序列中最后

    2021-10-13:单词接龙.字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:序列中第一个单词是 beginWord .序列中最后 ...

  6. 2023-05-15:对于某些非负整数 k ,如果交换 s1 中两个字母的位置恰好 k 次, 能够使结果字符串等于 s2 ,则认为字符串 s1 和 s2 的 相似度为 k。 给你两个字母异位词 s1

    2023-05-15:对于某些非负整数 k ,如果交换 s1 中两个字母的位置恰好 k 次, 能够使结果字符串等于 s2 ,则认为字符串 s1 和 s2 的 相似度为 k. 给你两个字母异位词 s1 ...

  7. Django admin管理工具的使用、定制及源码解析

    admin组件使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.你可以在项目的 settings.py 中的 INSTALLE ...

  8. SignalR WebSocket通讯机制

    1.什么是SignalR ASP.NET SignalR 是一个面向 ASP.NET 开发人员的库,可简化向应用程序添加实时 Web 功能的过程. 实时 Web 功能是让服务器代码在可用时立即将内容推 ...

  9. linux DNS域名解析

    目录 一.DNS概念 二.域名格式类型 三.查询类型 四.解析类型 五.配置DNS 六.dns解析实验 1.配置正向解析 2.反向解析 3.主从解析 一.DNS概念 概念:域名和IP地址的相互映射的分 ...

  10. 解读与用户一起“跳动”的开源实时监控工具 HertzBeat

    摘要:开源项目遇上华为云,会擦出怎样的火花? 在本期<开源实时监控工具HertzBeat如何与用户一起"跳动?>的主题直播中,HertzBeat & TanCloud 创 ...