2
* This file is based on the "poppler" exapmle from Qt's documantation,
3
* ( Copyright info here below), but has many changes done by me.
5
* Moshe Wagner. <moshe.wagner@gmail.com>
9
/****************************************************************************
11
** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
12
** Contact: Qt Software Information (qt-info@nokia.com)
14
** This file is part of the documentation of Qt. It was originally
15
** published as part of Qt Quarterly.
18
** Licensees holding valid Qt Commercial licenses may use this file in
19
** accordance with the Qt Commercial License Agreement provided with the
20
** Software or, alternatively, in accordance with the terms contained in
21
** a written agreement between you and Nokia.
24
** GNU General Public License Usage
25
** Alternatively, this file may be used under the terms of the GNU
26
** General Public License versions 2.0 or 3.0 as published by the Free
27
** Software Foundation and appearing in the file LICENSE.GPL included in
28
** the packaging of this file. Please review the following information
29
** to ensure GNU General Public Licensing requirements will be met:
30
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
31
** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
32
** exception, Nokia gives you certain additional rights. These rights
33
** are described in the Nokia Qt GPL Exception version 1.3, included in
34
** the file GPL_EXCEPTION.txt in this package.
36
** Qt for Windows(R) Licensees
37
** As a special exception, Nokia, as the sole copyright holder for Qt
38
** Designer, grants users of the Qt/Eclipse Integration plug-in the
39
** right for the Qt/Eclipse Integration to link to functionality
40
** provided by Qt Designer and its related libraries.
42
** If you are unsure which license is appropriate for your use, please
43
** contact the sales department at qt-sales@nokia.com.
45
****************************************************************************/
51
#include "pdfwidget.h"
55
// Pdfwidget constructor
56
PdfWidget::PdfWidget(QWidget *parent) : QScrollArea(parent)
58
viewlbl = new QLabel();
60
viewlbl->setMouseTracking(false);
63
setWidgetResizable(true);
64
verticalScrollBar()->setTracking(true);
70
viewlbl->setAlignment(Qt::AlignCenter);
74
menu = new QMenu(this);
75
copy = new QAction(QIcon(":/Icons/edit-copy.png"), tr("Copy text"), this);
76
menu->addAction(copy);
77
connect (copy, SIGNAL(triggered()), this, SLOT(copyText()));
79
connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
80
connect(verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(sliderRangeChanged(int,int)));
82
setCursor(Qt::IBeamCursor);
87
PdfWidget::~PdfWidget()
92
//Returns the PdfWidget's document
93
Poppler::Document *PdfWidget::document()
98
QMatrix PdfWidget::matrix() const
100
return QMatrix(scaleFactor * viewlbl->physicalDpiX() / 72.0, 0,
101
0, scaleFactor * viewlbl->physicalDpiY() / 72.0,
106
void PdfWidget::DrawRects(QList <QRect> rectlist, QColor color)
108
QImage i(Image.size(), Image.format());
110
foreach (QRect rect, rectlist)
112
QRect highlightRect = matrix().mapRect(rect);
113
highlightRect.adjust(0, -1, 0, 1);
118
painter.setCompositionMode(QPainter::CompositionMode_Source);
121
c.setRgba(color.rgba());
124
painter.fillRect(highlightRect, b);
133
p.drawImage(QPoint(0,0), i);
136
viewlbl->setPixmap(QPixmap::fromImage(image));
139
//Returns a QLine representing the size and position
140
// of the text line that is at the given point.
142
// If the point is not in the middle of any line -
143
// a height of 4 pixels, and the position of the given point are returned.
144
QLine PdfWidget::TextLine(QPoint p)
146
int h = 4; //Minimal size
148
//Create a thin rect going across the whole page
149
QRect r(0,p.y(),10000,1);
150
r = matrix().inverted().mapRect(r);
152
p = matrix().inverted().map(p);
155
foreach (Poppler::TextBox *box, doc->page(current_page)->textList())
157
//See if the rect intersects any word on the page.
158
// Find the biggest word, and remember it's top and height.
159
if (box->boundingBox().intersects(r))
161
int nh = box->boundingBox().height();
166
top = box->boundingBox().top();
172
return QLine(QPoint(p.x(), top), QPoint(p.x(), top + h));
176
//Returns a list of QRects, marking of all text selected between the two given points
177
QList <QRect> PdfWidget::SelectText(QPoint p1, QPoint p2, bool RTL)
183
//Make sure p1 is the top point
184
if (p1.y() > p2.y()) swap(p1, p2);
186
//Determine top and bottem line height
187
QLine l1 = TextLine(p1);
188
QLine l2 = TextLine(p2);
190
//One line mode (if l1 and l2's heights intersect)
191
if ( l2.y1() <= l1.y2())
193
QRect textRect(p1, p2);
194
textRect = matrix().inverted().mapRect(textRect);
196
bool hadSpace = false;
200
foreach (Poppler::TextBox *box, doc->page(current_page)->textList())
202
if (box->boundingBox().intersects(textRect))
204
for (int i=0; i<box->text().length(); i++)
206
QRect rect = box->charBoundingBox(i).toAlignedRect();
207
if (rect.intersects(textRect))
209
rect.adjust(-1,0,1,0);
212
selectedText += box->text()[i];
217
if (!selectedText.isEmpty() && box->boundingBox().top() > center.y())
218
selectedText += "\n";
220
hadSpace = box->hasSpaceAfter();
221
center = box->boundingBox().center();
227
p1 = matrix().inverted().map(p1);
228
p2 = matrix().inverted().map(p2);
234
topline.setCoords(p1.x(), l1.p1().y(), 0, l1.p2().y() );
235
bottemline.setCoords(p2.x(), l2.p1().y(), 1000, l2.p2().y());
239
topline.setCoords(p1.x(), l1.p1().y(), 1000, l1.p2().y() );
240
bottemline.setCoords(p2.x(), l2.p1().y(), 0, l2.p2().y() );
243
QRect rest( QPoint(0, l1.p2().y() + 5), QPoint(1000, l2.p1().y() - 5) );
245
if (rest.top() <= topline.bottom() || rest.bottom() >= bottemline.top()) rest = topline;
246
if (rest.intersects(topline) || rest.intersects(bottemline)) rest = topline;
248
if (bottemline.top() <= topline.bottom() ) bottemline = topline;
251
bool hadSpace = false;
255
foreach (Poppler::TextBox *box, doc->page(current_page)->textList())
257
if (box->boundingBox().intersects(topline) || box->boundingBox().intersects(bottemline) || box->boundingBox().intersects(rest))
259
for (int i=0; i<box->text().length(); i++)
261
rect = box->charBoundingBox(i).toAlignedRect();
262
if (rect.intersects(topline) || rect.intersects(bottemline) || rect.intersects(rest))
264
rect.adjust(-1,0,1,0);
267
selectedText += box->text()[i];
272
if (!selectedText.isEmpty() && box->boundingBox().top() > center.y())
273
selectedText += "\n";
275
hadSpace = box->hasSpaceAfter();
276
center = box->boundingBox().center();
286
void PdfWidget::mousePressEvent(QMouseEvent *event)
291
if (event->button() == Qt::LeftButton)
293
dragPosition = event->globalPos();
294
dragPosition = viewlbl->mapFromGlobal(dragPosition);
298
DrawRects(selected, QColor());
303
void PdfWidget::mouseMoveEvent(QMouseEvent *event)
308
QPoint newPosition = event->globalPos();
309
newPosition = viewlbl->mapFromGlobal(newPosition);
311
selected = SelectText(dragPosition, newPosition, useRTL);
312
QColor c("Blue"); c.setAlpha(100);
313
DrawRects(selected, c);
316
void PdfWidget::mouseReleaseEvent(QMouseEvent * event)
321
if (event->button() == Qt::RightButton)
323
menu->setLayoutDirection(Qt::RightToLeft);
324
menu->exec(event->globalPos());
328
qreal PdfWidget::scale() const
333
void PdfWidget::showPage(int page)
336
if (page != -1 && page != current_page + 1) {
337
current_page = page - 1;
338
emit pageChanged(page, doc->numPages());
341
Poppler::Page* pdfPage = doc->page(current_page);
343
qDebug() << "Couldn't open page " << current_page;
347
Image = pdfPage->renderToImage(scaleFactor * viewlbl->physicalDpiX(), scaleFactor * viewlbl->physicalDpiY());
349
if (Image.isNull()) {
350
qDebug() << "Poppler::Page::renderToImage fail...";
354
if (!searchLocation.isEmpty()) {
355
QRect highlightRect = matrix().mapRect(searchLocation).toRect();
356
highlightRect.adjust(-1, -1, 1, 1);
359
painter.begin(&Image);
362
c.setRgb(QColor("Yellow").rgb());
366
painter.fillRect(highlightRect, b);
371
viewlbl->setPixmap(QPixmap::fromImage(Image));
373
//Decide if selection should be RTL or not:
375
foreach (Poppler::TextBox *box, doc->page(current_page)->textList())
377
if (QTextCodec::codecForName("latin1")->canEncode(box->text())) count ++;
379
if (count < (doc->page(current_page)->textList().size()) / 2) useRTL = true;
381
//qDebug() << doc->metadata();
385
QRectF PdfWidget::searchBackwards(const QString &stext)
387
//Invert text to visual (flips hebrew chars).
388
QString text = ToBidiText(stext);
390
QRectF oldLocation = searchLocation;
392
int page = current_page;
393
if (oldLocation.isNull())
398
QList<QRectF> locations;
399
searchLocation = QRectF();
401
while (doc->page(page)->search(text, searchLocation, Poppler::Page::NextResult, Poppler::Page::CaseInsensitive))
403
if (searchLocation != oldLocation)
404
locations.append(searchLocation);
409
int index = locations.indexOf(oldLocation);
410
if (index == -1 && !locations.isEmpty()) {
411
searchLocation = locations.last();
413
return searchLocation;
414
} else if (index > 0) {
415
searchLocation = locations[index - 1];
417
return searchLocation;
420
oldLocation = QRectF();
424
if (current_page == doc->numPages() - 1)
427
oldLocation = QRectF();
428
page = doc->numPages() - 1;
430
while (page > current_page) {
432
QList<QRectF> locations;
433
searchLocation = QRectF();
435
while (doc->page(page)->search(text, searchLocation,
436
Poppler::Page::NextResult, Poppler::Page::CaseInsensitive)) {
438
locations.append(searchLocation);
441
if (!locations.isEmpty()) {
442
searchLocation = locations.last();
444
return searchLocation;
452
QRectF PdfWidget::searchForwards(const QString &stext)
455
//Invert text to visual (flips hebrew chars).
456
QString text = ToBidiText(stext);
458
int page = current_page;
459
while (page < doc->numPages()) {
461
if (doc->page(page)->search(text, searchLocation,
462
Poppler::Page::NextResult, Poppler::Page::CaseInsensitive)) {
463
if (!searchLocation.isNull()) {
465
return searchLocation;
469
searchLocation = QRectF();
474
while (page < current_page) {
476
searchLocation = QRectF();
478
if (doc->page(page)->search(text, searchLocation,
479
Poppler::Page::NextResult, Poppler::Page::CaseInsensitive)) {
480
if (!searchLocation.isNull()) {
483
return searchLocation;
492
void PdfWidget::copyText()
494
if (!selectedText.isEmpty())
496
//Invert text to visual (flips hebrew chars).
497
selectedText = ToBidiText(selectedText);
500
QClipboard *clipboard = QApplication::clipboard();
501
clipboard->setText(selectedText, QClipboard::Clipboard);
505
bool PdfWidget::setDocument(const QString &filePath)
507
Poppler::Document *oldDocument = doc;
510
//Dosn't work on some MS-Windows systems
511
doc = Poppler::Document::load(filePath);
513
QString fp = filePath;
514
//Hack to force "file:///" pathnames to normal ones
515
fp = fp.replace("file:///", "/");
520
if (!file.open(QIODevice::ReadOnly))
522
qDebug() << "couldn't open " << fp;
526
QByteArray data = file.read( file.size() );
529
doc = Poppler::Document::loadFromData(data);
532
doc->setRenderHint(Poppler::Document::Antialiasing);
533
doc->setRenderHint(Poppler::Document::TextAntialiasing);
534
searchLocation = QRectF();
540
qDebug() << "Couldn't open " << fp;
546
void PdfWidget::setPage(int page)
548
if (page != -1 && page != current_page + 1) {
549
searchLocation = QRectF();
554
void PdfWidget::setScale(qreal scale)
556
if (scaleFactor != scale) {
562
int PdfWidget::numPages() const
565
return doc->numPages();
570
int PdfWidget::currentPage() const
572
return current_page + 1;
575
void PdfWidget::sliderValueChanged(int val)
577
if (val == mSliderTop)
589
else if (val == mSliderBottem)
603
void PdfWidget::sliderRangeChanged(int top, int bottem)
606
mSliderBottem = bottem;
609
void PdfWidget::nextPage()
611
if (numPages() > current_page)
613
setPage(current_page + 2);
614
verticalScrollBar()->setValue(0);
619
void PdfWidget::previousPage()
621
if (current_page > 0)
623
setPage(current_page);
624
verticalScrollBar()->setValue(0);