C++(Qt) 和 Word、Excel、PDF 交互總結


閱讀本文大概需要 6 分鍾

日常開發軟件可能會遇到這類小眾需求,導出數據到 WordExcel 以及 PDF文件,如果你使用 C++ 編程語言,那么可以選擇的方案不是很多,恰好最近剛好有這部分需求,整理下這段時間踩過的坑,方便后人

讀寫 Word

日常開發的軟件使用最多的應該是導出數據到 Word 文檔中,目前可以用的方案有這幾種

沒有十全十美的方案,任何方案都存在優點和缺點,下面來詳細看下這幾種方案的優缺點以及適用場景

XML 模板替換

原理:事先編輯好一份 Word 模板,需要替換內容的
地方預留好位置,然后使用特殊字段進行標記,后面使用代碼進行全量替換即可完成

優點

  • 代碼量相對較少、導出速度快
  • 跨平台,支持多個系統,系統不安裝 office 也能導出;
  • 支持圖片以及固定格式導出;

缺點

  • 導出格式固定,可擴展性不強,如果需求變化導出格式變了,那么模板也要跟着改變;
  • 一種格式對應一份模板,如果導出格式較多,需要准備的模板文件較多,這樣比較繁瑣;
  • 需要 Word 2003 以上版本;

舉個栗子

我們先編輯一份 Word 模板文檔,內容大概如下所示:

  • 將該文檔另存為 Word XML 文檔 XML-Template.xml
  • 讀取文檔內容進行變量替換
    QFile file("XML-Template.xml");
    if (!file.open(QIODevice::ReadOnly))
    {
        qDebug() << "open xxml file fail. " << file.errorString();
        return 0;
    }
    QByteArray baContent = file.readAll();
    file.close();
    QString strAllContent = QString::fromLocal8Bit(baContent);

    strAllContent.replace("$VALUE0", "1");
    strAllContent.replace("$VALUE1", QString::fromLocal8Bit("法外狂徒張三"));
    strAllContent.replace("$VALUE2", QString::fromLocal8Bit("考試不合格"));
    strAllContent.replace("$VALUE3", "2");
    strAllContent.replace("$VALUE4", QString::fromLocal8Bit("李四"));
    strAllContent.replace("$VALUE5", QString::fromLocal8Bit("合格"));

    QFile newFile("export.doc");
    if (!newFile.open(QIODevice::WriteOnly))
    {
        qDebug() << "file open fail." << newFile.errorString();;
        return 0;
    }

    newFile.write(strAllContent.toLocal8Bit());
    newFile.close();
  • 保存替換后的內容,寫入文件

可以看出來這種方式比較繁瑣,重點是編輯固定的模板格式,而且編輯好后保存成XML格式后還需要繼續調整,這種 XML 格式標簽很多,不小心就修改錯了,導致導出的文檔打不開

這種方式適合模板內容不太復雜,內容較少的情況下使用

COM 組件方式

原理:采用 Micro Soft公開的接口進行通訊,進行讀寫時會打開一個 `Word進程來交互

COM 技術概述

Qt 為我們提供了專門進行交互的類和接口,使用 Qt ActiveX框架就可以很好的完成交互工作

優點

  • 實現簡單,快速上手;

缺點

  • 導出寫入速度慢,因為相當於打開 word 文檔操作;
  • Windows平台可用,其它平台失效;
  • 需要程序運行的電腦安裝 office word,否則調用失敗

舉個栗子

使用時需要引入對應的模塊,在 pro 文件引入模塊

QT  *= axcontainer

打開文檔寫入內容

QAxObject *pWordWidget = new(std::nothrow) QAxObject;

bool bResult = pWordWidget->setControl("word.Application");

if (!bResult)
{
    return false;
}

// 設置是否顯示
pWordWidget->setProperty("Visible", false);

QAxObject *pAllDocuments = pWordWidget->querySubObject("Documents");

if(nullptr == pAllDocuments)
{
    return false;
}

// 新建一個空白文檔
pAllDocuments->dynamicCall("Add (void)");

// 獲取激活的文檔並使用
QAxObject *pActiveDocument = pAllDocuments->querySubObject("ActiveDocument");
if(nullptr == pActiveDocument)
{
    return false;
}

// 插入字符串
QAxObject *pSelectObj = pWordWidget->querySubObject("Selection");
if (nullptr != pSelectObj)
{
    pSelectObj->dynamicCall("TypeText(const QString&)", "公眾號:devstone");
}

……

可以看出來使用起來不難,對於新手友好一點,很多寫入操作方法比較繁瑣,需要自己重新封裝一套接口

  • 這種方案比較適合那些排版比較復雜,圖片、文字、表格混排的場景下,而且內容都是動態變化的,可以很好的實現定制化

  • 當然了它的缺點也不少,也有一些坑,有時候莫名其妙會失敗,還有就是比如你電腦安裝的 Word 沒有激活,那么每次啟動會彈激活窗口

  • 還有就是這種方式要求所有的路徑必須是本地化的,比如 D:\\Soft\test.png

  • 使用前最好讀取注冊表判斷當前電腦是否安裝了 Office Word,如果沒有安裝,直接讀取操作肯定會崩潰

這種方式同樣適用於寫入 Excel 文件,后面再說

HTML 方式

原理:這種方式得益於 Word支持 HTML格式導出渲染顯示,那么反向也可以支持,需要我們拼接 HTML格式內容,然后寫入文件保存成 .doc格式

優點

  • 跨平台,不僅限於 Windows平台,代碼可擴展性比較好
  • 導出速度快、代碼可擴展;

缺點

  • 字符串拼接 HTML 容易出錯,缺失標簽導出后無法顯示;
  • 插入的圖片是本地圖片文件的鏈接,導出的 word文檔拷貝到其它電腦圖片無法顯示

舉個栗子

QString HTML2Word::getHtmlContent()
{
    QString strHtml = "";
    strHtml += "<html>";
    strHtml += "<head>";
    strHtml += "<title>測試生成word文檔</title>";
    strHtml += "<head>";
    strHtml += "<body style=\"bgcolor:yellow\">";
    strHtml += "<h1 style=\"background-color:red\">測試qt實現生成word文檔</h1>";
    strHtml += "<hr>";
    strHtml += "<p>這里是插入圖片<img src=\"D:\\title.jpg" alt=\"picture\" width=\"100\" height=\"100\"></p>";
    strHtml += "</hr>";
    strHtml += "</body>";
    strHtml += "</html>";

    return strHtml;
}

// 保存寫入文件
QFile file("D:/htmp2Word.doc");
if (!file.open(QIODevice::WriteOnly))
{
    return false;
}

QTextStream out(&file);
out << getHtmlContent();
file.close();

這種方式難點在於 HTML格式拼接,任何缺失字段都會導致導出失敗,適合小眾需求下導出

圖片問題其實可以手動進行轉化,文檔導出成功后手動拷貝內容到新的文檔,這樣圖片就真正插入到文檔中,文檔發送給別人也不會丟失圖片了

還有一個坑就是:如果你使用 WPS 打開導出的文檔,默認顯示的是 web視圖,需要手動進行調整

某些電腦分辨率變化也會導致生成的文檔中字體等產生變化

第三方開源庫

可以使用的第三方庫幾乎沒有,網絡上找到的有這么幾個

  • OpenOffice: 兼容性差,集成調用難度大
  • LibOffice: 太龐大,不容易集成
  • DuckX: 太小眾,只能簡單的使用
  • docx:小眾庫

DuckX庫
docx庫

在讀寫 Word這部分,C++ 基本沒有可以使用的第三方庫,不像其他語言JavaC#Python有很多可以選擇,這個痛苦也只有 C++ 程序員能夠理解了吧

所以怎么選擇還是看自己項目需求吧,沒有十全十美的方案


上面說了這么多,都是導出生成 Wrod,那么下面來看看有那些方式可以讀取顯示 Word內容

這種需求應該不會很多,而且顯示難度更大一些

使用 COM組件方式,即采用 QAxWidget框架顯示 office 文檔內容,本質上就是在我們編寫的 Qt 界面上嵌入 office 的軟件,這種方式其實和直接打開 Word查看沒有啥區別,效果、性能上不如直接打開更好一些

目前一般都會采用折中方案,把 Word 轉為 PDF 進行預覽加載顯示,我們知道 PDF 渲染庫比較多,生態相對來說要好一些,在選擇上就更廣泛些,如何使用后面部分有專門介紹 PDF章節

讀寫 Excel

目前有一個支持比較好的第三方庫可以使用,整體使用基本可以滿足日常使用

QXlsx

這款開源庫支持跨平台,Linux、Windows、Mac、IOS、Android,使用方式支持動態庫調用和源碼直接集成,非常方便

編譯支持 qmakecmake,可以根據你自己的項目直接集成編譯,讀寫速度非常快

QXlsx::Document xlsx;

// 設置一些樣式
QXlsx::Format titleFormat;
titleFormat.setBorderStyle(QXlsx::Format::BorderThin);  // 邊框樣式
titleFormat.setRowHeight(1,1,30);   // 設置行高
titleFormat.setHorizontalAlignment(QXlsx::Format::AlignHCenter);   // 設置對齊方式

// 插入文本
xlsx.write(1,1, "微信公眾號:devstone", titleFormat);

// 合並單元格
xlsx.mergeCells(QXlsx::CellRange(2,1,4,4), titleFormat);

// 導出保存
xlsx.saveAs("D:/xlsx_export.xlsx");

// 添加工作表
xlsx.addSheet("devstone");

可以看到上手非常容易、各個函數命名也貼近 Qt Api,是一款非常良心的開源軟件

PS:注意該軟件使用 MIT 許可協議,這樣對於很多個人或者公司來說非常良心,意味着你可以無償使用、修改該項目,但是必須在你項目中也添加同樣的 MIP許可

上面也提到了,還可以使用 COM 組件的方式讀寫 Excel,不過有了這款開源庫基本就可以告別 COM組件方式了

讀寫 PDF

PDF相關開源庫挺多的,給了 C++ 程序員莫大的幫助,目前可用的主要有這些

其中 mupdfpoppler 屬於功能強大但是很難編譯的那種,需要有扎實的三方庫編譯能力,否則面對 n 個依賴庫會無從下手

不過可喜的是 Github 上有兩個開源庫可以供選擇

qpdf 庫

這個庫其實封裝了 pdf.js庫,使用 WebEngine來執行 JavaScript進而加載文件

項目地址

  • 直接從本地文件加載;
  • 支持從內存數據直接加載渲染 PDF 內容;

這種方式對環境有特殊要求了,如果你的項目使用的 Qt 版本不支持 WebEngine,那么就無法使用

qtpdf 庫

這個庫是 Qt 官方親自操刀對第三方庫進行了封裝,暴露的 APIQt 類似,使用起來非常舒服

Qt 官方

代碼結構以及使用 Demo

小試牛刀

關於如何使用,官方已經給了我們非常詳細的步驟了,直接跟着下面幾步就 OK 了

官方教程

git clone git://code.qt.io/qt-labs/qtpdf
cd qtpdf
git submodule update --init --recursive
qmake
make
cd examples/pdf/pdfviewer
qmake
make

./pdfviewer /path/to/my/file.pdf

可以看到使用了谷歌開源的 pdfium 三方庫,編譯時需要單獨更新下載這個庫,因為某些原因可能你無法下載,不過好在有人在 GitHub上同步了這個倉庫的鏡像,有條件還是建議直接下載最新穩定版的

可正常訪問的倉庫地址:https://github.com/PDFium/PDFium

相關類可以看這個文檔:https://developers.foxit.com/resources/pdf-sdk/c_api_reference_pdfium/modules.html

最后還要注意項目開源協議:pdfium引擎開始來自於福昕,一個中國本土的軟件公司,Google與其合作最終進行了開源,目前采用的是 BSD 3-Clause 協議,這種協議允許開發者自由使用、修改源代碼,也可以修改后重新發布,允許閉源進行商業行為,不過需要你在發布的產品中包含原作者代碼中的 BSD 協議

總結

以上就是項目中常用的文檔處理方法總結,當然了肯定也還有其它方案可以實現,畢竟條條大路通羅馬,如果你還要不錯的方案和建議歡迎留言

PS: 以上方案和對應的源碼編譯、使用例子會統一上傳到 GitHub對應的倉庫,方便后人使用

取之互聯網、回報互聯網

原創不易,如果覺得對你有幫助,歡迎點贊、在看、轉發

推薦閱讀


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM