📄 pdfwidget.cpp
字号:
#include <qpainter.h>#include <qpixmap.h>#include <qimage.h>#include <qwidget.h>#include <qscrollview.h>#include <qlabel.h>#include <qpoint.h>#include <qarray.h>#include <qtimer.h>#include <qthread.h>#include <qapplication.h>#include <stdlib.h>#include <unistd.h>#include <sys/time.h>#include <qgfx_qws.h>#include "pdfwidget.h"#define MAX_SCROLL_DELAY 8static Poppler::Page *page = 0;static QWidget *eventReceiver;static bool abortCheck(void*);// must be accessable by the pdfWidget so it can abort the poppler librarystatic volatile bool abortedFastMove = false;static struct timeval preview_time;static bool running = false;struct renderJob{ Poppler::Document *doc; int pageNum; int scale;};class RenderThread : public QThread { public : RenderThread(QObject* m) : QThread() , master(m) , currentJobPending(false) , jobBufferPending(false) {} virtual void run(void) { QImage *q = 0; while (currentJobPending || jobBufferPending) { lock.lock(); if (jobBufferPending) { qWarning("picking up job from Buffer"); delete q; currentJob = jobBuffer; jobBufferPending = false; abortedFastMove = false; } lock.unlock(); delete page; page = 0; page = currentJob.doc->getPage(currentJob.pageNum); qWarning("starting to work on page %i, scale %i", currentJob.pageNum, currentJob.scale); gettimeofday(&preview_time, NULL); page->renderToImageScaled(&q, currentJob.scale, currentJob.scale, &abortCheck, 0); qWarning("finished Page"); currentJobPending = false; } QApplication::postEvent(master, new QCustomEvent((QEvent::Type)PageReady, q)); } void setJob(renderJob& j) { lock.lock(); if (!running()) { qWarning("starting renderThread"); currentJob = j; currentJobPending = true; abortedFastMove = false; start(); } else { qWarning("adding job for running thread"); jobBuffer = j; jobBufferPending = true; abortedFastMove = true; } lock.unlock(); } private: QObject* master; QMutex lock; renderJob currentJob; bool currentJobPending; renderJob jobBuffer; bool jobBufferPending;};bool preview_time_elapsed(struct timeval *preview, struct timeval *now){ // previews every 3/4 seconds return ((now->tv_sec * 1000000 + now->tv_usec) - (preview->tv_sec * 1000000 + preview->tv_usec)) >= 7500000;}static bool abortCheck(void*){ bool ret = false; struct timeval now; if (abortedFastMove) { qWarning("abortedFastMove"); ret = true; abortedFastMove = false; } else { gettimeofday(&now, NULL); if (preview_time_elapsed(&preview_time, &now)) { if (page) { qWarning("sending preview event"); QImage* image; page->getPagePreview(&image); QApplication::postEvent(eventReceiver, new QCustomEvent((QEvent::Type)PagePreview, image)); gettimeofday(&preview_time, NULL); } } } return ret;}PDFWidget::PDFWidget(Poppler::Document *d, QWidget *parent, const char* name, bool helpViewer ) : QScrollView(parent, name, WRepaintNoErase ), m_scale(72), currentPage(0), pixmap(0), doc(d), page(0), m_panning(false), m_panningPos(0), zoomLevel(FIT_TO_WIDTH), scheduledContentsPosX(0), scheduledContentsPosY(0), selectedLink(-1), m_linkSelected(false), m_LinkPos(0), x_origin(0), y_origin(0), direction(Forward){ renderThread = new RenderThread(this); if (helpViewer) { borderWidth = 0; // the help viewer doesn't use a border } else { borderWidth = 5; } setHScrollBarMode(AlwaysOff); setVScrollBarMode(AlwaysOff); setFrameStyle(NoFrame); eventReceiver = this; display();}PDFWidget::~PDFWidget() { /** @todo: should we pthread_join() the renderer thread? */ delete pixmap; delete renderThread;}void PDFWidget::setDocument( Poppler::Document *d, int scale) { doc = d; currentPage = 0; delete page; page = doc->getPage(currentPage); delete pixmap; pixmap = 0; emitNavSignals(); zoomLevel = FIT_TO_WIDTH; setZoom(scale);}void PDFWidget::drawContents( QPainter *painter, int, int, int, int){ int sv_width = viewport()->width(); int sv_height = viewport()->height(); if (pixmap) { x_origin = borderWidth; y_origin = borderWidth; // center horizontaly if (pixmap->width() <= sv_width) { x_origin = (sv_width - pixmap->width()) / 2; } // center verticaly if (pixmap->height() <= sv_height) { y_origin = (sv_height - pixmap->height()) / 2; } painter->drawPixmap(x_origin, y_origin, *pixmap); // paint the visible content area not used by the pixmap // structured so we avoid painting overlapping areas twice painter->fillRect(0, 0, x_origin, contentsHeight(), QColor(123, 121, 123)); // right of the pixmap painter->fillRect(x_origin + pixmap->width(), 0, contentsWidth(), contentsHeight(), QColor(123, 121, 123)); // over the pixmap painter->fillRect(x_origin, 0, x_origin + pixmap->width(), y_origin, QColor(123, 121, 123)); // under the pixmap painter->fillRect(x_origin, y_origin + pixmap->height(), x_origin + pixmap->width(), contentsHeight(), QColor(123, 121, 123)); // when not in helpViewer mode there is a border and we want a frame if (borderWidth) { int pageW, pageH; if (pixmap->width() == sv_width) { pageW = pixmap->width(); } else if (pixmap->width() < sv_width) { pageW = pixmap->width() + 1; } else { pageW = contentsWidth() - x_origin - (borderWidth - 1); } if (pixmap->height() == sv_height) { pageH = pixmap->height(); } else if (pixmap->height() < sv_height) { pageH = pixmap->height() + 1; } else { pageH = contentsHeight() - y_origin - (borderWidth - 1); } painter->drawRect(x_origin, y_origin, pageW, pageH); } } else { qWarning("No valid pixmap to display"); painter->fillRect(0, 0, sv_width, sv_height, QColor(123, 121, 123)); }}int PDFWidget::fitToPageDpi() const{ float pWidth = getPageWidth(); float pHeight = getPageHeight(); //qWarning("getPageWidth(); = %f", pWidth); //qWarning("getPageHeight() + borderWidth * 2 = %f", pHeight); // this will only work as long the pdfdWidget covers the whole screen int dw = qt_screen->width(); int dh = qt_screen->height(); //qWarning("------fitToPageDpi %i",(int)(QMIN((dw * 72.0) / pWidth, (dh * 72.0) / pHeight))); return (int)(QMIN((dw * 72.0) / pWidth, (dh * 72.0) / pHeight));}int PDFWidget::fitToWidthDpi() const{ int dw = qt_screen->width(); //qWarning("----------------fitToWidthDpi %i", (int)((dw * 72.0) / getPageWidth())); // this will only work as long the pdfdWidged covers the whole screen return (int)((dw * 72.0) / getPageWidth());}int PDFWidget::fitToPagePercent() const{ //qWarning("------------------fitToPagePercent %i", (int)(fitToPageDpi() / (72.0 / 100))); return (int)(fitToPageDpi() / (72.0 / 100));}int PDFWidget::dpiToPercent(int scale) const{ //qWarning("-----------------dpiToPercent %i", (int)(m_scale / (72.0 / 100))); return (int)(scale / (72.0 / 100));}void PDFWidget::zoomOut(){ if (zoomLevel == FIT_TO_WIDTH && (fitToPageDpi() != fitToWidthDpi())) { zoomLevel = FIT_TO_PAGE; //qWarning("FIT_TO_PAGE %i", zoomLevel); setZoom(FIT_TO_PAGE); } else if (zoomLevel == FIT_TO_WIDTH + 1) { zoomLevel = FIT_TO_WIDTH; //qWarning("FIT_TO_WIDTH__ %i", zoomLevel); setZoom(FIT_TO_WIDTH); } else if (zoomLevel > FIT_TO_WIDTH + 1) { zoomLevel--; //qWarning("zoomLevel-- %i", zoomLevel); setZoom(dpiToPercent(fitToWidthDpi()) + 15 * (zoomLevel - 1)); } selectedLink = selectLink(); emit zoomChanged();}void PDFWidget::zoomIn(){ if (zoomLevel == FIT_TO_PAGE) { zoomLevel = FIT_TO_WIDTH; //qWarning("FIT_TO_WIDTH %i", zoomLevel); setZoom(FIT_TO_WIDTH); } else if (zoomLevel < maxZoomLevel) { zoomLevel++; //qWarning("zoomLevel++ %i", zoomLevel); setZoom(dpiToPercent(fitToWidthDpi()) + 15 * (zoomLevel - 1)); } selectedLink = selectLink(); emit zoomChanged();}bool PDFWidget::zoomIsUpperBound(){ bool ret = true; if (doc) ret = zoomLevel == maxZoomLevel; return ret;}bool PDFWidget::zoomIsLowerBound(){ bool ret = true; if (doc) ret = (zoomLevel == FIT_TO_PAGE) || (zoomLevel == FIT_TO_WIDTH && fitToPageDpi() == fitToWidthDpi()); return ret;}bool PDFWidget::zoomIsDefault(){ return zoomLevel == FIT_TO_WIDTH;}// a temporary wrapper. setZoom is to broken to be public.void PDFWidget::setFitToPage(){ zoomLevel = FIT_TO_PAGE; setZoom(FIT_TO_PAGE);}void PDFWidget::setFitToWidth(){ zoomLevel = FIT_TO_WIDTH; setZoom(FIT_TO_WIDTH);}void PDFWidget::setZoom(unsigned int percent){ unsigned int dpi = 0; switch (percent) { case FIT_TO_WIDTH: dpi = fitToWidthDpi(); emit zoomChanged(); break; case FIT_TO_PAGE: dpi = fitToPageDpi(); emit zoomChanged(); break; default: dpi = static_cast<int>(percent * 72.0 / 100); //qWarning("dpi = %i, percent = %i", dpi, percent); break; } m_scale = dpi; if (doc == 0) { return; } // keep the content the user was viewing visible. int new_cX = (int) ((double)contentsX() / m_scale) * dpi; int new_cY = (int) ((double)contentsY() / m_scale) * dpi; scheduleContentsPosUpdate(new_cX, new_cY); display();}void PDFWidget::updateViewRect(){ int x1, y1, x2, y2; if (!pixmap) return; if (pixmap->width() < viewport()->width()) { x1 = 0; x2 = pixmap->width(); } else { x1 = contentsX() - x_origin > 0 ? contentsX() - x_origin : 0; x2 = x1 + visibleWidth() - x_origin; // FIXME if there is no border visible it's off by borderwidth pixels. } if (pixmap->height() < viewport()->height()) { //qWarning("doc inside vp"); y1 = 0; y2 = pixmap->height(); } else { // border visibl. at the top if (contentsY() <= y_origin) { //qWarning("border at top"); y1 = 0; y2 = visibleHeight() - (y_origin - contentsY()); } // border at the bottom else if ((contentsY() - y_origin) + visibleHeight() >= pixmap->height()) { //qWarning("border at bottom"); y1 = contentsY() - y_origin; y2 = y1 + (visibleHeight() - y_origin); } // no border visible else { //qWarning("no border whatsoever"); y1 = contentsY() - y_origin; y2 = y1 + visibleHeight(); } } //qWarning("y1 = %i, y2 = %i (diff %i)", y1, y2, y2 - y1); viewRect = QRect(QPoint(x1, y1), QPoint(x2, y2));}void PDFWidget::scrollUp(int factor){ static int delayCounter = 0; int tmp = selectNextLink(Up, selectedLink); if (tmp != selectedLink && tmp != -1) { selectedLink = tmp; drawLinks(pixmap, selectedLink); QRect m = viewRect; m.moveBy(0, -32); Poppler::Links links = page->getLinks(); Poppler::Link l = links.getLink(selectedLink); // will the link be visible if we scroll? if (m.contains(l.getScreenCoords().center(), true)) { //int x1, y1, x2, y2; //m.coords(&x1, &y1, &x2, &y2); //qWarning("x1 = %i, y1 = %i x2 = %i y2 = %i", x1, y1, x2, y2); //qWarning("xc = %i, yc = %i", l.getScreenCoords().center().x(), l.getScreenCoords().center().y()); scrollBy(0, -8 * factor); } updateContents(0, 0, contentsWidth(), contentsHeight()); } else { // special case: page is to small to scroll if (verticalScrollBar()->maxValue() == 0 && !running) { prevPage(); } else { scrollBy(0, -8 * factor); if (verticalScrollBar()->value() == verticalScrollBar()->minValue() && !running) { delayCounter++; if (delayCounter > MAX_SCROLL_DELAY) { prevPage(); delayCounter = 0; } } else { delayCounter = 0; } } } updateViewRect();}void PDFWidget::scrollDown(int factor){ static int delayCounter = 0; int tmp = selectNextLink(Down, selectedLink); if (tmp != selectedLink && tmp != -1) { selectedLink = tmp; drawLinks(pixmap, selectedLink); QRect m = viewRect; m.moveBy(0, 32); Poppler::Links links = page->getLinks(); Poppler::Link l = links.getLink(selectedLink); // will the link be visible if we scroll? if (m.contains(l.getScreenCoords().center(), true)) { //int x1, y1, x2, y2; //m.coords(&x1, &y1, &x2, &y2); //qWarning("x1 = %i, y1 = %i x2 = %i y2 = %i", x1, y1, x2, y2); //qWarning("xc = %i, yc = %i", l.getScreenCoords().center().x(), l.getScreenCoords().center().y()); scrollBy(0, 8 * factor); } updateContents(0, 0, contentsWidth(), contentsHeight()); } else { // special case: page is to small to scroll if (verticalScrollBar()->maxValue() == 0 && !running) { nextPage(); } else { scrollBy(0, 8 * factor); if (verticalScrollBar()->value() == verticalScrollBar()->maxValue() && !running) { delayCounter++; if (delayCounter > MAX_SCROLL_DELAY) { nextPage(); delayCounter = 0; } } else { delayCounter = 0; } } } updateViewRect();}void PDFWidget::scrollLeft(int factor){ int tmp = selectNextLink(Left, selectedLink); if (tmp != selectedLink) { selectedLink = tmp; drawLinks(pixmap, selectedLink); scrollBy(-8 * factor, 0); updateContents(0, 0, contentsWidth(), contentsHeight()); } else { scrollBy(-8 * factor, 0); } updateViewRect();}void PDFWidget::scrollRight(int factor){ int tmp = selectNextLink(Right, selectedLink); if (tmp != selectedLink) { selectedLink = tmp; drawLinks(pixmap, selectedLink); scrollBy(8 * factor, 0); updateContents(0, 0, contentsWidth(), contentsHeight()); } else { scrollBy(8 * factor, 0); } updateViewRect();}void PDFWidget::display(){ if (doc) { emit rendererRunning(true); running = true; firstPreview = true; renderJob j = { doc, currentPage, m_scale }; renderThread->setJob(j); } else { qWarning("No document was set"); }}void PDFWidget::customEvent(QCustomEvent *e) { if ((int)e->type() == PageReady) { pageReady(e->data());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -