~moshe-wagner/orayta/Orayta-QT

« back to all changes in this revision

Viewing changes to pdfwidget.cpp

  • Committer: moshe.wagner
  • Date: 2012-05-05 21:48:52 UTC
  • Revision ID: git-v1:70e09345355d8f7ecaf4ec2176a47d02dea9bcb7
Making the android branch into the main one

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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.
 
4
*
 
5
* Moshe Wagner. <moshe.wagner@gmail.com>
 
6
*/
 
7
 
 
8
 
 
9
/****************************************************************************
 
10
**
 
11
** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
 
12
** Contact: Qt Software Information (qt-info@nokia.com)
 
13
**
 
14
** This file is part of the documentation of Qt. It was originally
 
15
** published as part of Qt Quarterly.
 
16
**
 
17
** Commercial Usage
 
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.
 
22
**
 
23
**
 
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.
 
35
**
 
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.
 
41
**
 
42
** If you are unsure which license is appropriate for your use, please
 
43
** contact the sales department at qt-sales@nokia.com.
 
44
**
 
45
****************************************************************************/
 
46
 
 
47
 
 
48
 
 
49
#ifdef  POPPLER
 
50
 
 
51
#include "pdfwidget.h"
 
52
 
 
53
//TODO: Document
 
54
 
 
55
// Pdfwidget constructor
 
56
PdfWidget::PdfWidget(QWidget *parent) : QScrollArea(parent)
 
57
{
 
58
    viewlbl = new QLabel();
 
59
 
 
60
    viewlbl->setMouseTracking(false);
 
61
 
 
62
    setWidget(viewlbl);
 
63
    setWidgetResizable(true);
 
64
    verticalScrollBar()->setTracking(true);
 
65
 
 
66
    current_page = -1;
 
67
    doc = 0;
 
68
    rubberBand = 0;
 
69
    scaleFactor = 1.0;
 
70
    viewlbl->setAlignment(Qt::AlignCenter);
 
71
 
 
72
 
 
73
    //Init "copy" menu
 
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()));
 
78
 
 
79
    connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
 
80
    connect(verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(sliderRangeChanged(int,int)));
 
81
 
 
82
    setCursor(Qt::IBeamCursor);
 
83
 
 
84
    useRTL = false;
 
85
}
 
86
 
 
87
PdfWidget::~PdfWidget()
 
88
{
 
89
    delete doc;
 
90
}
 
91
 
 
92
//Returns the PdfWidget's document
 
93
Poppler::Document *PdfWidget::document()
 
94
{
 
95
    return doc;
 
96
}
 
97
 
 
98
QMatrix PdfWidget::matrix() const
 
99
{
 
100
    return QMatrix(scaleFactor * viewlbl->physicalDpiX() / 72.0, 0,
 
101
                   0, scaleFactor * viewlbl->physicalDpiY() / 72.0,
 
102
                   0, 0);
 
103
}
 
104
 
 
105
 
 
106
void PdfWidget::DrawRects(QList <QRect> rectlist, QColor color)
 
107
{
 
108
    QImage i(Image.size(), Image.format());
 
109
 
 
110
    foreach (QRect rect, rectlist)
 
111
    {
 
112
        QRect highlightRect = matrix().mapRect(rect);
 
113
        highlightRect.adjust(0, -1, 0, 1);
 
114
 
 
115
        QPainter painter;
 
116
        painter.begin(&i);
 
117
 
 
118
        painter.setCompositionMode(QPainter::CompositionMode_Source);
 
119
 
 
120
        QColor c;
 
121
        c.setRgba(color.rgba());
 
122
 
 
123
        QBrush  b(c);
 
124
        painter.fillRect(highlightRect, b);
 
125
 
 
126
        painter.end();
 
127
    }
 
128
 
 
129
    QImage image(Image);
 
130
 
 
131
    QPainter p;
 
132
    p.begin(&image);
 
133
    p.drawImage(QPoint(0,0), i);
 
134
    p.end();
 
135
 
 
136
    viewlbl->setPixmap(QPixmap::fromImage(image));
 
137
}
 
138
 
 
139
//Returns a QLine representing the size and position
 
140
//  of the text line that is at the given point.
 
141
//
 
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)
 
145
{
 
146
    int h = 4; //Minimal size
 
147
 
 
148
    //Create a thin rect going across the whole page
 
149
    QRect r(0,p.y(),10000,1);
 
150
    r = matrix().inverted().mapRect(r);
 
151
 
 
152
    p = matrix().inverted().map(p);
 
153
    int top = p.y();
 
154
 
 
155
    foreach (Poppler::TextBox *box, doc->page(current_page)->textList())
 
156
    {
 
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))
 
160
        {
 
161
            int nh = box->boundingBox().height();
 
162
 
 
163
            if (nh > h)
 
164
            {
 
165
                h = nh;
 
166
                top = box->boundingBox().top();
 
167
            }
 
168
        }
 
169
 
 
170
    }
 
171
 
 
172
    return QLine(QPoint(p.x(), top), QPoint(p.x(), top + h));
 
173
}
 
174
 
 
175
 
 
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)
 
178
{
 
179
    selectedText = "";
 
180
 
 
181
    QList <QRect> hits;
 
182
 
 
183
    //Make sure p1 is the top point
 
184
    if (p1.y() > p2.y()) swap(p1, p2);
 
185
 
 
186
    //Determine top and bottem line height
 
187
    QLine l1 = TextLine(p1);
 
188
    QLine l2 = TextLine(p2);
 
189
 
 
190
    //One line mode (if l1 and l2's heights intersect)
 
191
    if ( l2.y1() <= l1.y2())
 
192
    {
 
193
        QRect textRect(p1, p2);
 
194
        textRect = matrix().inverted().mapRect(textRect);
 
195
 
 
196
        bool hadSpace = false;
 
197
        QPointF center;
 
198
        QRect rect ;
 
199
 
 
200
        foreach (Poppler::TextBox *box, doc->page(current_page)->textList())
 
201
        {
 
202
            if (box->boundingBox().intersects(textRect))
 
203
            {
 
204
                for (int i=0; i<box->text().length(); i++)
 
205
                {
 
206
                    QRect rect = box->charBoundingBox(i).toAlignedRect();
 
207
                    if (rect.intersects(textRect))
 
208
                    {
 
209
                        rect.adjust(-1,0,1,0);
 
210
                        hits << rect;
 
211
 
 
212
                        selectedText += box->text()[i];
 
213
                    }
 
214
                }
 
215
                if (hadSpace)
 
216
                    selectedText += " ";
 
217
                if (!selectedText.isEmpty() && box->boundingBox().top() > center.y())
 
218
                    selectedText += "\n";
 
219
 
 
220
                hadSpace = box->hasSpaceAfter();
 
221
                center = box->boundingBox().center();
 
222
            }
 
223
        }
 
224
    }
 
225
    else
 
226
    {
 
227
        p1 = matrix().inverted().map(p1);
 
228
        p2 = matrix().inverted().map(p2);
 
229
        QRect topline;
 
230
        QRect bottemline;
 
231
 
 
232
        if (RTL)
 
233
        {
 
234
            topline.setCoords(p1.x(), l1.p1().y(), 0, l1.p2().y() );
 
235
            bottemline.setCoords(p2.x(), l2.p1().y(), 1000, l2.p2().y());
 
236
        }
 
237
        else
 
238
        {
 
239
            topline.setCoords(p1.x(), l1.p1().y(), 1000, l1.p2().y() );
 
240
            bottemline.setCoords(p2.x(), l2.p1().y(), 0, l2.p2().y() );
 
241
        }
 
242
 
 
243
        QRect rest( QPoint(0, l1.p2().y() + 5), QPoint(1000, l2.p1().y() - 5) );
 
244
 
 
245
        if (rest.top() <= topline.bottom() || rest.bottom() >= bottemline.top()) rest = topline;
 
246
        if (rest.intersects(topline) || rest.intersects(bottemline)) rest = topline;
 
247
 
 
248
        if (bottemline.top() <= topline.bottom() ) bottemline = topline;
 
249
 
 
250
    
 
251
        bool hadSpace = false;
 
252
        QPointF center;
 
253
        QRect rect ;
 
254
 
 
255
        foreach (Poppler::TextBox *box, doc->page(current_page)->textList())
 
256
        {
 
257
            if (box->boundingBox().intersects(topline) || box->boundingBox().intersects(bottemline) || box->boundingBox().intersects(rest))
 
258
            {
 
259
                for (int i=0; i<box->text().length(); i++)
 
260
                {
 
261
                    rect = box->charBoundingBox(i).toAlignedRect();
 
262
                    if (rect.intersects(topline) || rect.intersects(bottemline) || rect.intersects(rest))
 
263
                    {
 
264
                        rect.adjust(-1,0,1,0);
 
265
                        hits << rect;
 
266
 
 
267
                        selectedText += box->text()[i];
 
268
                    }
 
269
                }
 
270
                if (hadSpace)
 
271
                    selectedText += " ";
 
272
                if (!selectedText.isEmpty() && box->boundingBox().top() > center.y())
 
273
                    selectedText += "\n";
 
274
 
 
275
                hadSpace = box->hasSpaceAfter();
 
276
                center = box->boundingBox().center();
 
277
            }
 
278
        }
 
279
    }
 
280
 
 
281
 
 
282
    return hits;
 
283
}
 
284
 
 
285
 
 
286
void PdfWidget::mousePressEvent(QMouseEvent *event)
 
287
{
 
288
    if (!doc)
 
289
        return;
 
290
 
 
291
    if (event->button() == Qt::LeftButton)
 
292
    {
 
293
        dragPosition = event->globalPos();
 
294
        dragPosition = viewlbl->mapFromGlobal(dragPosition);
 
295
 
 
296
        //Clean
 
297
        selected.clear();
 
298
        DrawRects(selected, QColor());
 
299
        selectedText = "";
 
300
    }
 
301
}
 
302
 
 
303
void PdfWidget::mouseMoveEvent(QMouseEvent *event)
 
304
{
 
305
    if (!doc)
 
306
        return;
 
307
 
 
308
    QPoint newPosition = event->globalPos();
 
309
    newPosition = viewlbl->mapFromGlobal(newPosition); 
 
310
 
 
311
    selected = SelectText(dragPosition, newPosition, useRTL);
 
312
    QColor c("Blue"); c.setAlpha(100);
 
313
    DrawRects(selected, c);
 
314
}
 
315
 
 
316
void PdfWidget::mouseReleaseEvent(QMouseEvent * event)
 
317
{
 
318
    if (!doc)
 
319
        return;
 
320
 
 
321
    if (event->button() == Qt::RightButton)
 
322
    {
 
323
        menu->setLayoutDirection(Qt::RightToLeft);
 
324
        menu->exec(event->globalPos());
 
325
    }
 
326
}
 
327
 
 
328
qreal PdfWidget::scale() const
 
329
{
 
330
    return scaleFactor;
 
331
}
 
332
 
 
333
void PdfWidget::showPage(int page)
 
334
{
 
335
 
 
336
    if (page != -1 && page != current_page + 1) {
 
337
        current_page = page - 1;
 
338
        emit pageChanged(page, doc->numPages());
 
339
    }
 
340
 
 
341
    Poppler::Page* pdfPage = doc->page(current_page);
 
342
    if (pdfPage == 0) {
 
343
        qDebug() << "Couldn't open page " << current_page;
 
344
        return;
 
345
    }
 
346
 
 
347
    Image = pdfPage->renderToImage(scaleFactor * viewlbl->physicalDpiX(), scaleFactor * viewlbl->physicalDpiY());
 
348
 
 
349
    if (Image.isNull()) {
 
350
        qDebug() << "Poppler::Page::renderToImage fail...";
 
351
        return;
 
352
    }
 
353
 
 
354
    if (!searchLocation.isEmpty()) {
 
355
        QRect highlightRect = matrix().mapRect(searchLocation).toRect();
 
356
        highlightRect.adjust(-1, -1, 1, 1);
 
357
 
 
358
        QPainter painter;
 
359
        painter.begin(&Image);
 
360
 
 
361
        QColor c;
 
362
        c.setRgb(QColor("Yellow").rgb());
 
363
        c.setAlpha(130);
 
364
 
 
365
        QBrush  b(c);
 
366
        painter.fillRect(highlightRect, b);
 
367
 
 
368
        painter.end();
 
369
    }
 
370
 
 
371
    viewlbl->setPixmap(QPixmap::fromImage(Image));
 
372
 
 
373
    //Decide if selection should be RTL or not:
 
374
    int count = 0;
 
375
    foreach (Poppler::TextBox *box, doc->page(current_page)->textList())
 
376
    {
 
377
        if (QTextCodec::codecForName("latin1")->canEncode(box->text())) count ++;
 
378
    }
 
379
    if (count < (doc->page(current_page)->textList().size()) / 2) useRTL = true;
 
380
 
 
381
    //qDebug() << doc->metadata();
 
382
 
 
383
}
 
384
 
 
385
QRectF PdfWidget::searchBackwards(const QString &stext)
 
386
{
 
387
    //Invert text to visual (flips hebrew chars).
 
388
    QString text = ToBidiText(stext);
 
389
 
 
390
    QRectF oldLocation = searchLocation;
 
391
 
 
392
    int page = current_page;
 
393
    if (oldLocation.isNull())
 
394
        page -= 1;
 
395
 
 
396
    while (page > -1) {
 
397
 
 
398
        QList<QRectF> locations;
 
399
        searchLocation = QRectF();
 
400
 
 
401
        while (doc->page(page)->search(text, searchLocation, Poppler::Page::NextResult, Poppler::Page::CaseInsensitive))
 
402
        {
 
403
            if (searchLocation != oldLocation)
 
404
                locations.append(searchLocation);
 
405
            else
 
406
                break;
 
407
        }
 
408
 
 
409
        int index = locations.indexOf(oldLocation);
 
410
        if (index == -1 && !locations.isEmpty()) {
 
411
            searchLocation = locations.last();
 
412
            showPage(page + 1);
 
413
            return searchLocation;
 
414
        } else if (index > 0) {
 
415
            searchLocation = locations[index - 1];
 
416
            showPage(page + 1);
 
417
            return searchLocation;
 
418
        }
 
419
 
 
420
        oldLocation = QRectF();
 
421
        page -= 1;
 
422
    }
 
423
 
 
424
    if (current_page == doc->numPages() - 1)
 
425
        return QRectF();
 
426
 
 
427
    oldLocation = QRectF();
 
428
    page = doc->numPages() - 1;
 
429
 
 
430
    while (page > current_page) {
 
431
 
 
432
        QList<QRectF> locations;
 
433
        searchLocation = QRectF();
 
434
 
 
435
        while (doc->page(page)->search(text, searchLocation,
 
436
                                       Poppler::Page::NextResult, Poppler::Page::CaseInsensitive)) {
 
437
 
 
438
            locations.append(searchLocation);
 
439
        }
 
440
 
 
441
        if (!locations.isEmpty()) {
 
442
            searchLocation = locations.last();
 
443
            showPage(page + 1);
 
444
            return searchLocation;
 
445
        }
 
446
        page -= 1;
 
447
    }
 
448
 
 
449
    return QRectF();
 
450
}
 
451
 
 
452
QRectF PdfWidget::searchForwards(const QString &stext)
 
453
{
 
454
 
 
455
    //Invert text to visual (flips hebrew chars).
 
456
    QString text = ToBidiText(stext);
 
457
 
 
458
    int page = current_page;
 
459
    while (page < doc->numPages()) {
 
460
 
 
461
        if (doc->page(page)->search(text, searchLocation,
 
462
                                    Poppler::Page::NextResult, Poppler::Page::CaseInsensitive)) {
 
463
            if (!searchLocation.isNull()) {
 
464
                showPage(page + 1);
 
465
                return searchLocation;
 
466
            }
 
467
        }
 
468
        page += 1;
 
469
        searchLocation = QRectF();
 
470
    }
 
471
 
 
472
    page = 0;
 
473
 
 
474
    while (page < current_page) {
 
475
 
 
476
        searchLocation = QRectF();
 
477
 
 
478
        if (doc->page(page)->search(text, searchLocation,
 
479
                                    Poppler::Page::NextResult, Poppler::Page::CaseInsensitive)) {
 
480
            if (!searchLocation.isNull()) {
 
481
                showPage(page + 1);
 
482
                return QRectF();
 
483
                return searchLocation;
 
484
            }
 
485
        }
 
486
        page += 1;
 
487
    }
 
488
 
 
489
    return QRectF();
 
490
}
 
491
 
 
492
void PdfWidget::copyText()
 
493
{
 
494
    if (!selectedText.isEmpty())
 
495
    {
 
496
        //Invert text to visual (flips hebrew chars).
 
497
        selectedText = ToBidiText(selectedText);
 
498
 
 
499
        //Copy to clipboard:
 
500
        QClipboard *clipboard = QApplication::clipboard();
 
501
        clipboard->setText(selectedText, QClipboard::Clipboard);
 
502
    }
 
503
}
 
504
 
 
505
bool PdfWidget::setDocument(const QString &filePath)
 
506
{
 
507
    Poppler::Document *oldDocument = doc;
 
508
 
 
509
    /*
 
510
    //Dosn't work on some MS-Windows systems
 
511
    doc = Poppler::Document::load(filePath);
 
512
    */
 
513
    QString fp = filePath;
 
514
    //Hack to force "file:///" pathnames to normal ones
 
515
    fp   = fp.replace("file:///", "/");
 
516
 
 
517
    QFile file(fp);
 
518
 
 
519
 
 
520
    if (!file.open(QIODevice::ReadOnly))
 
521
    {
 
522
        qDebug() << "couldn't open " << fp;
 
523
        return false;
 
524
    }
 
525
 
 
526
    QByteArray data = file.read( file.size() );
 
527
    file.close();
 
528
 
 
529
    doc = Poppler::Document::loadFromData(data);
 
530
    if (doc) {
 
531
        delete oldDocument;
 
532
        doc->setRenderHint(Poppler::Document::Antialiasing);
 
533
        doc->setRenderHint(Poppler::Document::TextAntialiasing);
 
534
        searchLocation = QRectF();
 
535
        current_page = -1;
 
536
        setPage(1);
 
537
    }
 
538
    else
 
539
    {
 
540
        qDebug() << "Couldn't open " << fp;
 
541
    }
 
542
 
 
543
    return doc != 0;
 
544
}
 
545
 
 
546
void PdfWidget::setPage(int page)
 
547
{
 
548
    if (page != -1 && page != current_page + 1) {
 
549
        searchLocation = QRectF();
 
550
        showPage(page);
 
551
    }
 
552
}
 
553
 
 
554
void PdfWidget::setScale(qreal scale)
 
555
{
 
556
    if (scaleFactor != scale) {
 
557
        scaleFactor = scale;
 
558
        showPage();
 
559
    }
 
560
}
 
561
 
 
562
int PdfWidget::numPages() const
 
563
{
 
564
    if (doc)
 
565
        return doc->numPages();
 
566
    else
 
567
        return 0;
 
568
}
 
569
 
 
570
int PdfWidget::currentPage() const
 
571
{
 
572
    return current_page + 1;
 
573
}
 
574
 
 
575
void PdfWidget::sliderValueChanged(int val)
 
576
{
 
577
    if (val == mSliderTop)
 
578
    {
 
579
        if (mSTO == true)
 
580
        {
 
581
            previousPage();
 
582
            mSTO = false;
 
583
        }
 
584
        else
 
585
        {
 
586
            mSTO = true;
 
587
        }
 
588
    }
 
589
    else if (val == mSliderBottem)
 
590
    {
 
591
        if (mSBO == true)
 
592
        {
 
593
            nextPage();
 
594
            mSBO = false;
 
595
        }
 
596
        else
 
597
        {
 
598
            mSBO = true;
 
599
        }
 
600
    }
 
601
}
 
602
 
 
603
void PdfWidget::sliderRangeChanged(int top, int bottem)
 
604
{
 
605
    mSliderTop = top;
 
606
    mSliderBottem = bottem;
 
607
}
 
608
 
 
609
void PdfWidget::nextPage()
 
610
{
 
611
    if (numPages() > current_page)
 
612
    {
 
613
        setPage(current_page + 2);
 
614
        verticalScrollBar()->setValue(0);
 
615
        mSTO = false;
 
616
    }
 
617
}
 
618
 
 
619
void PdfWidget::previousPage()
 
620
{
 
621
    if (current_page > 0)
 
622
    {
 
623
        setPage(current_page);
 
624
        verticalScrollBar()->setValue(0);
 
625
        mSTO = false;
 
626
    }
 
627
}
 
628
 
 
629
#endif