1. 程式人生 > >基於mupdf的PDF閱讀器

基於mupdf的PDF閱讀器

上一篇講了基於poppler庫的PDF閱讀器的開發,這一篇來講基於mupdf的PDF閱讀器的開發。

在上一篇的篇尾我有附效果圖,可以很明顯的看出來這個效果是非常差的,網路上有童鞋同樣遇到了這個問題,並且做了修正,但是,經過測試完全沒用(也可能是版本的問題),所以我不得不做新的嘗試。

  • 基於mupdf的PDF閱讀器

同樣的,先要去下載原始碼,進行解壓編譯。mupdf庫比較好的一點是可以很簡單地配置,從而簡化很多不需要的庫,比如我僅僅是需要提取每一個page,然後放大縮小,上一頁、下一頁這種操作,而且是在Qt下做的開發,所以很多庫就不要了,我的配置如下:

make HAVE_X11=no HAVE_GLUT=no  prefix=./out install

三十秒不到的時間就編譯完成了,然後在Qt專案中配置庫和標頭檔案就可以進行開發了。在安裝目錄中有一個例子,可以做參考的,可能因為版本問題,例子沒有編過,然而這不是很大的問題,稍作修改就可以了。

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QLabel>
#include <QMouseEvent>
#include <QScrollArea>
#include "mupdf/fitz.h"
#include "mupdf/pdf.h"

#include <QPointF>

#define Max_abs 20

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();

    void mouseMoveEvent(QMouseEvent *event);
    void touchEvent(QTouchEvent *event);
    void mousePressEvent(QMouseEvent *event);
private:
    float bestFitScale(int pageW, int pageH);
    void updatePage();
    void fz_print_outline(fz_context *ctx, fz_output *out, fz_outline *outline, int level);

private:
    QLabel* label;
    float scale;
    int page_number;
    int page_count;
    float zoom, rotate;

    //
    fz_context *ctx;
    fz_document *doc;
    fz_pixmap *pix;

    QPointF Press_point;

public slots:
    void onPrev();
    void onNext();

    void onZoomIn();
    void onZoomOut();
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include <QDebug>
#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{

    page_number=0;//第一頁為0

    //100%縮放比
    zoom=1;
    //旋轉為0
    rotate=0;

    //建立上下文
    ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
    if (!ctx)
    {
        qDebug()<<stderr<<"cannot create mupdf context";
        return;
    }

    //註冊文件控制
    fz_try(ctx)
            fz_register_document_handlers(ctx);
    fz_catch(ctx)
    {
        qDebug()<<stderr<<"cannot register document handlers:"<< fz_caught_message(ctx);
        fz_drop_context(ctx);
        return;
    }

    //開啟文件
    fz_try(ctx)
        doc = fz_open_document(ctx, "Boost程式庫完全開發指南深入C++準標準庫.pdf");
    fz_catch(ctx)
    {
        qDebug()<<stderr<< "cannot open document:"<< fz_caught_message(ctx);
        fz_drop_context(ctx);
        return;
    }

    //取得總的頁數
    fz_try(ctx)
        page_count = fz_count_pages(ctx, doc);
    fz_catch(ctx)
    {
        qDebug()<<stderr<< "cannot count number of pages:"<< fz_caught_message(ctx);
        fz_drop_document(ctx, doc);
        fz_drop_context(ctx);
        return;
    }
    qDebug() << "page_count: "<< page_count;

    //get outline
    fz_outline *outline = fz_load_outline(ctx, (fz_document*)doc);
    fz_try(ctx)
        fz_print_outline(ctx, fz_stderr(ctx), outline, 0);
    fz_always(ctx)
        fz_drop_outline(ctx, outline);
    fz_catch(ctx)
        fz_rethrow(ctx);


    QScrollArea *LogoArea = new QScrollArea(this);
    //LogoArea->setGeometry(0, 0, width(), height());
    LogoArea->setGeometry(0, 0, 1024, 600);
    LogoArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    LogoArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    //QWidget *w = new QWidget(LogoArea);
    //LogoArea->setWidget(w);
    //QScrollBar* vertScrollBar = LogoArea->verticalScrollBar();


    label=new QLabel(this);

    label->setVisible(false);
    LogoArea->setWidget(label);
    LogoArea->setAlignment(Qt::AlignCenter);
    QPushButton* prevBtn = new QPushButton(this);
    prevBtn->setText("Prev");
    prevBtn->setGeometry(910, 240, 60,100);
    connect(prevBtn, SIGNAL(pressed()), this, SLOT(onPrev()));


    QPushButton* nextBtn = new QPushButton(this);
    nextBtn->setText("Next");
    nextBtn->setGeometry(910, 340, 60,100);
    connect( nextBtn, SIGNAL(pressed()), this, SLOT(onNext()));


    QPushButton* plusBtn = new QPushButton(this);
    plusBtn->setText("+");
    plusBtn->setGeometry(910, 50, 20, 20);
    connect( plusBtn, SIGNAL(pressed()), this, SLOT(onZoomIn()));
    QPushButton* minBtn = new QPushButton(this);
    minBtn->setText("-");
    minBtn->setGeometry(940, 50, 20, 20);
    connect( minBtn, SIGNAL(pressed()), this, SLOT(onZoomOut()));


    if (page_number < 0 || page_number >= page_count)
    {
        qDebug()<<stderr<< "page number out of range: "<< page_number + 1<<"page count:"<<page_count;
        fz_drop_document(ctx, doc);
        fz_drop_context(ctx);
        return;
    }

    //計算縮放以及旋轉



    updatePage();
}

Widget::~Widget()
{

}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    qDebug() << event;
    int xMove;
    int yMove;

    xMove = Press_point.x() - event->localPos().x();
    yMove = Press_point.y() - event->localPos().y();


    event->accept();
}

void Widget::mousePressEvent(QMouseEvent *event)
{
    Press_point = event->localPos();
}

void Widget::touchEvent(QTouchEvent *event)
{
    qDebug() << event;
}

float Widget::bestFitScale(int pageW, int pageH)
{
    return ((width() / pageW) > (height() / pageH) ? (height() / pageH) : (width() / pageW));
}

void Widget::updatePage()
{
    fz_matrix ctm = fz_scale( zoom, zoom);
    fz_pre_rotate(ctm, rotate);
    fz_try(ctx)
        pix = fz_new_pixmap_from_page_number(ctx, doc, page_number, ctm, fz_device_rgb(ctx), 0);
    fz_catch(ctx)
    {
        qDebug()<<stderr<< "cannot render page: %s\n"<< fz_caught_message(ctx);
        fz_drop_document(ctx, doc);
        fz_drop_context(ctx);
        return;
    }

    unsigned char *samples = pix->samples;
    int width = fz_pixmap_width(ctx, pix);
    int height = fz_pixmap_height(ctx, pix);

    qDebug() << "width: "<<width;
    qDebug() << "height: "<<height;
    label->setGeometry(0,0,width,height);
    label->setAlignment(Qt::AlignCenter);

    QImage image(samples, width, height,pix->stride,QImage::Format_RGB888);
    label->setPixmap(QPixmap::fromImage(image));
    label->setVisible(true);

    fz_drop_pixmap(ctx, pix);
    update();
}


void Widget::onPrev()
{
    qDebug() << "onPrev";
    page_number--;
    if(page_number <= 0)
        page_number = 0;
    updatePage();
}

void Widget::onNext()
{
    qDebug() << "onNext";
    page_number++;
    if(page_number >= page_count -1)
        page_number = page_count -1;
    updatePage();
}

void Widget::onZoomIn()
{
    zoom += 0.05;
    updatePage();
}

void Widget::onZoomOut()
{
    zoom -= 0.05;
    updatePage();
}


//Tools
void Widget::fz_print_outline(fz_context *ctx, fz_output *out, fz_outline *outline, int level)
{
    int i;
    if(outline == NULL)
        qDebug()<< "outline is NULL!";

    while (outline)
    {
        for (i = 0; i < level; i++)
        //    fz_write_printf(ctx, out, "\t");
        //fz_write_printf(ctx, out, "%s\t%s\n", outline->title, outline->uri);
        qDebug()<< outline->title<<" " << outline->uri;
        if (outline->down)
            fz_print_outline(ctx, out, outline->down, level + 1);
        outline = outline->next;
    }
}

效果圖

這個顯示的效果是相當令人滿意的。之前有網友說poppler不能滿足效能,確實如此。