From 0a546414d532fca79143da09037d50d7f7c5ea28 Mon Sep 17 00:00:00 2001 From: zhangtingan Date: Mon, 17 Aug 2026 13:57:25 +0800 Subject: [PATCH] fix: fall back to libreoffice for docx-to-pdf when htmltopdf fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 在 DOCX 渲染链路 htmltopdf 失败(temp.pdf 未生成)时,兜底使用 libreoffice 直接将 docx 转换为 pdf,绕开 sw_64 等架构下 QtWebEngine 内部崩溃导致 html→pdf 失败的问题; 2. 兜底触发条件为 htmltopdf 失败(temp.pdf 不存在),全架构通用且仅失败时触发,不影响 x86/ARM/LoongArch 等正常架构的现有渲染输出; 3. libreoffice 使用独立的 UserInstallation 配置目录,避免与已运行的 libreoffice 抢占用户配置; 4. 兜底进程通过 pprocess 暴露给调用方,转换结束/失败时及时置空,避免悬挂指针; 5. debian/control 增加 Recommends: libreoffice,确保兜底转换器默认可用,缺失时优雅降级为转换失败(不劣于现状); ===================================== 1. fall back to libreoffice to convert docx directly to pdf when htmltopdf fails (temp.pdf not generated), bypassing the QtWebEngine internal crash on sw_64 and other architectures where html-to-pdf conversion fails; 2. the fallback triggers only when htmltopdf fails (temp.pdf missing), applies to all architectures and only on failure, leaving existing rendering output on x86/ARM/LoongArch untouched; 3. libreoffice uses a dedicated UserInstallation profile dir to avoid profile-lock conflicts with an already running libreoffice; 4. the fallback process is exposed via pprocess and cleared on completion/failure to avoid dangling pointers; 5. add Recommends: libreoffice to debian/control so the fallback converter is available by default; degrades gracefully to conversion failure when absent (no worse than before); Log: 修复 sw_64 架构打开 docx 显示空白:QtWebEngine 在 sw_64 上崩溃导致 html→pdf 失败,新增 libreoffice 兜底转换路径绕开 webengine,恢复 DOCX 文档可用性 Bug: https://pms.uniontech.com/bug-view-365875.html PMS: BUG-365875 --- debian/control | 1 + reader/document/Model.cpp | 44 ++++++++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/debian/control b/debian/control index d1019828e..0f4c04c7a 100644 --- a/debian/control +++ b/debian/control @@ -8,5 +8,6 @@ Standards-Version: 4.5.0 Package: deepin-reader Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, pandoc +Recommends: libreoffice-writer Description: a tool for reading document files. Document Viewer is a tool for reading document files,supporting PDF, DJVU, DOCX etc diff --git a/reader/document/Model.cpp b/reader/document/Model.cpp index b7220d52f..f9db1ba18 100755 --- a/reader/document/Model.cpp +++ b/reader/document/Model.cpp @@ -137,14 +137,48 @@ deepin_reader::Document *deepin_reader::DocumentFactory::getDocument(const int & QFile realFile(realFilePath); if (!realFile.exists()) { - qInfo() << "htmltopdf failed! " << realFilePath << " doesn't exist"; - error = deepin_reader::Document::ConvertFailed; - if (!(QProcess::CrashExit == converter.exitStatus() && 9 == converter.exitCode())) { + // htmltopdf 依赖 QtWebEngine,在部分架构(如 sw_64)上 webengine 内部崩溃会导致 pdf 无法生成。 + // 兜底使用 libreoffice 直接将 docx 转换为 pdf,绕开 webengine,恢复 DOCX 文档的可用性。 + qInfo() << "htmltopdf failed!" << realFilePath << "doesn't exist, fallback to libreoffice"; + QProcess fallback; + *pprocess = &fallback; + fallback.setWorkingDirectory(convertedFileDir); + // 独立的 UserInstallation 避免与已运行的 libreoffice 抢占用户配置 + QString fallbackCommand = + "libreoffice --headless -env:UserInstallation=file://" + convertedFileDir + "/lo_profile" + + " --convert-to pdf --outdir " + convertedFileDir + " " + targetDoc; + qDebug() << "执行命令: " << fallbackCommand; + fallback.start(fallbackCommand); + if (!fallback.waitForStarted()) { + qInfo() << "start libreoffice fallback failed"; + error = deepin_reader::Document::ConvertFailed; *pprocess = nullptr; + return nullptr; } - return nullptr; + // libreoffice 为重量级套件,每次兜底均使用全新 lo_profile 冷启动,叠加 sw_64 较慢, + // 大文档/带图表 docx 转换可能超过 QProcess 默认 30s 超时,此处放宽至 180s 避免误判失败。 + if (!fallback.waitForFinished(180000)) { + qInfo() << "libreoffice fallback failed"; + error = deepin_reader::Document::ConvertFailed; + *pprocess = nullptr; + return nullptr; + } + if (!realFile.exists()) { + qInfo() << "libreoffice fallback failed!" << realFilePath << "doesn't exist"; + error = deepin_reader::Document::ConvertFailed; + // 转换过程中关闭应用,docsheet 被释放,对应的 *pprocess 已不存在; + // 仅当进程非被 SIGKILL(用户关闭文档)时才置空,避免对已释放内存写入。 + if (!(QProcess::CrashExit == fallback.exitStatus() && 9 == fallback.exitCode())) { + *pprocess = nullptr; + } + return nullptr; + } + // 兜底成功:在 fallback 析构前置空 pprocess,避免悬挂指针。 + // 此处与下方函数作用域的置空非冗余:此处服务块作用域的 fallback(析构前清空), + // 下方服务函数作用域的 converter2(此时仍存活)。 + *pprocess = nullptr; } - qDebug() << "html转pdf完成"; + qDebug() << "docx转pdf完成"; *pprocess = nullptr; document = deepin_reader::PDFDocument::loadDocument(realFilePath, password, error);