From 8aca7ade42a2008d8bebb1d24513869582e20c7c Mon Sep 17 00:00:00 2001 From: liyepan Date: Thu, 22 Oct 2026 18:50:18 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20v0.10.4=20=E2=80=94=E2=80=94=20=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=94=B9=E8=B5=B0=20QPdfWriter=EF=BC=8C=E5=BD=BB?= =?UTF-8?q?=E5=BA=95=E7=BB=95=E5=BC=80=E6=89=93=E5=8D=B0=E5=AD=90=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 之前用 QPrinter 出 PDF:它会先初始化平台打印引擎(Windows 枚举打印机 / 打印后台,Linux 走 CUPS),没有打印机或服务异常时就会卡住 - 改用 QPdfWriter + QTextDocument::print:纯写文件,不碰打印栈,三端一致 - 相应把 QtPrintSupport 从工程里去掉,二进制也小了一圈 - 文件对话框策略调整:Linux 强制 Qt 自带(GTK 原生在这台机器会卡), Windows / macOS 恢复系统原生对话框 - 端到端验证:从应用 UI 触发导出 → 生成 2 页 A4 PDF - 版本 0.10.4 --- README.md | 11 ++++++----- VoiletMark.pro | 4 ++-- src/MainWindow.cpp | 46 +++++++++++++++++++++++++++++++--------------- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 2834226..a48320d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ 一个轻量的跨平台 Markdown 编辑器,基于 **Qt 5 / C++17** 构建。 -当前版本 **0.10.3**。 +当前版本 **0.10.4**。 ## 界面 @@ -94,11 +94,12 @@ **文件 → 导出 PDF…**(`Ctrl+E`)直接弹保存对话框,选个位置就导出,没有中间预览。 A4 纸、16mm 页边距。 -文件对话框一律用 Qt 自带的实现(`DontUseNativeDialog`), -不走系统原生选择器——GTK 原生对话框在部分桌面环境下会因为 dconf / gvfs -不可用而卡住,这也是之前「点导出就没反应」的原因。 +Linux 上文件对话框强制用 Qt 自带的实现(`DontUseNativeDialog`): +GTK 原生选择器在 dconf / gvfs 不可用时会导致「点导出没反应」。 +Windows / macOS 保留系统原生对话框。 -导出用的是 Qt 自带的 `QPrinter` + `QTextDocument`,不需要额外的 PDF 库。 +导出用的是 Qt 自带的 `QPdfWriter` + `QTextDocument`,不需要额外的 PDF 库, +也不经过任何平台的打印子系统(Windows 打印后台、CUPS),所以三端行为一致。 支持标题、列表(含任务列表)、引用、代码块、行内代码、粗体 / 斜体 / 删除线、 链接、表格;图片只输出 alt 文字。 diff --git a/VoiletMark.pro b/VoiletMark.pro index 11f89b6..273bc2c 100644 --- a/VoiletMark.pro +++ b/VoiletMark.pro @@ -1,8 +1,8 @@ TEMPLATE = app TARGET = VoiletMark -VERSION = 0.10.3 +VERSION = 0.10.4 -QT += core gui widgets printsupport +QT += core gui widgets CONFIG += c++17 warn_on diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 9ab08a7..c6d71bd 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -27,9 +27,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -52,6 +52,17 @@ namespace { constexpr int kMaxRecentFiles = 8; constexpr int kDefaultFontSize = 12; +// Linux 桌面上 Qt 会尝试 GTK 原生文件选择器,dconf / gvfs 不可用时它会卡住, +// 所以只在 Linux 上强制使用 Qt 自己的对话框;Windows / macOS 保留原生对话框。 +QFileDialog::Options fileDialogOptions() +{ +#if defined(Q_OS_LINUX) + return QFileDialog::DontUseNativeDialog; +#else + return QFileDialog::Options(); +#endif +} + const char kWelcomeText[] = R"VOILETMARK( # 欢迎使用 VoiletMark @@ -613,7 +624,7 @@ void MainWindow::openFile() const QString path = QFileDialog::getOpenFileName( this, tr("打开 Markdown 文件"), startDirectory(), tr("Markdown 文件 (*.md *.markdown *.mdown *.mkd);;文本文件 (*.txt);;所有文件 (*)"), - nullptr, QFileDialog::DontUseNativeDialog); + nullptr, fileDialogOptions()); if (path.isEmpty()) return; openPath(path); @@ -667,7 +678,7 @@ bool MainWindow::saveFileAs() QString path = QFileDialog::getSaveFileName( this, tr("另存为"), startDirectory(), tr("Markdown 文件 (*.md);;文本文件 (*.txt);;所有文件 (*)"), - nullptr, QFileDialog::DontUseNativeDialog); + nullptr, fileDialogOptions()); if (path.isEmpty()) return false; @@ -685,7 +696,7 @@ void MainWindow::exportPdf() QString target = QFileDialog::getSaveFileName( this, tr("导出 PDF"), QDir(startDirectory()).filePath(suggested), - tr("PDF 文件 (*.pdf)"), nullptr, QFileDialog::DontUseNativeDialog); + tr("PDF 文件 (*.pdf)"), nullptr, fileDialogOptions()); if (target.isEmpty()) return; if (!target.endsWith(QStringLiteral(".pdf"), Qt::CaseInsensitive)) @@ -695,18 +706,23 @@ void MainWindow::exportPdf() QTextDocument document; MarkdownRenderer::render(m_editor->toPlainText(), &document); - document.setPageSize(QSizeF(672.76, 1001.57)); - QPrinter printer(QPrinter::HighResolution); - printer.setOutputFormat(QPrinter::PdfFormat); - printer.setOutputFileName(target); - printer.setPageSize(QPageSize(QPageSize::A4)); - printer.setPageMargins(QMarginsF(16, 16, 16, 16), QPageLayout::Millimeter); - printer.setDocName(QFileInfo(target).completeBaseName()); + { + // 用 QPdfWriter 直接写 PDF,不碰任何平台的打印子系统: + // Windows 的打印后台、Linux 的 CUPS 在没有打印机或服务异常时都可能卡住。 + QPdfWriter writer(target); + writer.setPageSize(QPageSize(QPageSize::A4)); + writer.setPageMargins(QMarginsF(16, 16, 16, 16), QPageLayout::Millimeter); + writer.setTitle(QFileInfo(target).completeBaseName()); - QApplication::setOverrideCursor(Qt::WaitCursor); - document.print(&printer); - QApplication::restoreOverrideCursor(); + const QRectF paintRect = writer.pageLayout().paintRectPixels(writer.resolution()); + const qreal scale = 96.0 / writer.resolution(); + document.setPageSize(QSizeF(paintRect.width() * scale, paintRect.height() * scale)); + + QApplication::setOverrideCursor(Qt::WaitCursor); + document.print(&writer); + QApplication::restoreOverrideCursor(); + } if (!QFileInfo::exists(target) || QFileInfo(target).size() <= 0) { QMessageBox::warning(this, tr("导出失败"), @@ -979,7 +995,7 @@ void MainWindow::chooseFolder() { const QString directory = QFileDialog::getExistingDirectory( this, tr("打开文件夹"), startDirectory(), - QFileDialog::ShowDirsOnly | QFileDialog::DontUseNativeDialog); + QFileDialog::ShowDirsOnly | fileDialogOptions()); if (!directory.isEmpty()) openFolder(directory); }