C# 使用openxml解析PPTX中的文本内容
前言
本文讨论的仅针对微软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中的文本内容的更多相关文章
- s:textarea中的文本内容在什么时候才能被赋值给Action中的属性?
下面是jsp程序片段: <s:form id="startForm" name ="startForm" action="/hall/hall_ ...
- .net获取select控件中的文本内容
.net获取select控件中的文本内容 2009-11-28 21:19小V古 | 分类:C#/.NET | 浏览1374次 <select id="SecType" st ...
- 【转载】 C#往文件中追加文本内容信息
在C#的文件相关操作中,往已有的文件中追加相关内容信息也是常有的操作,例如我们在写网站日志文件的时候,很多时候是1天的日志存放在一个txt文件中,此时就涉及到往原有文件中追加写入文本内容信息,追加文本 ...
- 利用java从docx文档中提取文本内容
利用java从docx文档中提取文本内容 使用Apache的第三方jar包,地址为https://poi.apache.org/ docx文档内容如图: 目录结构: 每个文件夹的名称为日期加上来源,例 ...
- 使用openxml提取word中的文本和图片并转为Html
使用openxml提取word中的文本和图片 使用 openXml 提取 word 中的 Text 和 Drawing 使用 openXml 将 word 中的文本和图片转为Html 使用 openX ...
- C++中关于文本内容的实用操作集合(新)(添加一些关于文件流的介绍)
首先先给大家一个链接:http://baike.baidu.com/view/1679747.htm 主要是关于ios的使用,头文件要include<ios>,然后就可以调用下面的一些操作 ...
- 用python解析pdf中的文本与表格【pdfplumber的安装与使用】
我们接触到的很多文档资料都是以pdf格式存在的,比如:论文,技术文档,标准文件,书籍等.pdf格式使得用机器从中提取信息格外困难. 为了解决这个问题,我找到了几种解决方案,最后选择了python上的p ...
- 【编程技巧】JAVA读取url地址中的文本内容
应用场景:最常见的是有自已的网站,在注册广告联盟的时候.都需要下载一个文本文件来验证网站的有效性.例如淘宝.京东等都有这一过程 实现代码://读url地址的内容 public void ...
- 4-4 Selector有一个方法可以获取Selector中的文本内容---extract()
####### 例如: response.xpath('//div[@class ="entry-header"]/h1/text()').extract()
- Jsoup解析Html中文文档
jsoup 简介Java 程序在解析 HTML 文档时,相信大家都接触过 htmlparser 这个开源项目,我曾经在 IBM DW 上发表过两篇关于 htmlparser 的文章,分别是:从 HTM ...
随机推荐
- RMQ问题ST表
稀疏表(Sparse Table表) 解决静态RMQ,区间最值查询问题的数据结构,树状数组(BIT)解决动态前缀和问题的数据结构: 例:https://www.luogu.org/problemnew ...
- 文心一言 VS chatgpt (12)-- 算法导论3.1 6~7题
六.证明:一个算法的运行时间为θ(g(n))当且仅当其最坏情况运行时间为O(g(n)),且其最好情况运行时间为Ω(g(n)) . 文心一言: chatgpt: 要证明「一个算法的运行时间为θ(g(n) ...
- 2023-03-06:给定一个二维网格 grid ,其中: ‘.‘ 代表一个空房间 ‘#‘ 代表一堵 ‘@‘ 是起点 小写字母代表钥匙 大写字母代表锁 我们从起点开始出发,一次移动是指向四个基本方向之
2023-03-06:给定一个二维网格 grid ,其中: '.' 代表一个空房间 '#' 代表一堵 '@' 是起点 小写字母代表钥匙 大写字母代表锁 我们从起点开始出发,一次移动是指向四个基本方向之 ...
- 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 ...
- 2021-10-13:单词接龙。字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:序列中第一个单词是 beginWord 。序列中最后
2021-10-13:单词接龙.字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:序列中第一个单词是 beginWord .序列中最后 ...
- 2023-05-15:对于某些非负整数 k ,如果交换 s1 中两个字母的位置恰好 k 次, 能够使结果字符串等于 s2 ,则认为字符串 s1 和 s2 的 相似度为 k。 给你两个字母异位词 s1
2023-05-15:对于某些非负整数 k ,如果交换 s1 中两个字母的位置恰好 k 次, 能够使结果字符串等于 s2 ,则认为字符串 s1 和 s2 的 相似度为 k. 给你两个字母异位词 s1 ...
- Django admin管理工具的使用、定制及源码解析
admin组件使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.你可以在项目的 settings.py 中的 INSTALLE ...
- SignalR WebSocket通讯机制
1.什么是SignalR ASP.NET SignalR 是一个面向 ASP.NET 开发人员的库,可简化向应用程序添加实时 Web 功能的过程. 实时 Web 功能是让服务器代码在可用时立即将内容推 ...
- linux DNS域名解析
目录 一.DNS概念 二.域名格式类型 三.查询类型 四.解析类型 五.配置DNS 六.dns解析实验 1.配置正向解析 2.反向解析 3.主从解析 一.DNS概念 概念:域名和IP地址的相互映射的分 ...
- 解读与用户一起“跳动”的开源实时监控工具 HertzBeat
摘要:开源项目遇上华为云,会擦出怎样的火花? 在本期<开源实时监控工具HertzBeat如何与用户一起"跳动?>的主题直播中,HertzBeat & TanCloud 创 ...