当前位置: 首页 > article >正文

【学写LibreCAD】 2.1 pdf_print_loop文件

pdf_print_loop.h和pdf_print_loop.cpp文件是 LibreCAD 项目中用于将 DXF 文件打印为 PDF 文件的核心模块。它通过 Qt 的 QPrinter 类实现了 PDF 文件的生成,并结合 LibreCAD 的图形处理功能,能够处理单页和多页打印任务。

头文件(pdf_print_loop.h)

头文件包含结构体定义

struct PdfPrintParams {
        QStringList dxfFiles;
        QString outDir;
        QString outFile;
        int resolution = 1200;
        bool centerOnPage=false;
        bool fitToPage=false;
        bool monochrome=false;
        bool grayscale=false;
        double scale = 0.0;  // If scale <= 0.0, use value from dxf file.
        RS_Vector pageSize;  // If zeros, use value from dxf file.
        struct {
            double left = -1.0;
            double top = -1.0;
            double right = -1.0;
            double bottom = -1.0;
        } margins;           // If margin < 0.0, use value from dxf file.
        int pagesH = 0;      // If number of pages < 1,
        int pagesV = 0;      // use value from dxf file.
};

等价rust代码为:

#[derive(Debug)]
pub struct PdfPrintParams {
    pub dxf_files: Vec<String>, // 对应 QStringList dxfFiles
    pub out_dir: String,        // 对应 QString outDir
    pub out_file: String,       // 对应 QString outFile
    pub resolution: i32,        // 对应 int resolution
    pub center_on_page: bool,   // 对应 bool centerOnPage
    pub fit_to_page: bool,      // 对应 bool fitToPage
    pub monochrome: bool,       // 对应 bool monochrome
    pub grayscale: bool,        // 对应 bool grayscale
    pub scale: f64,             // 对应 double scale
    pub page_size: (Mm, Mm),    // 对应 RS_Vector pageSize
    pub margins: (Mm, Mm, Mm, Mm), // 边距 (左, 上, 右, 下),对应 margins 结构体
    pub pages_h: i32,           // 对应 int pagesH
    pub pages_v: i32,           // 对应 int pagesV
}

impl Default for PdfPrintParams {
    fn default() -> Self {
        PdfPrintParams {
            dxf_files: Vec::new(), // 默认值为空列表
            out_dir: String::new(), // 默认值为空字符串
            out_file: String::new(), // 默认值为空字符串
            resolution: 1200, // 默认值为 1200
            center_on_page: false, // 默认值为 false
            fit_to_page: false, // 默认值为 false
            monochrome: false, // 默认值为 false
            grayscale: false, // 默认值为 false
            scale: 0.0, // 默认值为 0.0
            page_size: (Mm(0.0), Mm(0.0)), // 默认值为 (0.0, 0.0)
            margins: (Mm(0.0), Mm(0.0), Mm(0.0), Mm(0.0)), // 默认值为 (0.0, 0.0, 0.0, 0.0)
            pages_h: 0, // 默认值为 0
            pages_v: 0, // 默认值为 0
        }
    }
}

程序文件(pdf_print_loop.cpp)分析

源码
#include <QtCore>

#include "rs.h"
#include "rs_graphic.h"
#include "rs_painter.h"
#include "lc_printing.h"
#include "rs_staticgraphicview.h"
#include "rs_units.h"

#include "pdf_print_loop.h"


static bool openDocAndSetGraphic(RS_Document**, RS_Graphic**, const QString&);
static void touchGraphic(RS_Graphic*, PdfPrintParams&);
static void setupPrinterAndPaper(RS_Graphic*, QPrinter&, PdfPrintParams&);
static void drawPage(RS_Graphic*, QPrinter&, RS_Painter&);

void PdfPrintLoop::run()
{
    if (params.outFile.isEmpty()) {
        for (auto &&f : params.dxfFiles) {
            printOneDxfToOnePdf(f);
        }
    } else {
        printManyDxfToOnePdf();
    }

    emit finished();
}


void PdfPrintLoop::printOneDxfToOnePdf(const QString& dxfFile) {

    // Main code logic and flow for this method is originally stolen from
    // QC_ApplicationWindow::slotFilePrint(bool printPDF) method.
    // But finally it was split in to smaller parts.

    QFileInfo dxfFileInfo(dxfFile);
    params.outFile =
        (params.outDir.isEmpty() ? dxfFileInfo.path() : params.outDir)
        + "/" + dxfFileInfo.completeBaseName() + ".pdf";

    RS_Document *doc;
    RS_Graphic *graphic;

    if (!openDocAndSetGraphic(&doc, &graphic, dxfFile))
        return;

    qDebug() << "Printing" << dxfFile << "to" << params.outFile << ">>>>";

    touchGraphic(graphic, params);

    QPrinter printer(QPrinter::HighResolution);

    setupPrinterAndPaper(graphic, printer, params);

    RS_Painter painter(&printer);

    if (params.monochrome)
        painter.setDrawingMode(RS2::ModeBW);

    drawPage(graphic, printer, painter);

    painter.end();

    qDebug() << "Printing" << dxfFile << "to" << params.outFile << "DONE";

    delete doc;
}


void PdfPrintLoop::printManyDxfToOnePdf() {

    struct DxfPage {
        RS_Document* doc;
        RS_Graphic* graphic;
        QString dxfFile;
        QPageSize::PageSizeId paperSize;
    };

    if (!params.outDir.isEmpty()) {
        QFileInfo outFileInfo(params.outFile);
        params.outFile = params.outDir + "/" + outFileInfo.fileName();
    }

    QVector<DxfPage> pages;
    int nrPages = 0;

    // FIXME: Should probably open and print all dxf files in one 'for' loop.
    // Tried but failed to do this. It looks like some 'chicken and egg'
    // situation for the QPrinter and RS_PainterQt. Therefore, first open
    // all dxf files and apply required actions. Then run another 'for'
    // loop for actual printing.
    for (auto dxfFile : params.dxfFiles) {

        DxfPage page;

        page.dxfFile = dxfFile;

        if (!openDocAndSetGraphic(&page.doc, &page.graphic, dxfFile))
            continue;

        qDebug() << "Opened" << dxfFile;

        touchGraphic(page.graphic, params);

        pages.append(page);

        nrPages++;
    }

    QPrinter printer(QPrinter::HighResolution);

    if (nrPages > 0) {
        // FIXME: Is it possible to set up printer and paper for every
        // opened dxf file and tie them with painter? For now just using
        // data extracted from the first opened dxf file for all pages.
        setupPrinterAndPaper(pages.at(0).graphic, printer, params);
    }

    RS_Painter painter(&printer);

    if (params.monochrome)
        painter.setDrawingMode(RS2::ModeBW);

    // And now it's time to actually print all previously opened dxf files.
    for (auto page : pages) {
        nrPages--;

        qDebug() << "Printing" << page.dxfFile
                 << "to" << params.outFile << ">>>>";

        drawPage(page.graphic, printer, painter);

        qDebug() << "Printing" << page.dxfFile
                 << "to" << params.outFile << "DONE";

        delete page.doc;

        if (nrPages > 0)
            printer.newPage();
    }

    painter.end();
}


static bool openDocAndSetGraphic(RS_Document** doc, RS_Graphic** graphic,
    const QString& dxfFile)
{
    *doc = new RS_Graphic();

    if (!(*doc)->open(dxfFile, RS2::FormatUnknown)) {
        qDebug() << "ERROR: Failed to open document" << dxfFile;
        delete *doc;
        return false;
    }

    *graphic = (*doc)->getGraphic();
    if (*graphic == nullptr) {
        qDebug() << "ERROR: No graphic in" << dxfFile;
        delete *doc;
        return false;
    }

    return true;
}


static void touchGraphic(RS_Graphic* graphic, PdfPrintParams& params)
{
    graphic->calculateBorders();
    graphic->setMargins(params.margins.left, params.margins.top,
                        params.margins.right, params.margins.bottom);
    graphic->setPagesNum(params.pagesH, params.pagesV);

    if (params.scale > 0.0)
        graphic->setPaperScale(params.scale);

    if (params.pageSize != RS_Vector(0.0, 0.0))
        graphic->setPaperSize(params.pageSize);

    if (params.fitToPage)
        graphic->fitToPage(); // fit and center
    else if (params.centerOnPage)
        graphic->centerToPage();
}


static void setupPrinterAndPaper(RS_Graphic* graphic, QPrinter& printer,
    PdfPrintParams& params)
{
    bool landscape = false;

    RS2::PaperFormat pf = graphic->getPaperFormat(&landscape);
    QPageSize::PageSizeId paperSize = LC_Printing::rsToQtPaperFormat(pf);

    if (paperSize == QPageSize::Custom){
        RS_Vector r = graphic->getPaperSize();
        RS_Vector s = RS_Units::convert(r, graphic->getUnit(),
            RS2::Millimeter);
        if (landscape)
            s = s.flipXY();
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
        printer.setPageSize(QPageSize{QSizeF{s.x,s.y}, QPageSize::Millimeter});
#else
        printer.setPaperSize(QSizeF{s.x,s.y}, QPrinter::Millimeter);
#endif
    } else {
        printer.setPageSize(paperSize);
    }

#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
    printer.setPageOrientation(landscape ? QPageLayout::Landscape : QPageLayout::Portrait);
#else
    printer.setOrientation(landscape ? QPrinter::Landscape : QPrinter::Portrait);
#endif

    printer.setOutputFileName(params.outFile);
    printer.setOutputFormat(QPrinter::PdfFormat);
    printer.setResolution(params.resolution);
    printer.setFullPage(true);

    if (params.grayscale)
        printer.setColorMode(QPrinter::GrayScale);
    else
        printer.setColorMode(QPrinter::Color);
}


static void drawPage(RS_Graphic* graphic, QPrinter& printer,
    RS_Painter& painter)
{
    double printerFx = (double)printer.width() / printer.widthMM();
    double printerFy = (double)printer.height() / printer.heightMM();

    double marginLeft = graphic->getMarginLeft();
    double marginTop = graphic-> getMarginTop();
    double marginRight = graphic->getMarginRight();
    double marginBottom = graphic->getMarginBottom();

    painter.setClipRect(marginLeft * printerFx, marginTop * printerFy,
                        printer.width() - (marginLeft + marginRight) * printerFx,
                        printer.height() - (marginTop + marginBottom) * printerFy);

    RS_StaticGraphicView gv(printer.width(), printer.height(), &painter);
    gv.setPrinting(true);
    gv.setBorders(0,0,0,0);

    gv.updateSettings(graphic);

    double fx = printerFx * RS_Units::getFactorToMM(graphic->getUnit());
    double fy = printerFy * RS_Units::getFactorToMM(graphic->getUnit());

    double f = (fx + fy) / 2.0;

    double scale = graphic->getPaperScale();

    gv.setOffset((int)(graphic->getPaperInsertionBase().x * f),
                 (int)(graphic->getPaperInsertionBase().y * f));
    gv.setFactor(f*scale);
    gv.setContainer(graphic);

    double baseX = graphic->getPaperInsertionBase().x;
    double baseY = graphic->getPaperInsertionBase().y;
    int numX = graphic->getPagesNumHoriz();
    int numY = graphic->getPagesNumVert();
    RS_Vector printArea = graphic->getPrintAreaSize(false);

    for (int pY = 0; pY < numY; pY++) {
        double offsetY = printArea.y * pY;
        for (int pX = 0; pX < numX; pX++) {
            double offsetX = printArea.x * pX;
            // First page is created automatically.
            // Extra pages must be created manually.
            if (pX > 0 || pY > 0) printer.newPage();
            gv.setOffset((int)((baseX - offsetX) * f),
                         (int)((baseY - offsetY) * f));
            gv.drawEntity(&painter, graphic );
        }
    }
}

源码分析
  1. 文件头信息
  • 代码中包含了多个 LibreCAD 的核心头文件,如 rs.h、rs_graphic.h 等,这些文件定义了与图形处理相关的类和函数。
  1. PdfPrintLoop 类
  • PdfPrintLoop 类负责处理 DXF 文件的打印任务。它有两个主要的公共方法:

    • printOneDxfToOnePdf:将一个 DXF 文件打印为一个 PDF 文件。

    • printManyDxfToOnePdf:将多个 DXF 文件打印到一个 PDF 文件中,每个 DXF 文件对应 PDF 文件中的一页。

  1. printOneDxfToOnePdf 方法
  • 该方法将一个 DXF 文件打印为一个 PDF 文件。主要步骤如下:

    1. 设置输出文件路径:根据输入的 DXF 文件路径和输出目录,生成 PDF 文件的输出路径。

    2. 打开 DXF 文件并获取图形对象:通过 openDocAndSetGraphic 函数打开 DXF 文件,并获取其中的图形对象 RS_Graphic。

    3. 调整图形设置:调用 touchGraphic 函数,根据打印参数(如边距、页面数量、缩放比例等)调整图形对象的设置。

    4. 设置打印机和纸张:通过 setupPrinterAndPaper 函数配置 QPrinter 对象,设置纸张大小、方向、分辨率等。

    5. 绘制页面:使用 RS_Painter 对象将图形内容绘制到 PDF 文件中。

    6. 清理资源:打印完成后,删除文档对象以释放资源。

  1. printManyDxfToOnePdf 方法
  • 该方法将多个 DXF 文件打印到一个 PDF 文件中,每个 DXF 文件对应 PDF 文件中的一页。主要步骤如下:

    1. 设置输出文件路径:根据输入的 DXF 文件列表和输出目录,生成 PDF 文件的输出路径。

    2. 打开所有 DXF 文件并获取图形对象:通过 openDocAndSetGraphic 函数打开每个 DXF 文件,并获取其中的图形对象 RS_Graphic。

    3. 调整图形设置:对每个图形对象调用 touchGraphic 函数,根据打印参数调整图形设置。

    4. 设置打印机和纸张:使用第一个 DXF 文件的图形对象来配置 QPrinter 对象,设置纸张大小、方向、分辨率等。

    5. 绘制所有页面:使用 RS_Painter 对象将每个 DXF 文件的图形内容绘制到 PDF 文件中,每个 DXF 文件对应一页。

    6. 清理资源:打印完成后,删除所有文档对象以释放资源。

  1. 辅助函数
  • openDocAndSetGraphic:打开 DXF 文件并获取图形对象。如果文件打开失败或文件中没有图形对象,则返回 false。

  • touchGraphic:根据打印参数调整图形对象的设置,如边距、页面数量、缩放比例等。

  • setupPrinterAndPaper:根据图形对象的纸张大小和方向配置 QPrinter 对象。

  • drawPage:将图形对象的内容绘制到 PDF 文件中,处理多页打印的情况。

  1. Qt 和 LibreCAD 的集成
  • 代码中使用了 Qt 的 QPrinter 类来处理 PDF 文件的生成,QPrinter 提供了丰富的打印功能,如设置纸张大小、方向、分辨率等。

  • RS_Painter 是 LibreCAD 中的一个绘图类,负责将图形内容绘制到 QPrinter 上。

  • RS_Graphic 是 LibreCAD 中的图形对象,包含了 DXF 文件中的所有图形数据。

  1. 多页打印处理
    在 drawPage 函数中,处理了多页打印的情况。如果图形对象设置了多个页面(水平和垂直方向),则会自动生成多个 PDF 页面,并将图形内容分别绘制到每个页面上。

  2. 调试信息
    代码中使用了 qDebug() 输出调试信息,方便开发者跟踪打印过程中的各个步骤。

  3. Qt 版本兼容性
    代码中考虑了不同 Qt 版本的兼容性,特别是在设置纸张大小和方向时,使用了条件编译来处理不同版本的 Qt API。

等价rust代码

Rust 中没有直接对应的 Qt 库,但可以使用 printpdf库来生成 PDF 文件,dxf 库解析 DXF 文件。绘制图形的工作初步考虑lyon。
本文件调用RS_Document等类,我们先实现底层内容,该文件等价的rust代码在后期完善。


http://www.kler.cn/a/582846.html

相关文章:

  • Spring的基础事务注解@Transactional
  • C++零基础LeetCode热题100- 128.最长连续序列
  • 大模型微调|使用 LLaMA-Factory 微调 Llama3-8B-Chinese-Chat 完成知识问答任务
  • [高阶技术了解]WebRPC详解
  • Linux错误(5)程序fork子进程后访问内存触发缺页中断(COW)
  • 四、子串——11. 滑动窗口最大值
  • 交换机、路由器、网关、MAC地址——从入门到实战
  • 计算机网络:计算机网络的分类
  • 《HTML + CSS + JS 打造炫酷轮播图详解》
  • Cursor初体验:excel转成CANoe的vsysvar文件
  • 批量删除 Excel 表格的页眉页脚
  • 读书笔记 - Spring Boot实战
  • 大语言模型对软件工程师的影响曲线
  • Lock接口与synchronized锁机制
  • LCD显示翻转
  • 每日OJ_牛客_过桥_贪心+BFS_C++_Java
  • Next.js介绍(React框架)
  • 2025年第十届数维杯大学生数学建模挑战赛参赛规则
  • 卷积神经网络(笔记02)
  • 总结数据链路层相关知识