打印不止是QPrinter:深入Qt Print Support框架的内核设计与跨平台输出管道

从平台抽象层到PostScript生成,从PDF导出到高DPI打印------揭秘Qt如何在Windows GDI、macOS CUPS、Linux Print Dialog之间构建统一的打印基础设施

前言

在Qt应用开发中,打印功能往往被低估。大多数开发者只知道QPrinter配合QPrintDialog就能"打印",但当你需要在Windows GDI、macOS CUPS、Linux三种完全不同的打印子系统上实现一致的打印输出时,当你需要在300 DPI的激光打印机上精确渲染复杂矢量图形时,当你需要生成符合PDF/A标准的归档文档时------你才会发现Qt Print Support框架的精妙之处。

本文将深入Qt Print Support框架的内核设计,从平台抽象层到PostScript生成,从PDF导出到高DPI打印,为你揭示Qt如何在异构打印子系统之上构建统一输出管道的完整架构。

一、Print Support框架架构概览

1.1 核心类层次结构

Qt Print Support框架采用了分层架构设计,核心类层次如下:

复制代码
QPrinter (核心设备抽象)
    ├── QPrinterInfo (打印机信息管理)
    ├── QPrintDialog (打印对话框)
    ├── QPageSetupDialog (页面设置对话框)
    ├── QPrintPreviewDialog (打印预览对话框)
    └── QPrintPreviewWidget (打印预览组件)

平台抽象层 (QPlatformPrinterSupport)
    ├── Windows (QWin32PrintEngine)
    ├── macOS (QCocoaPrinterSupport)
    └── Linux (QCupsPrinterSupport)

源码路径:src/printsupport/kernel/

1.2 打印管道的三层架构

Qt的打印系统采用三层架构:

  1. 应用层QPrinter + QPaintDevice接口
  2. 引擎层QPaintEngine的打印专用实现
  3. 平台层:操作系统原生打印API封装
cpp 复制代码
// 核心抽象:QPrinter继承自QPaintDevice
class Q_PRINTSUPPORT_EXPORT QPrinter : public QPaintDevice
{
    Q_DECLARE_PRIVATE(QPrinter)
public:
    enum PrinterMode {
        ScreenResolution,
        PrinterResolution,
        HighResolution
    };
    
    explicit QPrinter(PrinterMode mode = ScreenResolution);
    ~QPrinter();
    
    // 核心输出接口
    void setOutputFormat(OutputFormat format);
    OutputFormat outputFormat() const;
    
    // 打印引擎获取
    QPaintEngine *paintEngine() const override;
    
protected:
    QPrinter(QPrinterPrivate *ptr);
};

二、QPrinter核心实现原理

2.1 QPrinter的构造与初始化

QPrinter的构造函数接受PrinterMode参数,这个参数决定了打印输出的分辨率策略:

cpp 复制代码
QPrinter::QPrinter(PrinterMode mode)
    : QPaintDevice()
{
    d_ptr = new QPrinterPrivate(this);
    
    // 根据模式设置分辨率
    switch (mode) {
    case ScreenResolution:
        d_ptr->resolution = qt_defaultDpi();
        break;
    case PrinterResolution:
        d_ptr->resolution = 72;  // PostScript标准DPI
        break;
    case HighResolution:
        d_ptr->resolution = 300;  // 默认高精度
        break;
    }
    
    // 创建平台相关的打印引擎
    d_ptr->initEngine();
}

源码路径:src/printsupport/kernel/qprinter.cpp

2.2 打印引擎的创建与选择

QPrinterPrivate::initEngine()根据输出格式选择合适的打印引擎:

cpp 复制代码
void QPrinterPrivate::initEngine()
{
    delete engine;
    engine = nullptr;
    
    if (outputFormat == QPrinter::PdfFormat) {
        // PDF引擎:跨平台矢量输出
        engine = new QPdfPrintEngine(this);
    } else {
        // 原生打印引擎:平台相关
        QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get();
        if (ps) {
            engine = ps->createPaintEngine(printerMode);
        }
    }
    
    if (engine) {
        // 设置引擎的分辨率
        engine->setProperty(QPrintEngine::PPK_Resolution, resolution);
    }
}

2.3 PDF打印引擎深度解析

QPdfPrintEngine是Qt最重要的打印引擎之一,它不依赖操作系统打印API,直接生成PDF文件:

cpp 复制代码
// src/printsupport/pdf/qpdfprintengine.cpp
bool QPdfPrintEngine::begin(QPaintDevice *pdev)
{
    // 1. 初始化PDF文档结构
    pdfEngine = new QPdfEngine();
    pdfEngine->setOutputFileName(outputFileName);
    
    // 2. 设置页面尺寸
    QSizeF pageSize = pageLayout.pageSize().size(QPageSize::Point);
    pdfEngine->setPageSize(pageSize);
    
    // 3. 设置元数据
    pdfEngine->setCreator(QStringLiteral("Qt %1").arg(QT_VERSION_STR));
    pdfEngine->setTitle(docName);
    
    // 4. 开始PDF生成
    return pdfEngine->begin(pdev);
}

PDF引擎的关键特性:

  1. 矢量图形保留 :所有QPainter绘图命令都转换为PDF矢量指令
  2. 字体嵌入:确保跨平台字体一致性
  3. 图像压缩:自动选择合适的JPEG/Flate压缩
  4. 色彩管理:支持CMYK色彩空间(需要平台支持)

三、平台打印引擎实现

3.1 Windows GDI打印引擎

Windows平台使用GDI(Graphics Device Interface)进行打印:

cpp 复制代码
// src/plugins/printsupport/windows/qwin32printengine.cpp
class QWin32PrintEngine : public QPaintEngine
{
public:
    bool begin(QPaintDevice *pdev) override;
    bool end() override;
    
    void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override;
    void drawPath(const QPainterPath &path) override;
    
private:
    HDC m_hdc;  // Windows设备上下文
    DOCINFO m_docInfo;
};

Windows打印流程:

cpp 复制代码
bool QWin32PrintEngine::begin(QPaintDevice *pdev)
{
    // 1. 打开打印机
    OpenPrinterW(printerName, &hPrinter, nullptr);
    
    // 2. 获取设备模式
    DEVMODEW *devMode = getDevmode();
    
    // 3. 创建设备上下文
    m_hdc = CreateDCW(L"WINSPOOL", printerName, nullptr, devMode);
    
    // 4. 开始打印文档
    memset(&m_docInfo, 0, sizeof(DOCINFO));
    m_docInfo.cbSize = sizeof(DOCINFO);
    m_docInfo.lpszDocName = docName.utf16();
    
    StartDocW(m_hdc, &m_docInfo);
    
    // 5. 开始第一页
    StartPage(m_hdc);
    
    return true;
}

3.2 macOS CUPS打印引擎

macOS使用CUPS(Common Unix Printing System)和Apple的打印框架:

objc 复制代码
// src/plugins/printsupport/cocoa/qcocoaprinterengine.mm
@implementation QCocoaPrintEngine

- (BOOL)beginWithPaintDevice:(QPaintDevice *)pdev
{
    // 1. 获取Printing Dialog
    NSPrintInfo *printInfo = [NSPrintInfo sharedPrintInfo];
    
    // 2. 设置页面格式
    NSPageLayout *pageLayout = [NSPageLayout pageLayout];
    [pageLayout beginSheetWithPrintInfo:printInfo
                          modalForWindow:qt_mac_window_for_view(qt_view)
                          delegate:nil
                          didEndSelector:nil
                          contextInfo:nil];
    
    // 3. 创建打印操作
    NSPrintOperation *op = [NSPrintOperation
        printOperationWithView:qtPrintView
                     printInfo:printInfo];
    
    // 4. 运行打印对话框
    [op runOperationModalForWindow:qtWindow
                          delegate:nil
                    didRunSelector:nil
                       contextInfo:nil];
    
    return YES;
}

@end

3.3 Linux CUPS打印引擎

Linux通过CUPS的IPP(Internet Printing Protocol)进行打印:

cpp 复制代码
// src/plugins/printsupport/cups/qcupsprinterengine.cpp
bool QCupsPrintEngine::begin(QPaintDevice *pdev)
{
    // 1. 连接到CUPS服务器
    cups_dest_t *dests;
    int numDests = cupsGetDests(&dests);
    
    // 2. 查找目标打印机
    cups_dest_t *dest = cupsGetDest(printerName, nullptr, numDests, dests);
    
    // 3. 创建打印任务
    int jobId = cupsPrintFile(dest->name, 
                              tempFilePath,  // PostScript或PDF临时文件
                              docName,
                              0, nullptr);
    
    // 4. 监控打印任务状态
    cups_job_t *jobs;
    int numJobs = cupsGetJobs(&jobs, dest->name, 1, -1);
    
    return jobId > 0;
}

四、高级打印技术与实战

4.1 高DPI打印支持

现代打印机支持600 DPI甚至1200 DPI输出,Qt提供了完整的高DPI打印支持:

cpp 复制代码
class HighDpiPrintWidget : public QWidget
{
protected:
    void paintEvent(QPaintEvent *event) override
    {
        QPainter painter(this);
        
        // 1. 设置视口和窗口的映射关系
        painter.setViewport(0, 0, width(), height());
        painter.setWindow(0, 0, 
            logicalDpiX() * width() / 72, 
            logicalDpiY() * height() / 72);
        
        // 2. 使用打印机实际DPI进行绘制
        QPrinter *printer = dynamic_cast<QPrinter*>(painter.device());
        if (printer) {
            int dpiX = printer->resolution();
            int dpiY = printer->resolution();
            
            // 3. 根据DPI缩放字体和图形
            QFont font = painter.font();
            font.setPointSizeF(font.pointSizeF() * dpiX / 72.0);
            painter.setFont(font);
            
            // 4. 绘制高分辨率图形
            drawHighDpiContent(painter, dpiX, dpiY);
        }
    }
    
    void drawHighDpiContent(QPainter &painter, int dpiX, int dpiY)
    {
        // 矢量图形:自动适应高DPI
        painter.setRenderHint(QPainter::Antialiasing, true);
        painter.setRenderHint(QPainter::TextAntialiasing, true);
        
        // 绘制1像素宽的线在高DPI下仍然清晰
        QPen pen(Qt::black);
        pen.setWidthF(1.0 / dpiX * 72.0);  // 转换为逻辑坐标
        painter.setPen(pen);
        
        painter.drawLine(QLineF(0, 0, 100, 100));
    }
};

4.2 多页打印与分页控制

实现复杂的多页打印需要精确的分页控制:

cpp 复制代码
class MultiPageReport : public QObject
{
    Q_OBJECT
public:
    void printReport(QPrinter *printer)
    {
        QPainter painter(printer);
        
        // 1. 计算可打印区域
        QRectF printableArea = printer->pageRect(QPrinter::Point);
        qreal margin = 36.0;  // 0.5英寸边距
        
        QRectF contentRect = printableArea.adjusted(margin, margin, 
                                                   -margin, -margin);
        
        // 2. 准备打印内容
        QList<ReportPage> pages = preparePages();
        
        // 3. 逐页打印
        for (int i = 0; i < pages.size(); ++i) {
            if (i > 0) {
                // 换页
                if (!printer->newPage()) {
                    qWarning() << "Failed to create new page";
                    break;
                }
                painter.begin(printer);  // 重新初始化painter
            }
            
            // 4. 绘制当前页
            drawPage(painter, pages[i], contentRect);
            
            // 5. 绘制页眉页脚
            drawHeaderFooter(painter, printableArea, i + 1, pages.size());
        }
    }
    
    void drawPage(QPainter &painter, const ReportPage &page, 
                  const QRectF &contentRect)
    {
        // 使用QTextDocument渲染富文本
        QTextDocument document;
        document.setPageSize(contentRect.size());
        document.setDocumentMargin(0);
        document.setHtml(page.htmlContent());
        
        // 绘制到打印机
        document.drawContents(&painter, contentRect.toRect());
    }
    
    void drawHeaderFooter(QPainter &painter, const QRectF &pageRect, 
                          int currentPage, int totalPages)
    {
        QFont oldFont = painter.font();
        QFont headerFont = oldFont;
        headerFont.setPointSize(8);
        painter.setFont(headerFont);
        
        // 页眉
        painter.drawText(pageRect.left(), pageRect.top() - 10,
                        "Qt Print Support Report");
        
        // 页脚(页码)
        QString pageStr = QString("Page %1 of %2")
                            .arg(currentPage).arg(totalPages);
        painter.drawText(pageRect.right() - 100, pageRect.bottom() + 20,
                        pageStr);
        
        painter.setFont(oldFont);
    }
};

4.3 打印预览实现原理

QPrintPreviewWidget通过虚拟打印到PDF实现预览:

cpp 复制代码
// src/printsupport/widgets/qprintpreviewwidget.cpp
void QPrintPreviewWidgetPrivate::generatePreview()
{
    // 1. 创建临时PDF引擎
    QPdfPrintEngine *previewEngine = new QPdfPrintEngine();
    previewEngine->setOutputFormat(QPrinter::PdfFormat);
    previewEngine->setOutputFileName(tempPdfPath);
    
    // 2. 渲染所有页面到PDF
    QPainter painter;
    for (int i = 0; i < totalPages; ++i) {
        if (i == 0) {
            painter.begin(previewEngine);
        } else {
            previewEngine->newPage();
        }
        
        // 调用用户的paint函数
        emit q->paintRequested(previewEngine);
    }
    painter.end();
    
    // 3. 将PDF转换为图像用于显示
    Poppler::Document *pdfDoc = Poppler::Document::load(tempPdfPath);
    for (int i = 0; i < pdfDoc->numPages(); ++i) {
        QImage pageImage = pdfDoc->page(i)->renderToImage(96, 96);
        previewPages.append(pageImage);
    }
    
    // 4. 在QGraphicsView中显示
    updatePreviewScene();
}

五、性能优化与最佳实践

5.1 图像打印优化

打印高分辨率图像时的内存和性能优化:

cpp 复制代码
void optimizeImagePrinting(QPrinter *printer, const QImage &image)
{
    QPainter painter(printer);
    
    // 1. 检查图像尺寸与打印机DPI的匹配度
    int printerDpi = printer->resolution();
    QSize imageSize = image.size();
    
    // 2. 如果图像分辨率低于打印机DPI,进行高质量缩放
    if (imageSize.width() < printer->width()) {
        QImage scaledImage = image.scaled(printer->width(), 
                                         printer->height(),
                                         Qt::KeepAspectRatio,
                                         Qt::SmoothTransformation);
        
        // 3. 使用QImageWriter进行优化的图像输出
        QByteArray imageData;
        QBuffer buffer(&imageData);
        buffer.open(QIODevice::WriteOnly);
        
        QImageWriter writer(&buffer, "JPEG");
        writer.setQuality(95);  // 高质量JPEG压缩
        writer.write(scaledImage);
        
        // 4. 直接绘制优化后的图像数据
        QImage optimizedImage = QImage::fromData(imageData);
        painter.drawImage(0, 0, optimizedImage);
    } else {
        // 图像分辨率足够,直接绘制
        painter.drawImage(0, 0, image);
    }
}

5.2 大型表格打印优化

打印包含成千上万行数据的表格时的优化策略:

cpp 复制代码
class LargeTablePrinter : public QObject
{
    Q_OBJECT
public:
    void printLargeTable(QPrinter *printer, const QSqlQuery &query)
    {
        QPainter painter(printer);
        painter.setRenderHint(QPainter::Antialiasing);
        
        // 1. 计算每页可打印的行数
        QFontMetrics fm = painter.fontMetrics();
        int lineHeight = fm.height() + 4;
        int availableHeight = printer->pageRect().height();
        int rowsPerPage = availableHeight / lineHeight - 5;  // 减去页眉页脚
        
        // 2. 分批获取数据并打印
        int currentRow = 0;
        int currentPage = 0;
        
        while (query.next()) {
            // 3. 检查是否需要换页
            if (currentRow % rowsPerPage == 0 && currentRow > 0) {
                printer->newPage();
                currentPage++;
                drawTableHeader(painter, printer);
            }
            
            // 4. 绘制当前行
            drawTableRow(painter, query, currentRow % rowsPerPage);
            
            currentRow++;
        }
        
        qDebug() << "Printed" << currentRow << "rows on" 
                 << currentPage + 1 << "pages";
    }
    
    void drawTableRow(QPainter &painter, const QSqlQuery &query, int row)
    {
        int y = headerHeight + row * lineHeight;
        
        // 使用QTextLayout进行高效的文本布局
        QTextLayout textLayout;
        textLayout.setFont(painter.font());
        
        // 只绘制可见列
        for (int col = 0; col < visibleColumns; ++col) {
            QString text = query.value(col).toString();
            int x = columnPositions[col];
            
            // 文本截断优化
            QRect textRect(x, y, columnWidths[col], lineHeight);
            QString elidedText = painter.fontMetrics().elidedText(
                text, Qt::ElideRight, columnWidths[col]);
            
            painter.drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, 
                           elidedText);
        }
    }
};

5.3 异步打印与进度反馈

对于耗时的打印任务,应该提供异步接口和进度反馈:

cpp 复制代码
class AsyncPrinter : public QObject
{
    Q_OBJECT
    
signals:
    void printProgress(int percentage);
    void printFinished(bool success);
    
public slots:
    void printAsync(QPrinter *printer, const ReportData &data)
    {
        // 使用Qt Concurrent进行异步打印
        QtConcurrent::run([this, printer, data]() {
            QTime startTime = QTime::currentTime();
            
            // 1. 准备打印
            QPainter painter(printer);
            int totalPages = calculateTotalPages(data);
            
            // 2. 逐页打印并报告进度
            for (int i = 0; i < totalPages; ++i) {
                if (i > 0) {
                    printer->newPage();
                }
                
                // 绘制页面
                drawPage(painter, data, i);
                
                // 计算并发送进度
                int progress = (i + 1) * 100 / totalPages;
                emit printProgress(progress);
                
                // 检查是否取消
                if (m_cancelRequested) {
                    emit printFinished(false);
                    return;
                }
            }
            
            painter.end();
            
            QTime endTime = QTime::currentTime();
            qDebug() << "Print completed in" 
                     << startTime.msecsTo(endTime) << "ms";
            
            emit printFinished(true);
        });
    }
    
    void cancelPrint()
    {
        m_cancelRequested = true;
    }
    
private:
    bool m_cancelRequested = false;
};

六、实战案例:发票打印系统

6.1 发票模板设计

cpp 复制代码
class InvoicePrinter : public QObject
{
    Q_OBJECT
public:
    bool printInvoice(QPrinter *printer, const Invoice &invoice)
    {
        QPainter painter(printer);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.setRenderHint(QPainter::TextAntialiasing);
        
        // 1. 设置发票专用字体
        QFont invoiceFont("SimSun", 10);  // 宋体,适合发票打印
        invoiceFont.setStyleStrategy(QFont::PreferDevice);
        painter.setFont(invoiceFont);
        
        // 2. 绘制发票头部
        drawInvoiceHeader(painter, invoice);
        
        // 3. 绘制发票主体(表格)
        drawInvoiceTable(painter, invoice);
        
        // 4. 绘制发票底部(合计、签名等)
        drawInvoiceFooter(painter, invoice);
        
        // 5. 绘制二维码/条形码
        drawInvoiceBarcode(painter, invoice);
        
        return true;
    }
    
    void drawInvoiceHeader(QPainter &painter, const Invoice &invoice)
    {
        // 发票标题
        QFont titleFont = painter.font();
        titleFont.setPointSize(16);
        titleFont.setBold(true);
        painter.setFont(titleFont);
        
        painter.drawText(100, 80, "增值税专用发票");
        
        // 发票代码和号码
        QFont codeFont = painter.font();
        codeFont.setPointSize(9);
        painter.setFont(codeFont);
        
        painter.drawText(500, 80, 
            QString("发票代码:%1").arg(invoice.code()));
        painter.drawText(500, 100, 
            QString("发票号码:%1").arg(invoice.number()));
        
        // 开票日期
        painter.drawText(100, 120, 
            QString("开票日期:%1").arg(invoice.date().toString("yyyy年MM月dd日")));
    }
    
    void drawInvoiceTable(QPainter &painter, const Invoice &invoice)
    {
        // 表格位置和尺寸
        int startX = 50;
        int startY = 150;
        int rowHeight = 30;
        
        // 绘制表格线
        painter.setPen(QPen(Qt::black, 1));
        
        // 表头
        QStringList headers = {"货物名称", "规格型号", "单位", "数量", "单价", "金额", "税率", "税额"};
        QList<int> columnWidths = {150, 80, 60, 60, 80, 100, 60, 80};
        
        int currentX = startX;
        for (int i = 0; i < headers.size(); ++i) {
            QRect headerRect(currentX, startY, columnWidths[i], rowHeight);
            painter.drawRect(headerRect);
            painter.drawText(headerRect, Qt::AlignCenter, headers[i]);
            currentX += columnWidths[i];
        }
        
        // 数据行
        int currentY = startY + rowHeight;
        for (const InvoiceItem &item : invoice.items()) {
            currentX = startX;
            
            QStringList rowData = {
                item.name(),
                item.specification(),
                item.unit(),
                QString::number(item.quantity()),
                QString::number(item.unitPrice(), 'f', 2),
                QString::number(item.amount(), 'f', 2),
                QString("%1%").arg(item.taxRate() * 100),
                QString::number(item.taxAmount(), 'f', 2)
            };
            
            for (int i = 0; i < rowData.size(); ++i) {
                QRect cellRect(currentX, currentY, columnWidths[i], rowHeight);
                painter.drawRect(cellRect);
                painter.drawText(cellRect, Qt::AlignCenter, rowData[i]);
                currentX += columnWidths[i];
            }
            
            currentY += rowHeight;
            
            // 检查是否需要换页
            if (currentY > painter.device()->height() - 100) {
                QPrinter *printer = dynamic_cast<QPrinter*>(painter.device());
                if (printer) {
                    printer->newPage();
                    currentY = 100;
                }
            }
        }
    }
};

七、常见问题与解决方案

7.1 打印模糊问题

问题:打印出来的文字或图形模糊不清。

原因

  1. 使用了ScreenResolution模式,导致DPI不匹配
  2. 图像分辨率低于打印机DPI
  3. 没有启用抗锯齿

解决方案

cpp 复制代码
void fixBlurryPrinting(QPrinter *printer)
{
    // 1. 使用高分辨率模式
    printer->setResolution(QPrinter::HighResolution);
    
    QPainter painter(printer);
    
    // 2. 启用抗锯齿
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setRenderHint(QPainter::TextAntialiasing, true);
    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
    
    // 3. 使用打印机实际DPI进行字体设置
    int dpi = printer->resolution();
    QFont font = painter.font();
    font.setPointSizeF(font.pointSizeF() * dpi / 72.0);
    painter.setFont(font);
    
    // 4. 图像打印前进行高质量缩放
    QImage image("input.png");
    if (image.width() < printer->width()) {
        image = image.scaled(printer->width(), printer->height(),
                           Qt::KeepAspectRatio, Qt::SmoothTransformation);
    }
    
    painter.drawImage(0, 0, image);
}

7.2 打印速度慢问题

问题:打印复杂文档时速度非常慢。

原因

  1. 每一页都重新查询数据库
  2. 没有使用双缓冲
  3. 图像没有进行压缩优化

解决方案

cpp 复制代码
void optimizePrintSpeed(QPrinter *printer)
{
    // 1. 预加载所有数据
    QList<PageData> allPages = preloadAllData();
    
    // 2. 使用双缓冲
    QPixmap buffer(printer->width(), printer->height());
    
    QPainter painter;
    for (int i = 0; i < allPages.size(); ++i) {
        buffer.fill(Qt::white);
        
        QPainter bufferPainter(&buffer);
        drawPage(bufferPainter, allPages[i]);
        bufferPainter.end();
        
        if (i == 0) {
            painter.begin(printer);
        } else {
            printer->newPage();
        }
        
        painter.drawPixmap(0, 0, buffer);
    }
    painter.end();
    
    // 3. 图像压缩
    QImage image = getLargeImage();
    QByteArray compressedData;
    QBuffer buffer2(&compressedData);
    buffer2.open(QIODevice::WriteOnly);
    image.save(&buffer2, "JPEG", 85);  // 85%质量,减小文件大小
}

八、总结

Qt Print Support框架通过精巧的分层架构,在异构操作系统打印子系统之上构建了统一的打印API。其核心设计理念包括:

  1. 平台抽象 :通过QPlatformPrinterSupport实现跨平台打印
  2. 引擎架构QPaintEngine的打印专用实现(PDF/GDI/CUPS)
  3. 设备无关QPaintDevice接口让打印和绘图使用同一套API
  4. 高DPI支持:完整的分辨率管理和缩放策略

在实际开发中,应该根据需求选择合适的打印模式(PDF输出 vs 原生打印),并注意性能优化(异步打印、图像压缩、分批处理)。

随着无纸化办公的普及,PDF输出(Qt PDF模块)正逐渐成为主流,但掌握原生打印API仍然是Qt开发者的必备技能。


注:若有发现问题欢迎大家提出来纠正

相关推荐
性野喜悲1 小时前
python将excel中的链接转成图片并替换链接展示在excel中【将pdf的第一页插入excel并将对应信息获取到插入签名等位置】
开发语言·python·excel
诙_1 小时前
C++代码实践应用
开发语言·c++
谙弆悕博士1 小时前
【附C语言源码】从零实现命令行贪吃蛇游戏
c语言·开发语言·学习·游戏·游戏程序·小游戏·贪吃蛇
Evand J1 小时前
【无人机编队控制程序4】复杂障碍环境下多无人机编队避障(人工势场法APF)与协同控制,MATLAB仿真例程
开发语言·matlab·无人机·控制·apf·避障
南宫萧幕2 小时前
基于 MATLAB 的插电混动汽车 CD-CS 策略 WLTC 前向仿真实现
开发语言·matlab·汽车
代钦塔拉2 小时前
第一篇:工业级 C++/Qt 项目头文件包含原则:告别循环依赖与编译玄学
开发语言·c++·qt
谷雨不太卷2 小时前
Linux基础IO
java·开发语言
神仙别闹3 小时前
基于PHP+MySQL实现在线考试系统
开发语言·mysql·php