在C++中操作Office文档,有几种方法。
在Windows环境下面,可以使用COM调用,在Qt开发之中,通过QtActive的QAxObject来操作Microsoft Office的COM。
而在Linux等环境中,则可以使用libreoffice的libreofficekit。
libreoffice简介
根据libreoffice的官网介绍:
LibreOffice 是一款功能强大的办公软件,默认使用开放文档格式 (OpenDocument Format , ODF), 并支持 *.docx, *.xlsx, *.pptx 等其他格式。
它包含了 Writer, Calc, Impress, Draw, Base 以及 Math 等组件,可用于处理文本文档、电子表格、演示文稿、绘图以及公式编辑。
它可以运行于 Windows, GNU/Linux 以及 macOS 等操作系统上,并具有一致的用户体验。
准备libreofficekit开发库
libreofficekit的二进制分发,默认没有开发需要的头文件以及库文件,需要单独安装。
Fedora等使用rpm软件包的系统可以通过:
sudo dnf install libreofficekit-devel
来安装,Ubuntu等使用deb软件包的系统,包名字则是libreofficekit-dev。
如:
sudo apt install libreofficekit-dev
libreofficekit的开发包文件比较简单,甚至没有提供一个pkg-config脚本文件,也没有cmake文件。
只是把需要的几个头文件,都放在了一个目录里。
在Fedora系统里,libreofficekit-devel安装完文件列表如下:
~/$ rpm -ql libreofficekit-devel
/usr/include/LibreOfficeKit
/usr/include/LibreOfficeKit/LibreOfficeKit.h
/usr/include/LibreOfficeKit/LibreOfficeKit.hxx
/usr/include/LibreOfficeKit/LibreOfficeKitEnums.h
/usr/include/LibreOfficeKit/LibreOfficeKitGtk.h
/usr/include/LibreOfficeKit/LibreOfficeKitInit.h
/usr/include/LibreOfficeKit/LibreOfficeKitTypes.h
/usr/lib64/gir-1.0/LOKDocView-0.1.gir
所以,我们开发程序的时候,直接把/usr/include/LibreOfficeKit目录加入头文件目录就好了,否则就需要多写一层目录。
目录里面的文件,主要是LibreOfficeKit.h和LibreOfficeKit.hxx,分别使用在C语言和C++语言中,其实LibreOfficeKit.hxx就是包装了一层LibreOfficeKit.h,使用起来大同小异。
下面我们以C++语言为例。
libreofficekit初始化
libreofficekit的C++类库,使用了名字空间lok。我们下面要使用的类,如lok::Office,可以直接使用,也可以通过using语句把lok引入我们的名字空间。
使用libreofficekit打开Office文件之前,需要初始化一个lok::Office类。
需要使用的函数是lok_cpp_init,在LibreOfficeKit.hxx中,这是一个inline函数,源代码为:
/// Factory method to create a lok::Office instance.
inline Office* lok_cpp_init(const char* pInstallPath, const char* pUserProfileUrl = NULL)
{
LibreOfficeKit* pThis = lok_init_2(pInstallPath, pUserProfileUrl);
if (pThis == NULL || pThis->pClass->nSize == 0)
return NULL;
return new ::lok::Office(pThis);
}
需要注意的是,这个初始化语句的pInstallPath参数,这个指的是libreoffice的二进制可执行程序路径。
对于默认安装的libreoffice的Fedora系统来说,这个路径是:
/usr/lib64/libreoffice/program,而Ubuntu则是:
/usr/lib/libreoffice/program
。
所以,我们可以在程序开头实现:
using namespace lok;
int
main (int argc, char *argv[])
{
Office *office = lok_cpp_init ("/usr/lib64/libreoffice/program", nullptr);
}
加载Office文件
初始化lok::Office之后,就可以加载Office文档了,Office文档在libreofficekit中是类lok::Document。
值得注意的是,在libreoffice提供的文档LibreOfficeKit.hxx头文件中,初始化Document不能使用默认初始化方法,而要通过上文初始化的Office对象来加载,亦即使用Office的documentLoad方法。
docmentLoad方法的定义为:
Document* documentLoad(const char* pUrl, const char* pFilterOptions = NULL)
{
LibreOfficeKitDocument* pDoc = NULL;
if (LIBREOFFICEKIT_HAS(mpThis, documentLoadWithOptions))
pDoc = mpThis->pClass->documentLoadWithOptions(mpThis, pUrl, pFilterOptions);
else
pDoc = mpThis->pClass->documentLoad(mpThis, pUrl);
if (pDoc == NULL)
return NULL;
return new Document(pDoc);
}
而对照头文件开头:
/// The lok::Document class represents one loaded document instance.
class Document
{
private:
LibreOfficeKitDocument* mpDoc;
public:
/// A lok::Document is typically created by the lok::Office::documentLoad() method.
Document(LibreOfficeKitDocument* pDoc) :
mpDoc(pDoc)
{}
~Document()
{
mpDoc->pClass->destroy(mpDoc);
}
……
则不能看出,C++的头文件LibreOfficeKit.hxx中的lok::Document就是完全包装的LibreOfficeKit.h。
加载完Document以后,需要调用initializeForRendering方法,完成文档的渲染初始化。
Office文档的属性
加载完lok::Document以后,就可以通过它来查询或者控制文档了。
以下是Document主要支持的一些方法:
- int getParts() 获取文档的页数。对于word文档,这个getParts返回的是页数,对于工作表来说,这个返回的是子表。
- void setPart(int nPart) 设置当前的页数。
- void getPart() 获取当前的页数。
- char* getPartName(int nPart) 获取当前页的名称
- void getDocumentSize(long* pWidth, long* pHeight) 获取整个文档的大小
- void paintTile(unsigned char* pBuffer,
const int nCanvasWidth,
const int nCanvasHeight,
const int nTilePosX,
const int nTilePosY,
const int nTileWidth,
const int nTileHeight) 渲染文档。其中,nCanvasWidth、nCanvasHeight是画布大小,nTileWidth、nTileHeight是文档的右下角的坐标值,即后4个参数是原始文档被选择的区块,要区分一下。以下函数同理。 - void paintPartTile(unsigned char* pBuffer,
const int nPart,
const int nMode,
const int nCanvasWidth,
const int nCanvasHeight,
const int nTilePosX,
const int nTilePosY,
const int nTileWidth,
const int nTileHeight) 渲染指定页数的文档
比如,以下代码便是把整个文档,按照指定的大小,渲染到内存地址里去:
bool
renderToImage(Document *doc, int pn, int width, int height, unsiged char **buffer)
{
auto size = width * height * 4;
*buffer = new (size);
if (*buffer == nullptr)
{
cerr << "memory error: " << strerror (errno);
return false;
}
doc->paintPartTile (*buffer, pn, 0, width, height, 0, 0, width, height);
return true;
}
Office文档转换
Document有一个SaveAs方法,亦即“另存为”。
这个方法的原型为:
bool saveAs(const char* pUrl, const char* pFormat = NULL, const char* pFilterOptions = NULL);
第一个参数pUrl是文件路径。
第二个是pFormat是文件格式,默认为NULL,如果为NULL的时候,libreofficekit将通过pUrl的后缀名来确定文件格式。
比如,以下代码,可以把Office文档的指定页数,转成一个文本文档,只包含文档中的文字。
void
toTextFile (Document *doc, int pn, const char *txt_name)
{
doc->setPart (pn);
doc->saveAs (txt_name, "txt");
}
而以下代码,则可以把Office的指定页数,保存成一个png格式的图片,对于预览文档非常使用。
void
toPngFile (Document *doc, int pn, const char *png_file)
{
doc->setPart (pn);
doc->saveAs (png_file, "png");
}