pdfpage.cpp 16,8 КБ
Newer Older
OMP Education's avatar
OMP Education включено в состав коммита
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/****************************************************************************
**
** Copyright (C) 2021 - 2022 Open Mobile Platform LLC.
** Contact: https://community.omprussia.ru/open-source
**
** This file is part of the AmberPDF project.
**
** $QT_BEGIN_LICENSE:BSD$
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of Open Mobile Platform LLC copyright holder nor
**     the names of its contributors may be used to endorse or promote
**     products derived from this software without specific prior written
**     permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtAlgorithms>
OMP Education's avatar
OMP Education включено в состав коммита
41
#include <pdfium/fpdf_text.h>
OMP Education's avatar
OMP Education включено в состав коммита
42
43
44
45
46
47
48
49
50
51
52
53
54
55

#include "pdftaskqueue.h"
#include "pdfdocumentholder.h"
#include "tasks/pdfpageclosetask.h"
#include "tasks/pdfpageannotationloadtask.h"
#include "tasks/pdfpagerendertask.h"
#include "tasks/pdfpagewordstask.h"
#include "tasks/pdfpagesizetask.h"
#include "tasks/pdfpageaddannotationtask.h"
#include "tasks/pdfpageremoveannotationtask.h"
#include "tasks/pdfpageaddinkannotationtask.h"
#include "tasks/pdfpagedrawsolidpaths.h"
#include "tasks/pdfpageaddimagetask.h"
#include "tasks/pdfpageremovesearchresultannotationstask.h"
OMP Education's avatar
OMP Education включено в состав коммита
56
57
#include "tasks/pdfpageaddinkannotationtask.h"

OMP Education's avatar
OMP Education включено в состав коммита
58
59
60
61

#include "pdfpage_p.h"
#include "pdfpage.h"

OMP Education's avatar
OMP Education включено в состав коммита
62
63
64
#define TEXT_FIND_X_TOLERANCE 50
#define TEXT_FIND_Y_TOLERANCE 50

OMP Education's avatar
OMP Education включено в состав коммита
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
PdfPageData::PdfPageData() : m_pageNumber(-1) { }

PdfPageData::PdfPageData(int pageNumber, QSharedPointer<fpdf_page_t__> page,
                         QSharedPointer<PdfDocumentHolder> doc)
    : m_pageNumber(pageNumber), m_page(page), m_documentHolder(doc)
{
}

PdfPageData::PdfPageData(const PdfPageData &other)
    : QSharedData(other),
      m_pageNumber(other.m_pageNumber),
      m_page(other.m_page),
      m_documentHolder(other.m_documentHolder),
      m_originalSize(other.m_originalSize)
{
}

PdfPageData::PdfPageData(PdfPageData &&other)
    : QSharedData(qMove(other)),
      m_pageNumber(qMove(other.m_pageNumber)),
      m_page(qMove(other.m_page)),
      m_documentHolder(qMove(other.m_documentHolder)),
      m_originalSize(qMove(other.m_originalSize))
{
}

PdfPageData &PdfPageData::operator=(const PdfPageData &other)
{
    m_pageNumber = other.m_pageNumber;
    m_page = other.m_page;
    m_documentHolder = other.m_documentHolder;
    m_originalSize = other.m_originalSize;
    m_words = other.m_words;
    m_originalSize = other.m_originalSize;
    return *this;
}

PdfPageData &PdfPageData::operator=(PdfPageData &&other)
{
    qSwap(m_pageNumber, other.m_pageNumber);
    qSwap(m_page, other.m_page);
    qSwap(m_documentHolder, other.m_documentHolder);
    qSwap(m_originalSize, other.m_originalSize);
    qSwap(m_words, other.m_words);
    qSwap(m_originalSize, other.m_originalSize);
    return *this;
}

PdfPageData::~PdfPageData()
{
    if (!m_originalSize.isFinished())
        m_originalSize.cancel();

    auto wordsFuture = m_words.future();
    if (wordsFuture.isFinished()) {
        auto wordsList = wordsFuture.result();
        QMutableListIterator<QObject *> wordsIt(wordsList);
        while (wordsIt.hasNext())
            delete wordsIt.next();
    } else {
        wordsFuture.cancel();
    }

    auto closePageInterface = QFutureInterface<void>();
    auto *closePageTask = new PdfPageCloseTask(m_documentHolder, closePageInterface, m_page);
    closePageTask->run();
    delete closePageTask;

    closePageInterface.reportStarted();
    closePageInterface.future().waitForFinished();
}

PdfPage::PdfPage() : d(new PdfPageData()) { }

PdfPage::~PdfPage() = default;

int PdfPage::pageNumber() const
{
    return d->m_pageNumber;
}

bool PdfPage::isValid() const
{
    if (!d->m_documentHolder && !d->m_page)
        return false;

    return d->m_page;
}

QFuture<QSizeF> PdfPage::originalSize()
{
    if (d->m_originalSize.isFinished() || d->m_originalSize.isRunning())
        return d->m_originalSize.future();

OMP Education's avatar
OMP Education включено в состав коммита
159
    auto task = QSharedPointer<PdfTask>(new PdfPageSizeTask(d->m_pageNumber, d->m_documentHolder, d->m_originalSize));
OMP Education's avatar
OMP Education включено в состав коммита
160
161
162
    auto addingResult = PdfTaskQueue::instance().addTask(task, PdfTaskQueue::TaskPriority::High,
                                                         d->m_documentHolder->id());
    if (!addingResult) {
OMP Education's avatar
OMP Education включено в состав коммита
163
        task.reset();
OMP Education's avatar
OMP Education включено в состав коммита
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
        return {};
    }

    d->m_originalSize.reportStarted();

    return d->m_originalSize.future();
}

QFuture<QImage> PdfPage::bitmapFull(qreal pageScale, int renderFlags) const
{
    if (!d->m_documentHolder || !d->m_page)
        return {};

    if (!d->m_originalSize.isFinished())
        return {};

    QFutureInterface<QImage> interface;
    const auto &future = interface.future();

OMP Education's avatar
OMP Education включено в состав коммита
183
184
185
    auto task =
            QSharedPointer<PdfTask>(new PdfPageRenderFullTask(pageScale, renderFlags, d->m_originalSize.future().result(),
                                      d->m_page, d->m_documentHolder, interface));
OMP Education's avatar
OMP Education включено в состав коммита
186
187
188
    auto addingResult = PdfTaskQueue::instance().addTask(task, PdfTaskQueue::TaskPriority::Low,
                                                         d->m_documentHolder->id());
    if (!addingResult) {
OMP Education's avatar
OMP Education включено в состав коммита
189
        task.reset();
OMP Education's avatar
OMP Education включено в состав коммита
190
191
192
193
194
195
196
197
        return {};
    }

    interface.reportStarted();

    return future;
}

OMP Education's avatar
OMP Education включено в состав коммита
198
QFuture<QImage *> PdfPage::bitmapPart(qreal pageScaleX, qreal pageScaleY, int renderFlags, qreal zoom,
OMP Education's avatar
OMP Education включено в состав коммита
199
200
201
202
203
204
205
206
                                    const QPointF &bias) const
{
    if (!d->m_documentHolder || !d->m_page)
        return {};

    if (!d->m_originalSize.isFinished())
        return {};

OMP Education's avatar
OMP Education включено в состав коммита
207
    QFutureInterface<QImage *> interface;
OMP Education's avatar
OMP Education включено в состав коммита
208
209
    const auto &future = interface.future();

OMP Education's avatar
OMP Education включено в состав коммита
210
    auto task = QSharedPointer<PdfTask>(new PdfPageRenderPartTask(pageScaleX, pageScaleY, renderFlags, zoom, bias,
OMP Education's avatar
OMP Education включено в состав коммита
211
                                           d->m_originalSize.future().result(), d->m_page,
OMP Education's avatar
OMP Education включено в состав коммита
212
213
                                           d->m_documentHolder, interface));
    auto addingResult = PdfTaskQueue::instance().addTask(task, PdfTaskQueue::TaskPriority::High,
OMP Education's avatar
OMP Education включено в состав коммита
214
215
                                                         d->m_documentHolder->id());
    if (!addingResult) {
OMP Education's avatar
OMP Education включено в состав коммита
216
        task.reset();
OMP Education's avatar
OMP Education включено в состав коммита
217
218
219
220
221
222
223
224
        return {};
    }

    interface.reportStarted();

    return future;
}

OMP Education's avatar
OMP Education включено в состав коммита
225
QFuture<QList<QSharedPointer<QObject>>> PdfPage::annotations()
OMP Education's avatar
OMP Education включено в состав коммита
226
227
228
229
{
    if (!d->m_documentHolder || !d->m_page)
        return {};

OMP Education's avatar
OMP Education включено в состав коммита
230
    QFutureInterface<QList<QSharedPointer<QObject>>> interface;
OMP Education's avatar
OMP Education включено в состав коммита
231
232
    const auto &future = interface.future();

OMP Education's avatar
OMP Education включено в состав коммита
233
234
    auto task = QSharedPointer<PdfTask>(new PdfPageAnnotationTask(d->m_page, QList<QSharedPointer<QObject>>(), interface,
                                           d->m_documentHolder));
OMP Education's avatar
OMP Education включено в состав коммита
235
236
237
    auto addingResult = PdfTaskQueue::instance().addTask(task, PdfTaskQueue::TaskPriority::High,
                                                         d->m_documentHolder->id());
    if (!addingResult) {
OMP Education's avatar
OMP Education включено в состав коммита
238
        task.reset();
OMP Education's avatar
OMP Education включено в состав коммита
239
240
241
242
243
244
245
246
247
248
249
250
251
252
        return {};
    }
    interface.reportStarted();

    return future;
}

QFuture<QList<QObject *>> PdfPage::words()
{
    if (!d->m_documentHolder || !d->m_page)
        return {};
    auto origSizeF = originalSize();
    origSizeF.waitForFinished();
    auto origSize = origSizeF.result();
OMP Education's avatar
OMP Education включено в состав коммита
253
254
255
    auto task =
            QSharedPointer<PdfTask>(new PdfPageWordsTask(d->m_page, QList<QObject *>(), origSize,
                                 d->m_words, d->m_documentHolder));
OMP Education's avatar
OMP Education включено в состав коммита
256
257
258
    auto addingResult = PdfTaskQueue::instance().addTask(task, PdfTaskQueue::TaskPriority::High,
                                                         d->m_documentHolder->id());
    if (!addingResult) {
OMP Education's avatar
OMP Education включено в состав коммита
259
        task.reset();
OMP Education's avatar
OMP Education включено в состав коммита
260
261
262
263
264
265
266
267
        return {};
    }

    d->m_words.reportStarted();
    return d->m_words.future();
}

QFuture<bool> PdfPage::addAnnotation(const QRectF &rect, const QColor &color, const QString &author,
OMP Education's avatar
OMP Education включено в состав коммита
268
269
                                     const QString &content, const QString &annotationType,
                                     const QList<QRectF> &attachedPoints)
OMP Education's avatar
OMP Education включено в состав коммита
270
271
272
273
274
275
276
{
    if (!d->m_documentHolder || !d->m_page)
        return {};

    QFutureInterface<bool> interface;
    const auto &future = interface.future();

OMP Education's avatar
OMP Education включено в состав коммита
277
278
    PdfPageAddAnnotationTask::NewAnnotation newAnnotation{ rect, color, author, content,
                                                           annotationType, attachedPoints };
OMP Education's avatar
OMP Education включено в состав коммита
279

OMP Education's avatar
OMP Education включено в состав коммита
280
281
    auto task =
            QSharedPointer<PdfTask>(new PdfPageAddAnnotationTask(d->m_page, newAnnotation, interface, d->m_documentHolder));
OMP Education's avatar
OMP Education включено в состав коммита
282
283
284
    auto addingResult = PdfTaskQueue::instance().addTask(task, PdfTaskQueue::TaskPriority::High,
                                                         d->m_documentHolder->id());
    if (!addingResult) {
OMP Education's avatar
OMP Education включено в состав коммита
285
        task.reset();
OMP Education's avatar
OMP Education включено в состав коммита
286
287
288
289
290
291
        return {};
    }
    interface.reportStarted();
    return future;
}

OMP Education's avatar
OMP Education включено в состав коммита
292
QFuture<bool> PdfPage::drawInkAnnotation(const QRectF &rect, const QColor &color, const int penSize,
OMP Education's avatar
OMP Education включено в состав коммита
293
                                              const QList<QList<QPointF>> &points,
OMP Education's avatar
OMP Education включено в состав коммита
294
                                              const QString &author,
OMP Education's avatar
OMP Education включено в состав коммита
295
296
297
298
299
300
301
302
                                              const QString &content, const int annotationType)
{
    if (!d->m_documentHolder || !d->m_page)
        return {};

    QFutureInterface<bool> interface;
    const auto &future = interface.future();

OMP Education's avatar
OMP Education включено в состав коммита
303
304
    PdfPageAddInkAnnotationTask::InkAnnotation newAnnotation{ rect, color, author, content, points, penSize };

OMP Education's avatar
OMP Education включено в состав коммита
305
306
    auto task = QSharedPointer<PdfTask>(new PdfPageAddInkAnnotationTask(d->m_page, newAnnotation, interface,
                                              d->m_documentHolder));
OMP Education's avatar
OMP Education включено в состав коммита
307
308
309
    auto addingResult = PdfTaskQueue::instance().addTask(task, PdfTaskQueue::TaskPriority::High,
                                                         d->m_documentHolder->id());
    if (!addingResult) {
OMP Education's avatar
OMP Education включено в состав коммита
310
        task.reset();
OMP Education's avatar
OMP Education включено в состав коммита
311
312
313
314
315
        return {};
    }
    interface.reportStarted();
    return future;

OMP Education's avatar
OMP Education включено в состав коммита
316
    if (annotationType == Highlight) {
OMP Education's avatar
OMP Education включено в состав коммита
317
        PdfPageAddAnnotationTask::NewAnnotation newAnnotation{ rect, color, author, content, "Highlight", {} };
OMP Education's avatar
OMP Education включено в состав коммита
318

OMP Education's avatar
OMP Education включено в состав коммита
319
320
        auto task = QSharedPointer<PdfTask>(new PdfPageAddAnnotationTask(d->m_page, newAnnotation, interface,
                                                  d->m_documentHolder));
OMP Education's avatar
OMP Education включено в состав коммита
321
322
323
        auto addingResult = PdfTaskQueue::instance().addTask(task, PdfTaskQueue::TaskPriority::High,
                                                             d->m_documentHolder->id());
        if (!addingResult) {
OMP Education's avatar
OMP Education включено в состав коммита
324
            task.reset();
OMP Education's avatar
OMP Education включено в состав коммита
325
326
327
            return {};
        }
    } else if (annotationType == Ink) {
OMP Education's avatar
OMP Education включено в состав коммита
328
329
330
331
332
333
334
        QList<QPointF> pointfs;
        QList<QList<QPointF>> pointflist;
        for (int i = 0; i < points.first().size() ; i++) {
            pointfs.append(QPointF(points.first().at(i).x(), points.first().at(i).y()));
        }
        pointflist.append(pointfs);
        PdfPageDrawSolidPaths::Strokes strokes{ rect, color, points, penSize };
OMP Education's avatar
OMP Education включено в состав коммита
335

OMP Education's avatar
OMP Education включено в состав коммита
336
        auto task = QSharedPointer<PdfTask>(new PdfPageDrawSolidPaths(d->m_page, strokes, interface, d->m_documentHolder));
OMP Education's avatar
OMP Education включено в состав коммита
337
338
339
        auto addingResult = PdfTaskQueue::instance().addTask(task, PdfTaskQueue::TaskPriority::High,
                                                             d->m_documentHolder->id());
        if (!addingResult) {
OMP Education's avatar
OMP Education включено в состав коммита
340
            task.reset();
OMP Education's avatar
OMP Education включено в состав коммита
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
            return {};
        }
    }

    interface.reportStarted();
    return future;
}

QFuture<bool> PdfPage::removeAnnotation(int annotationIndex)
{
    if (!d->m_documentHolder || !d->m_page)
        return {};

    QFutureInterface<bool> interface;
    const auto &future = interface.future();

OMP Education's avatar
OMP Education включено в состав коммита
357
358
    auto task = QSharedPointer<PdfTask>(new PdfPageRemoveAnnotationTask(d->m_page, annotationIndex, interface,
                                                 d->m_documentHolder));
OMP Education's avatar
OMP Education включено в состав коммита
359
360
361
    auto addingResult = PdfTaskQueue::instance().addTask(task, PdfTaskQueue::TaskPriority::High,
                                                         d->m_documentHolder->id());
    if (!addingResult) {
OMP Education's avatar
OMP Education включено в состав коммита
362
        task.reset();
OMP Education's avatar
OMP Education включено в состав коммита
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
        return {};
    }

    interface.reportStarted();
    return future;
}

QFuture<bool> PdfPage::removeTextSearchResult()
{
    if (!d->m_documentHolder || !d->m_page)
        return {};

    QFutureInterface<bool> interface;
    const auto &future = interface.future();

OMP Education's avatar
OMP Education включено в состав коммита
378
379
    auto task = QSharedPointer<PdfTask>(new PdfPageRemoveSearchResultAnnotationsTask(d->m_page, interface,
                                                 d->m_documentHolder));
OMP Education's avatar
OMP Education включено в состав коммита
380
381
382
    auto addingResult = PdfTaskQueue::instance().addTask(task, PdfTaskQueue::TaskPriority::High,
                                                         d->m_documentHolder->id());
    if (!addingResult) {
OMP Education's avatar
OMP Education включено в состав коммита
383
        task.reset();
OMP Education's avatar
OMP Education включено в состав коммита
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
        return {};
    }

    interface.reportStarted();
    return future;
}

QFuture<bool> PdfPage::addImage(const QPointF &topLeft, const qreal &pageRate,
                                const QString &imagePath)
{
    if (!d->m_documentHolder || !d->m_page)
        return {};

    QFutureInterface<bool> interface;
    const auto &future = interface.future();

OMP Education's avatar
OMP Education включено в состав коммита
400
401
    auto task = QSharedPointer<PdfTask>(new PdfPageAddImageTask(d->m_page, imagePath, topLeft, pageRate, interface,
                                         d->m_documentHolder));
OMP Education's avatar
OMP Education включено в состав коммита
402
403
404
    auto addingResult = PdfTaskQueue::instance().addTask(task, PdfTaskQueue::TaskPriority::High,
                                                         d->m_documentHolder->id());
    if (!addingResult) {
OMP Education's avatar
OMP Education включено в состав коммита
405
        task.reset();
OMP Education's avatar
OMP Education включено в состав коммита
406
407
408
409
410
411
        return {};
    }

    interface.reportStarted();
    return future;
}
OMP Education's avatar
OMP Education включено в состав коммита
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483

QFuture<bool> PdfPage::highlightText(QPoint startPoint, QPoint endPoint, QColor color)
{
    QList<QRectF> attachedPoints = findTextRects(startPoint, endPoint);
    if (attachedPoints.isEmpty())
        return {};

    QRectF globalRect = attachedPoints.at(0);

    for (int i = 1; i < attachedPoints.size(); i++) {
        globalRect.setLeft(qMin(attachedPoints[i].left(), globalRect.left()));
        globalRect.setRight(qMax(attachedPoints[i].right(), globalRect.right()));
        globalRect.setTop(qMin(attachedPoints[i].top(), globalRect.top()));
        globalRect.setBottom(qMax(attachedPoints[i].bottom(), globalRect.bottom()));
    }

    return addAnnotation(globalRect, color, "defaultuser", "", "HighLight", attachedPoints);
}

QList<QRectF> PdfPage::findTextRects(QPoint startPoint, QPoint endPoint)
{
    auto *textPage = FPDFText_LoadPage(d->m_page.data());
    if (!textPage) {
        return {};
    }

    double pageHeight = FPDF_GetPageHeight(d->m_page.data());
    double pageWidth = FPDF_GetPageWidth(d->m_page.data());
    int startIndex = FPDFText_GetCharIndexAtPos(textPage, startPoint.x(), pageHeight - startPoint.y(), TEXT_FIND_X_TOLERANCE, TEXT_FIND_Y_TOLERANCE);
    int endIndex = FPDFText_GetCharIndexAtPos(textPage, endPoint.x(), pageHeight - endPoint.y(), pageWidth, pageHeight);

    if (startIndex < 0)
        return {};
    if (endIndex < 0)
        return {};

    if (startIndex > endIndex)
        qSwap(startIndex, endIndex);

    QRectF attachedRect;
    double left, right, top, bottom;
    double maxTop = 0.0;
    double minBottom = pageHeight;
    bool isFirstRowChar = true;
    QString str;
    QList<QRectF> attachedPoints;

    for (int i = startIndex; i < endIndex; i++) {
        FPDFText_GetCharBox(textPage, i, &left, &right, &bottom, &top);
        char sym = FPDFText_GetUnicode(textPage, i);

        maxTop = qMax(maxTop, top);
        minBottom = qMin(minBottom, bottom);

        if (sym == '\n' || i == endIndex - 1) {
            attachedRect.setRight(right);
            attachedRect.setBottom(maxTop);
            attachedRect.setTop(minBottom);
            attachedRect.setHeight(maxTop - minBottom);
            attachedPoints.append(attachedRect);
            isFirstRowChar = true;
        } else if (isFirstRowChar) {
            attachedRect.setLeft(left);
            isFirstRowChar = false;
            maxTop = 0.0;
            minBottom = pageHeight;
        }

        str += sym;
    }
    return attachedPoints;
}