Okular

page.cpp
1/*
2 SPDX-FileCopyrightText: 2004 Enrico Ros <eros.kde@email.it>
3
4 Work sponsored by the LiMux project of the city of Munich:
5 SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB a KDAB Group company <info@kdab.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include "page.h"
11#include "page_p.h"
12
13// qt/kde includes
14#include <QDomDocument>
15#include <QDomElement>
16#include <QHash>
17#include <QPixmap>
18#include <QSet>
19#include <QString>
20#include <QUuid>
21#include <QVariant>
22
23#include <QDebug>
24
25// local includes
26#include "action.h"
27#include "annotations.h"
28#include "annotations_p.h"
29#include "debug_p.h"
30#include "document.h"
31#include "document_p.h"
32#include "form.h"
33#include "form_p.h"
34#include "observer.h"
35#include "pagecontroller_p.h"
36#include "pagesize.h"
37#include "pagetransition.h"
38#include "rotationjob_p.h"
39#include "textpage_p.h"
40#include "tile.h"
41#include "tilesmanager_p.h"
42#include "utils_p.h"
43
44#include <limits>
45
46#ifdef PAGE_PROFILE
47#include <QTime>
48#endif
49
50using namespace Okular;
51
52static const double distanceConsideredEqual = 25; // 5px
53
54static void deleteObjectRects(QList<ObjectRect *> &rects, const QSet<ObjectRect::ObjectType> &which)
55{
57 for (; it != rects.end();) {
58 if (which.contains((*it)->objectType())) {
59 delete *it;
60 it = rects.erase(it);
61 } else {
62 ++it;
63 }
64 }
65}
66
67PagePrivate::PagePrivate(Page *page, uint n, double w, double h, Rotation o)
68 : m_page(page)
69 , m_number(n)
70 , m_orientation(o)
71 , m_width(w)
72 , m_height(h)
73 , m_doc(nullptr)
74 , m_boundingBox(0, 0, 1, 1)
75 , m_rotation(Rotation0)
76 , m_text(nullptr)
77 , m_transition(nullptr)
78 , m_textSelections(nullptr)
79 , m_openingAction(nullptr)
80 , m_closingAction(nullptr)
81 , m_duration(-1)
82 , m_isBoundingBoxKnown(false)
83{
84 // avoid Division-By-Zero problems in the program
85 if (m_width <= 0) {
86 m_width = 1;
87 }
88
89 if (m_height <= 0) {
90 m_height = 1;
91 }
92}
93
94PagePrivate::~PagePrivate()
95{
96 qDeleteAll(formfields);
97 delete m_openingAction;
98 delete m_closingAction;
99 delete m_text;
100 delete m_transition;
101}
102
103PagePrivate *PagePrivate::get(Page *page)
104{
105 return page ? page->d : nullptr;
106}
107
108void PagePrivate::imageRotationDone(RotationJob *job)
109{
110 TilesManager *tm = tilesManager(job->observer());
111 if (tm) {
112 QPixmap *pixmap = new QPixmap(QPixmap::fromImage(job->image()));
113 tm->setPixmap(pixmap, job->rect(), job->isPartialUpdate());
114 delete pixmap;
115 return;
116 }
117
118 QMap<DocumentObserver *, PixmapObject>::iterator it = m_pixmaps.find(job->observer());
119 if (it != m_pixmaps.end()) {
120 PixmapObject &object = it.value();
121 (*object.m_pixmap) = QPixmap::fromImage(job->image());
122 object.m_rotation = job->rotation();
123 object.m_isPartialPixmap = job->isPartialUpdate();
124 } else {
125 PixmapObject object;
126 object.m_pixmap = new QPixmap(QPixmap::fromImage(job->image()));
127 object.m_rotation = job->rotation();
128 object.m_isPartialPixmap = job->isPartialUpdate();
129
130 m_pixmaps.insert(job->observer(), object);
131 }
132}
133
134QTransform PagePrivate::rotationMatrix() const
135{
136 return Okular::buildRotationMatrix(m_rotation);
137}
138
139/** class Page **/
140
141Page::Page(uint pageNumber, double w, double h, Rotation o)
142 : d(new PagePrivate(this, pageNumber, w, h, o))
143{
144}
145
147{
148 if (d) {
150 deleteRects();
151 d->deleteHighlights();
153 d->deleteTextSelections();
155
156 delete d;
157 }
158}
159
160int Page::number() const
161{
162 return d->m_number;
163}
164
166{
167 return d->m_orientation;
168}
169
171{
172 return d->m_rotation;
173}
174
176{
177 return (Rotation)(((int)d->m_orientation + (int)d->m_rotation) % 4);
178}
179
180double Page::width() const
181{
182 return d->m_width;
183}
184
185double Page::height() const
186{
187 return d->m_height;
188}
189
190double Page::ratio() const
191{
192 return d->m_height / d->m_width;
193}
194
196{
197 return d->m_boundingBox;
198}
199
201{
202 return d->m_isBoundingBoxKnown;
203}
204
206{
207 if (d->m_isBoundingBoxKnown && d->m_boundingBox == bbox) {
208 return;
209 }
210
211 // Allow tiny rounding errors (happens during rotation)
212 static const double epsilon = 0.00001;
213 Q_ASSERT(bbox.left >= -epsilon && bbox.top >= -epsilon && bbox.right <= 1 + epsilon && bbox.bottom <= 1 + epsilon);
214
215 d->m_boundingBox = bbox & NormalizedRect(0., 0., 1., 1.);
216 d->m_isBoundingBoxKnown = true;
217}
218
219bool Page::hasPixmap(DocumentObserver *observer, int width, int height, const NormalizedRect &rect) const
220{
221 TilesManager *tm = d->tilesManager(observer);
222 if (tm) {
223 if (width != tm->width() || height != tm->height()) {
224 return false;
225 }
226
227 return tm->hasPixmap(rect);
228 }
229
231 if (it == d->m_pixmaps.constEnd()) {
232 return false;
233 }
234
235 if (width == -1 || height == -1) {
236 return true;
237 }
238
239 if (it.value().m_isPartialPixmap) {
240 return false;
241 }
242
243 const QPixmap *pixmap = it.value().m_pixmap;
244
245 return (pixmap->width() == width && pixmap->height() == height);
246}
247
248void Page::setPageSize(DocumentObserver *observer, int width, int height)
249{
250 TilesManager *tm = d->tilesManager(observer);
251 if (tm) {
252 tm->setSize(width, height);
253 }
254}
255
257{
258 return d->m_text != nullptr;
259}
260
261std::unique_ptr<RegularAreaRect> Page::wordAt(const NormalizedPoint &p) const
262{
263 if (d->m_text) {
264 return d->m_text->wordAt(p);
265 }
266
267 return nullptr;
268}
269
270std::unique_ptr<RegularAreaRect> Page::textArea(const TextSelection &selection) const
271{
272 if (d->m_text) {
273 return d->m_text->textArea(selection);
274 }
275
276 return nullptr;
277}
278
279bool Page::hasObjectRect(double x, double y, double xScale, double yScale) const
280{
281 if (m_rects.isEmpty()) {
282 return false;
283 }
284
285 for (ObjectRect *rect : m_rects) {
286 if (rect->distanceSqr(x, y, xScale, yScale) < distanceConsideredEqual) {
287 return true;
288 }
289 }
290
291 return false;
292}
293
294bool Page::hasHighlights(int s_id) const
295{
296 // simple case: have no highlights
297 if (m_highlights.isEmpty()) {
298 return false;
299 }
300 // simple case: we have highlights and no id to match
301 if (s_id == -1) {
302 return true;
303 }
304 // iterate on the highlights list to find an entry by id
305 for (HighlightAreaRect *highlight : m_highlights) {
306 if (highlight->s_id == s_id) {
307 return true;
308 }
309 }
310 return false;
311}
312
314{
315 return d->m_transition != nullptr;
316}
317
319{
320 return !m_annotations.isEmpty();
321}
322
323RegularAreaRect *Page::findText(int id, const QString &text, SearchDirection direction, Qt::CaseSensitivity caseSensitivity, const RegularAreaRect *lastRect) const
324{
325 RegularAreaRect *rect = nullptr;
326 if (text.isEmpty() || !d->m_text) {
327 return rect;
328 }
329
330 rect = d->m_text->findText(id, text, direction, caseSensitivity, lastRect);
331 return rect;
332}
333
338
340{
341 QString ret;
342
343 if (!d->m_text) {
344 return ret;
345 }
346
347 if (area) {
348 RegularAreaRect rotatedArea = *area;
349 rotatedArea.transform(d->rotationMatrix().inverted());
350
351 ret = d->m_text->text(&rotatedArea, b);
352 } else {
353 ret = d->m_text->text(nullptr, b);
354 }
355
356 return ret;
357}
358
360{
362
363 if (!d->m_text) {
364 return ret;
365 }
366
367 if (area) {
368 RegularAreaRect rotatedArea = *area;
369 rotatedArea.transform(d->rotationMatrix().inverted());
370
371 ret = d->m_text->words(&rotatedArea, b);
372 } else {
373 ret = d->m_text->words(nullptr, b);
374 }
375
376 for (auto &retI : ret) {
377 retI = TextEntity(retI.text(), Okular::NormalizedRect(retI.transformedArea(d->rotationMatrix())));
378 }
379
380 return ret;
381}
382
383void PagePrivate::rotateAt(Rotation orientation)
384{
385 if (orientation == m_rotation) {
386 return;
387 }
388
389 deleteTextSelections();
390
391 if (((int)m_orientation + (int)m_rotation) % 2 != ((int)m_orientation + (int)orientation) % 2) {
392 std::swap(m_width, m_height);
393 }
394
395 Rotation oldRotation = m_rotation;
396 m_rotation = orientation;
397
398 /**
399 * Rotate the images of the page.
400 */
402 while (it.hasNext()) {
403 it.next();
404
405 const PagePrivate::PixmapObject &object = it.value();
406
407 RotationJob *job = new RotationJob(object.m_pixmap->toImage(), object.m_rotation, m_rotation, it.key());
408 job->setPage(this);
409 m_doc->m_pageController->addRotationJob(job);
410 }
411
412 /**
413 * Rotate tiles manager
414 */
416 while (i.hasNext()) {
417 i.next();
418
419 TilesManager *tm = i.value();
420 if (tm) {
421 tm->setRotation(m_rotation);
422 }
423 }
424
425 /**
426 * Rotate the object rects on the page.
427 */
428 const QTransform matrix = rotationMatrix();
429 for (ObjectRect *objRect : std::as_const(m_page->m_rects)) {
430 objRect->transform(matrix);
431 }
432
433 const QTransform highlightRotationMatrix = Okular::buildRotationMatrix((Rotation)(((int)m_rotation - (int)oldRotation + 4) % 4));
434 for (HighlightAreaRect *hlar : std::as_const(m_page->m_highlights)) {
435 hlar->transform(highlightRotationMatrix);
436 }
437}
438
439void PagePrivate::changeSize(const PageSize &size)
440{
441 if (size.isNull() || (size.width() == m_width && size.height() == m_height)) {
442 return;
443 }
444
445 m_page->deletePixmaps();
446 // deleteHighlights();
447 // deleteTextSelections();
448
449 m_width = size.width();
450 m_height = size.height();
451 if (m_rotation % 2) {
452 std::swap(m_width, m_height);
453 }
454}
455
456const ObjectRect *Page::objectRect(ObjectRect::ObjectType type, double x, double y, double xScale, double yScale) const
457{
458 // Walk list in reverse order so that annotations in the foreground are preferred
459 QListIterator<ObjectRect *> it(m_rects);
460 it.toBack();
461 while (it.hasPrevious()) {
462 const ObjectRect *objrect = it.previous();
463 if ((objrect->objectType() == type) && objrect->distanceSqr(x, y, xScale, yScale) < distanceConsideredEqual) {
464 return objrect;
465 }
466 }
467
468 return nullptr;
469}
470
471QList<const ObjectRect *> Page::objectRects(ObjectRect::ObjectType type, double x, double y, double xScale, double yScale) const
472{
474
475 QListIterator<ObjectRect *> it(m_rects);
476 it.toBack();
477 while (it.hasPrevious()) {
478 const ObjectRect *objrect = it.previous();
479 if ((objrect->objectType() == type) && objrect->distanceSqr(x, y, xScale, yScale) < distanceConsideredEqual) {
480 result.append(objrect);
481 }
482 }
483
484 return result;
485}
486
487const ObjectRect *Page::nearestObjectRect(ObjectRect::ObjectType type, double x, double y, double xScale, double yScale, double *distance) const
488{
489 ObjectRect *res = nullptr;
490 double minDistance = std::numeric_limits<double>::max();
491
492 for (ObjectRect *rect : m_rects) {
493 if (rect->objectType() == type) {
494 double d = rect->distanceSqr(x, y, xScale, yScale);
495 if (d < minDistance) {
496 res = rect;
497 minDistance = d;
498 }
499 }
500 }
501
502 if (distance) {
503 *distance = minDistance;
504 }
505 return res;
506}
507
509{
510 return d->m_transition;
511}
512
514{
515 return m_annotations;
516}
517
518Annotation *Page::annotation(const QString &uniqueName) const
519{
520 for (Annotation *a : m_annotations) {
521 if (a->uniqueName() == uniqueName) {
522 return a;
523 }
524 }
525 return nullptr;
526}
527
529{
530 switch (action) {
531 case Page::Opening:
532 return d->m_openingAction;
533 break;
534 case Page::Closing:
535 return d->m_closingAction;
536 break;
537 }
538
539 return nullptr;
540}
541
543{
544 return d->formfields;
545}
546
547void Page::setPixmap(DocumentObserver *observer, QPixmap *pixmap, const NormalizedRect &rect)
548{
549 d->setPixmap(observer, pixmap, rect, false /*isPartialPixmap*/);
550}
551
552void PagePrivate::setPixmap(DocumentObserver *observer, QPixmap *pixmap, const NormalizedRect &rect, bool isPartialPixmap)
553{
554 if (m_rotation == Rotation0) {
555 TilesManager *tm = tilesManager(observer);
556 if (tm) {
557 tm->setPixmap(pixmap, rect, isPartialPixmap);
558 delete pixmap;
559 return;
560 }
561
563 if (it != m_pixmaps.end()) {
564 delete it.value().m_pixmap;
565 } else {
566 it = m_pixmaps.insert(observer, PagePrivate::PixmapObject());
567 }
568 it.value().m_pixmap = pixmap;
569 it.value().m_rotation = m_rotation;
570 it.value().m_isPartialPixmap = isPartialPixmap;
571 } else {
572 // it can happen that we get a setPixmap while closing and thus the page controller is gone
573 if (m_doc->m_pageController) {
574 RotationJob *job = new RotationJob(pixmap->toImage(), Rotation0, m_rotation, observer);
575 job->setPage(this);
576 job->setRect(TilesManager::toRotatedRect(rect, m_rotation));
577 job->setIsPartialUpdate(isPartialPixmap);
578 m_doc->m_pageController->addRotationJob(job);
579 }
580
581 delete pixmap;
582 }
583}
584
586{
587 delete d->m_text;
588
589 d->m_text = textPage;
590 if (d->m_text) {
591 d->m_text->d->m_page = this;
592 // Correct/optimize text order for search and text selection
593 d->m_text->d->correctTextOrder();
594 }
595}
596
598{
601 deleteObjectRects(m_rects, which);
602
603 /**
604 * Rotate the object rects of the page.
605 */
606 const QTransform matrix = d->rotationMatrix();
607
608 for (ObjectRect *objectRect : rects) {
609 objectRect->transform(matrix);
610 }
611
612 m_rects << rects;
613}
614
616{
617 return m_rects;
618}
619
620void PagePrivate::setHighlight(int s_id, RegularAreaRect *rect, const QColor &color)
621{
622 HighlightAreaRect *hr = new HighlightAreaRect(rect);
623 hr->s_id = s_id;
624 hr->color = color;
625
626 m_page->m_highlights.append(hr);
627}
628
629void PagePrivate::setTextSelections(const RegularAreaRect &r, const QColor &color)
630{
631 deleteTextSelections();
633 hr->s_id = -1;
634 hr->color = color;
635 m_textSelections = hr;
636}
637
639{
641 for (SourceRefObjectRect *rect : refRects) {
642 m_rects << rect;
643 }
644}
645
646void Page::setDuration(double seconds)
647{
648 d->m_duration = seconds;
649}
650
651double Page::duration() const
652{
653 return d->m_duration;
654}
655
656void Page::setLabel(const QString &label)
657{
658 d->m_label = label;
659}
660
662{
663 return d->m_label;
664}
665
667{
668 return d->m_textSelections;
669}
670
672{
673 return d->m_textSelections ? d->m_textSelections->color : QColor();
674}
675
677{
678 // Generate uniqueName: okular-{UUID}
679 if (annotation->uniqueName().isEmpty()) {
680 QString uniqueName = QStringLiteral("okular-") + QUuid::createUuid().toString();
681 annotation->setUniqueName(uniqueName);
682 }
683 annotation->d_ptr->m_page = d;
684 m_annotations.append(annotation);
685
687
688 // Rotate the annotation on the page.
689 const QTransform matrix = d->rotationMatrix();
690 annotation->d_ptr->annotationTransform(matrix);
691
692 m_rects.append(rect);
693}
694
696{
697 if (!d->m_doc->m_parent->canRemovePageAnnotation(annotation)) {
698 return false;
699 }
700
701 QList<Annotation *>::iterator aIt = m_annotations.begin();
702 for (; aIt != m_annotations.end(); ++aIt) {
703 if ((*aIt) && (*aIt)->uniqueName() == annotation->uniqueName()) {
704 int rectfound = false;
705 QList<ObjectRect *>::iterator it = m_rects.begin();
706 for (; it != m_rects.end() && !rectfound; ++it) {
707 if (((*it)->objectType() == ObjectRect::OAnnotation) && ((*it)->object() == (*aIt))) {
708 delete *it;
709 it = m_rects.erase(it);
710 rectfound = true;
711 }
712 }
713 qCDebug(OkularCoreDebug) << "removed annotation:" << annotation->uniqueName();
714 annotation->d_ptr->m_page = nullptr;
715 m_annotations.erase(aIt);
716 break;
717 }
718 }
719
720 return true;
721}
722
724{
725 delete d->m_transition;
726 d->m_transition = transition;
727}
728
730{
731 switch (action) {
732 case Page::Opening:
733 delete d->m_openingAction;
734 d->m_openingAction = link;
735 break;
736 case Page::Closing:
737 delete d->m_closingAction;
738 d->m_closingAction = link;
739 break;
740 }
741}
742
744{
745 qDeleteAll(d->formfields);
746 d->formfields = fields;
747 for (FormField *ff : std::as_const(d->formfields)) {
748 ff->d_ptr->setDefault();
749 ff->d_ptr->m_page = this;
750 }
751}
752
754{
755 TilesManager *tm = d->tilesManager(observer);
756 if (tm) {
757 delete tm;
758 d->m_tilesManagers.remove(observer);
759 } else {
760 PagePrivate::PixmapObject object = d->m_pixmaps.take(observer);
761 delete object.m_pixmap;
762 }
763}
764
766{
768 while (it.hasNext()) {
769 it.next();
770 delete it.value().m_pixmap;
771 }
772
773 d->m_pixmaps.clear();
774
775 qDeleteAll(d->m_tilesManagers);
776 d->m_tilesManagers.clear();
777}
778
780{
781 // delete ObjectRects of type Link and Image
784 deleteObjectRects(m_rects, which);
785}
786
787void PagePrivate::deleteHighlights(int s_id)
788{
789 // delete highlights by ID
790 QList<HighlightAreaRect *>::iterator it = m_page->m_highlights.begin();
791 while (it != m_page->m_highlights.end()) {
792 HighlightAreaRect *highlight = *it;
793 if (s_id == -1 || highlight->s_id == s_id) {
794 it = m_page->m_highlights.erase(it);
795 delete highlight;
796 } else {
797 ++it;
798 }
799 }
800}
801
802void PagePrivate::deleteTextSelections()
803{
804 delete m_textSelections;
805 m_textSelections = nullptr;
806}
807
809{
810 deleteObjectRects(m_rects, QSet<ObjectRect::ObjectType>() << ObjectRect::SourceRef);
811}
812
814{
815 // delete ObjectRects of type Annotation
816 deleteObjectRects(m_rects, QSet<ObjectRect::ObjectType>() << ObjectRect::OAnnotation);
817 // delete all stored annotations
818 qDeleteAll(m_annotations);
819 m_annotations.clear();
820}
821
822bool PagePrivate::restoreLocalContents(const QDomNode &pageNode)
823{
824 bool loadedAnything = false; // set if something actually gets loaded
825
826 // iterate over all children (annotationList, ...)
827 QDomNode childNode = pageNode.firstChild();
828 while (childNode.isElement()) {
829 QDomElement childElement = childNode.toElement();
830 childNode = childNode.nextSibling();
831
832 // parse annotationList child element
833 if (childElement.tagName() == QLatin1String("annotationList")) {
834#ifdef PAGE_PROFILE
835 QTime time;
836 time.start();
837#endif
838 // Clone annotationList as root node in restoredLocalAnnotationList
839 const QDomNode clonedNode = restoredLocalAnnotationList.importNode(childElement, true);
840 restoredLocalAnnotationList.appendChild(clonedNode);
841
842 // iterate over all annotations
843 QDomNode annotationNode = childElement.firstChild();
844 while (annotationNode.isElement()) {
845 // get annotation element and advance to next annot
846 QDomElement annotElement = annotationNode.toElement();
847 annotationNode = annotationNode.nextSibling();
848
849 // get annotation from the dom element
850 Annotation *annotation = AnnotationUtils::createAnnotation(annotElement);
851
852 // append annotation to the list or show warning
853 if (annotation) {
854 m_doc->performAddPageAnnotation(m_number, annotation);
855 qCDebug(OkularCoreDebug) << "restored annot:" << annotation->uniqueName();
856 loadedAnything = true;
857 } else {
858 qCWarning(OkularCoreDebug).nospace() << "page (" << m_number << "): can't restore an annotation from XML.";
859 }
860 }
861#ifdef PAGE_PROFILE
862 qCDebug(OkularCoreDebug).nospace() << "annots: XML Load time: " << time.elapsed() << "ms";
863#endif
864 }
865 // parse formList child element
866 else if (childElement.tagName() == QLatin1String("forms")) {
867 // Clone forms as root node in restoredFormFieldList
868 const QDomNode clonedNode = restoredFormFieldList.importNode(childElement, true);
869 restoredFormFieldList.appendChild(clonedNode);
870
871 if (formfields.isEmpty()) {
872 continue;
873 }
874
875 QHash<int, FormField *> hashedforms;
876 for (FormField *ff : std::as_const(formfields)) {
877 hashedforms[ff->id()] = ff;
878 }
879
880 // iterate over all forms
881 QDomNode formsNode = childElement.firstChild();
882 while (formsNode.isElement()) {
883 // get annotation element and advance to next annot
884 QDomElement formElement = formsNode.toElement();
885 formsNode = formsNode.nextSibling();
886
887 if (formElement.tagName() != QLatin1String("form")) {
888 continue;
889 }
890
891 bool ok = true;
892 int index = formElement.attribute(QStringLiteral("id")).toInt(&ok);
893 if (!ok) {
894 continue;
895 }
896
897 QHash<int, FormField *>::const_iterator wantedIt = hashedforms.constFind(index);
898 if (wantedIt == hashedforms.constEnd()) {
899 continue;
900 }
901
902 QString value = formElement.attribute(QStringLiteral("value"));
903 (*wantedIt)->d_ptr->setValue(value);
904 loadedAnything = true;
905 }
906 }
907 }
908
909 return loadedAnything;
910}
911
912void PagePrivate::saveLocalContents(QDomNode &parentNode, QDomDocument &document, PageItems what) const
913{
914 // create the page node and set the 'number' attribute
915 QDomElement pageElement = document.createElement(QStringLiteral("page"));
916 pageElement.setAttribute(QStringLiteral("number"), m_number);
917
918 // add annotations info if has got any
919 if ((what & AnnotationPageItems) && (what & OriginalAnnotationPageItems)) {
920 const QDomElement savedDocRoot = restoredLocalAnnotationList.documentElement();
921 if (!savedDocRoot.isNull()) {
922 // Import and append node in target document
923 const QDomNode importedNode = document.importNode(savedDocRoot, true);
924 pageElement.appendChild(importedNode);
925 }
926 } else if ((what & AnnotationPageItems) && !m_page->m_annotations.isEmpty()) {
927 // create the annotationList
928 QDomElement annotListElement = document.createElement(QStringLiteral("annotationList"));
929
930 // add every annotation to the annotationList
931 for (const Annotation *a : std::as_const(m_page->m_annotations)) {
932 // only save okular annotations (not the embedded in file ones)
933 if (!(a->flags() & Annotation::External)) {
934 // append an filled-up element called 'annotation' to the list
935 QDomElement annElement = document.createElement(QStringLiteral("annotation"));
936 AnnotationUtils::storeAnnotation(a, annElement, document);
937 annotListElement.appendChild(annElement);
938 qCDebug(OkularCoreDebug) << "save annotation:" << a->uniqueName();
939 }
940 }
941
942 // append the annotationList element if annotations have been set
943 if (annotListElement.hasChildNodes()) {
944 pageElement.appendChild(annotListElement);
945 }
946 }
947
948 // add forms info if has got any
949 if ((what & FormFieldPageItems) && (what & OriginalFormFieldPageItems)) {
950 const QDomElement savedDocRoot = restoredFormFieldList.documentElement();
951 if (!savedDocRoot.isNull()) {
952 // Import and append node in target document
953 const QDomNode importedNode = document.importNode(savedDocRoot, true);
954 pageElement.appendChild(importedNode);
955 }
956 } else if ((what & FormFieldPageItems) && !formfields.isEmpty()) {
957 // create the formList
958 QDomElement formListElement = document.createElement(QStringLiteral("forms"));
959
960 // add every form data to the formList
961 for (const FormField *f : formfields) {
962 QString newvalue = f->d_ptr->value();
963 if (f->d_ptr->m_default == newvalue) {
964 continue;
965 }
966
967 // append an filled-up element called 'annotation' to the list
968 QDomElement formElement = document.createElement(QStringLiteral("form"));
969 formElement.setAttribute(QStringLiteral("id"), f->id());
970 formElement.setAttribute(QStringLiteral("value"), newvalue);
971 formListElement.appendChild(formElement);
972 }
973
974 // append the annotationList element if annotations have been set
975 if (formListElement.hasChildNodes()) {
976 pageElement.appendChild(formListElement);
977 }
978 }
979
980 // append the page element only if has children
981 if (pageElement.hasChildNodes()) {
982 parentNode.appendChild(pageElement);
983 }
984}
985
986const QPixmap *Page::_o_nearestPixmap(DocumentObserver *observer, int w, int h) const
987{
988 Q_UNUSED(h)
989
990 const QPixmap *pixmap = nullptr;
991
992 // if a pixmap is present for given id, use it
993 QMap<DocumentObserver *, PagePrivate::PixmapObject>::const_iterator itPixmap = d->m_pixmaps.constFind(observer);
994 if (itPixmap != d->m_pixmaps.constEnd()) {
995 pixmap = itPixmap.value().m_pixmap;
996 } else if (!d->m_pixmaps.isEmpty()) {
997 // else find the closest match using pixmaps of other IDs (great optim!)
998 int minDistance = -1;
999 QMap<DocumentObserver *, PagePrivate::PixmapObject>::const_iterator it = d->m_pixmaps.constBegin(), end = d->m_pixmaps.constEnd();
1000 for (; it != end; ++it) {
1001 int pixWidth = (*it).m_pixmap->width(), distance = pixWidth > w ? pixWidth - w : w - pixWidth;
1002 if (minDistance == -1 || distance < minDistance) {
1003 pixmap = (*it).m_pixmap;
1004 minDistance = distance;
1005 }
1006 }
1007 }
1008
1009 return pixmap;
1010}
1011
1012bool Page::hasTilesManager(const DocumentObserver *observer) const
1013{
1014 return d->tilesManager(observer) != nullptr;
1015}
1016
1018{
1019 TilesManager *tm = d->m_tilesManagers.value(observer);
1020 if (tm) {
1021 return tm->tilesAt(rect, TilesManager::PixmapTile);
1022 } else {
1023 return QList<Tile>();
1024 }
1025}
1026
1027TilesManager *PagePrivate::tilesManager(const DocumentObserver *observer) const
1028{
1029 return m_tilesManagers.value(observer);
1030}
1031
1032void PagePrivate::setTilesManager(const DocumentObserver *observer, TilesManager *tm)
1033{
1034 TilesManager *old = m_tilesManagers.value(observer);
1035 delete old;
1036
1037 m_tilesManagers.insert(observer, tm);
1038}
1039
1040void PagePrivate::adoptGeneratedContents(PagePrivate *oldPage)
1041{
1042 rotateAt(oldPage->m_rotation);
1043
1044 m_pixmaps = oldPage->m_pixmaps;
1045 oldPage->m_pixmaps.clear();
1046
1047 m_tilesManagers = oldPage->m_tilesManagers;
1048 oldPage->m_tilesManagers.clear();
1049
1050 m_boundingBox = oldPage->m_boundingBox;
1051 m_isBoundingBoxKnown = oldPage->m_isBoundingBoxKnown;
1052 m_text = oldPage->m_text;
1053 oldPage->m_text = nullptr;
1054
1055 m_textSelections = oldPage->m_textSelections;
1056 oldPage->m_textSelections = nullptr;
1057
1058 restoredLocalAnnotationList = oldPage->restoredLocalAnnotationList;
1059 restoredFormFieldList = oldPage->restoredFormFieldList;
1060}
1061
1062FormField *PagePrivate::findEquivalentForm(const Page *p, FormField *oldField)
1063{
1064 // given how id is not very good of id (at least for pdf) we do a few passes
1065 // same rect, type and id
1066 for (FormField *f : std::as_const(p->d->formfields)) {
1067 if (f->rect() == oldField->rect() && f->type() == oldField->type() && f->id() == oldField->id()) {
1068 return f;
1069 }
1070 }
1071 // same rect and type
1072 for (FormField *f : std::as_const(p->d->formfields)) {
1073 if (f->rect() == oldField->rect() && f->type() == oldField->type()) {
1074 return f;
1075 }
1076 }
1077 // fuzzy rect, same type and id
1078 for (FormField *f : std::as_const(p->d->formfields)) {
1079 if (f->type() == oldField->type() && f->id() == oldField->id() && qFuzzyCompare(f->rect().left, oldField->rect().left) && qFuzzyCompare(f->rect().top, oldField->rect().top) &&
1080 qFuzzyCompare(f->rect().right, oldField->rect().right) && qFuzzyCompare(f->rect().bottom, oldField->rect().bottom)) {
1081 return f;
1082 }
1083 }
1084 // fuzzy rect and same type
1085 for (FormField *f : std::as_const(p->d->formfields)) {
1086 if (f->type() == oldField->type() && qFuzzyCompare(f->rect().left, oldField->rect().left) && qFuzzyCompare(f->rect().top, oldField->rect().top) && qFuzzyCompare(f->rect().right, oldField->rect().right) &&
1087 qFuzzyCompare(f->rect().bottom, oldField->rect().bottom)) {
1088 return f;
1089 }
1090 }
1091 return nullptr;
1092}
Encapsulates data that describes an action.
Definition action.h:41
This class describes the object rectangle for an annotation.
Definition area.h:556
static Annotation * createAnnotation(const QDomElement &element)
Restore an annotation (with revisions if needed) from the dom element.
static void storeAnnotation(const Annotation *annotation, QDomElement &element, QDomDocument &document)
Saves the annotation as a child of element taking care of saving all revisions if it has any.
Annotation struct holds properties shared by all annotations.
Definition annotations.h:96
@ External
Is stored external.
QString uniqueName() const
Returns the unique name of the annotation.
void setUniqueName(const QString &name)
Sets the unique name of the annotation.
Base class for objects being notified when something changes.
Definition observer.h:29
The base interface of a form field.
Definition form.h:40
FieldType type() const
The type of the field.
Definition form.cpp:47
virtual NormalizedRect rect() const =0
The bounding rect of the field, in normalized coordinates.
virtual int id() const =0
The ID of the field.
This class stores the geometry of a highlighting area in normalized coordinates, together with highli...
Definition area.h:951
int s_id
The search ID of the highlight owner.
Definition area.h:962
QColor color
The color of the highlight.
Definition area.h:967
NormalizedPoint is a helper class which stores the coordinates of a normalized point.
Definition area.h:117
A NormalizedRect is a rectangle which can be defined by two NormalizedPoints.
Definition area.h:189
double bottom
The normalized bottom coordinate.
Definition area.h:435
double right
The normalized right coordinate.
Definition area.h:430
double left
The normalized left coordinate.
Definition area.h:420
double top
The normalized top coordinate.
Definition area.h:425
An area with normalized coordinates that contains a reference to an object.
Definition area.h:458
ObjectType objectType() const
Returns the object type of the object rectangle.
Definition area.cpp:352
ObjectType
Describes the type of storable object.
Definition area.h:463
@ Image
An image.
Definition area.h:465
@ Action
An action.
Definition area.h:464
@ OAnnotation
An annotation.
Definition area.h:466
@ SourceRef
A source reference.
Definition area.h:467
double distanceSqr(double x, double y, double xScale, double yScale) const
Returns the squared distance between the object and the point with normalized coordinates (x,...
Definition area.cpp:384
virtual void transform(const QTransform &matrix)
Transforms the object rectangle with the operations defined by matrix.
Definition area.cpp:379
A small class that represents the size of a page.
Definition pagesize.h:24
double width() const
Returns the width of the page size.
Definition pagesize.cpp:52
double height() const
Returns the height of the page size.
Definition pagesize.cpp:61
bool isNull() const
Whether the page size is null.
Definition pagesize.cpp:79
Information object for the transition effect of a page.
Rotation orientation() const
Returns the orientation of the page as defined by the document.
Definition page.cpp:165
void deletePixmap(DocumentObserver *observer)
Deletes the pixmap for the given observer.
Definition page.cpp:753
RegularAreaRect * findText(int id, const QString &text, SearchDirection direction, Qt::CaseSensitivity caseSensitivity, const RegularAreaRect *lastRect=nullptr) const
Returns the bounding rect of the text which matches the following criteria or 0 if the search is not ...
Definition page.cpp:323
double height() const
Returns the height of the page.
Definition page.cpp:185
bool hasTilesManager(const DocumentObserver *observer) const
Returns whether pixmaps for the tiled observer are handled by a tile manager.
Definition page.cpp:1012
~Page()
Destroys the page.
Definition page.cpp:146
void setTextPage(TextPage *text)
Sets the text page.
Definition page.cpp:585
bool hasTransition() const
Returns whether the page provides a transition effect.
Definition page.cpp:313
Rotation totalOrientation() const
Returns the total orientation which is the original orientation plus the user defined rotation.
Definition page.cpp:175
Annotation * annotation(const QString &uniqueName) const
Returns the annotation with the given unique name.
Definition page.cpp:518
bool hasObjectRect(double x, double y, double xScale, double yScale) const
Returns whether the page has an object rect which includes the point (x, y) at scale (xScale,...
Definition page.cpp:279
const Action * pageAction(PageAction action) const
Returns the Action object which is associated with the given page action or 0 if no page action is se...
Definition page.cpp:528
const QList< ObjectRect * > & objectRects() const
Gets the list of object rects of the page.
Definition page.cpp:615
NormalizedRect boundingBox() const
Returns the bounding box of the page content in normalized [0,1] coordinates, in terms of the upright...
Definition page.cpp:195
void setTransition(PageTransition *transition)
Sets the page transition effect.
Definition page.cpp:723
void deleteSourceReferences()
Deletes all source reference objects of the page.
Definition page.cpp:808
int number() const
Returns the number of the page in the document.
Definition page.cpp:160
bool removeAnnotation(Annotation *annotation)
Removes the annotation from the page.
Definition page.cpp:695
std::unique_ptr< RegularAreaRect > wordAt(const NormalizedPoint &p) const
Returns the area and text of the word at the given point Note that ownership of the returned area bel...
Definition page.cpp:261
double ratio() const
Returns the ration (height / width) of the page.
Definition page.cpp:190
void deleteAnnotations()
Deletes all annotations of the page.
Definition page.cpp:813
const PageTransition * transition() const
Returns the transition effect of the page or 0 if no transition effect is set (see hasTransition()).
Definition page.cpp:508
void deletePixmaps()
Deletes all pixmaps of the page.
Definition page.cpp:765
PageAction
An action to be executed when particular events happen.
Definition page.h:53
@ Opening
An action to be executed when the page is "opened".
Definition page.h:54
@ Closing
An action to be executed when the page is "closed".
Definition page.h:55
const ObjectRect * objectRect(ObjectRect::ObjectType type, double x, double y, double xScale, double yScale) const
Returns the object rect of the given type which is at point (x, y) at scale (xScale,...
Definition page.cpp:456
QList< Annotation * > annotations() const
Returns the list of annotations of the page.
Definition page.cpp:513
QString label() const
Returns the label of the page, or a null string if not set.
Definition page.cpp:661
double duration() const
Returns the duration in seconds of the page when displayed in presentation mode.
Definition page.cpp:651
bool hasPixmap(DocumentObserver *observer, int width=-1, int height=-1, const NormalizedRect &rect=NormalizedRect()) const
Returns whether the page of size width x height has a pixmap in the region given by rect for the give...
Definition page.cpp:219
void addAnnotation(Annotation *annotation)
Adds a new annotation to the page.
Definition page.cpp:676
void setObjectRects(const QList< ObjectRect * > &rects)
Sets the list of object rects of the page.
Definition page.cpp:597
bool hasAnnotations() const
Returns whether the page provides annotations.
Definition page.cpp:318
QColor textSelectionColor() const
Returns the color of the current text selection, or an invalid color if no text selection has been se...
Definition page.cpp:671
void setSourceReferences(const QList< SourceRefObjectRect * > &rects)
Sets the list of source reference objects rects.
Definition page.cpp:638
QList< FormField * > formFields() const
Returns the list of FormField of the page.
Definition page.cpp:542
TextEntity::List words(const RegularAreaRect *area, TextPage::TextAreaInclusionBehaviour b) const
Returns the page text (or part of it) including the bounding rectangles.
Definition page.cpp:359
const RegularAreaRect * textSelection() const
Returns the current text selection.
Definition page.cpp:666
bool hasTextPage() const
Returns whether the page provides a text page (TextPage).
Definition page.cpp:256
void setFormFields(const QList< FormField * > &fields)
Sets fields as list of FormField of the page.
Definition page.cpp:743
double width() const
Returns the width of the page.
Definition page.cpp:180
const ObjectRect * nearestObjectRect(ObjectRect::ObjectType type, double x, double y, double xScale, double yScale, double *distance) const
Returns the object rect of the given type which is nearest to the point (x, y) at scale (xScale,...
Definition page.cpp:487
void setLabel(const QString &label)
Sets the labels for the page to label .
Definition page.cpp:656
std::unique_ptr< RegularAreaRect > textArea(const TextSelection &selection) const
Returns the rectangular area of the given selection.
Definition page.cpp:270
QList< Tile > tilesAt(const DocumentObserver *observer, const NormalizedRect &rect) const
Returns a list of all tiles intersecting with rect.
Definition page.cpp:1017
void setBoundingBox(const NormalizedRect &bbox)
Sets the bounding box of the page content in normalized [0,1] coordinates, in terms of the upright or...
Definition page.cpp:205
bool hasHighlights(int id=-1) const
Returns whether the page provides highlighting for the observer with the given id.
Definition page.cpp:294
void deleteRects()
Deletes all object rects of the page.
Definition page.cpp:779
QString text(const RegularAreaRect *area=nullptr) const
Returns the page text (or part of it).
Definition page.cpp:334
void setPageSize(DocumentObserver *observer, int width, int height)
Sets the size of the page (in screen pixels) if there is a TilesManager.
Definition page.cpp:248
void setPageAction(PageAction action, Action *link)
Sets the link object for the given page action.
Definition page.cpp:729
void setPixmap(DocumentObserver *observer, QPixmap *pixmap, const NormalizedRect &rect=NormalizedRect())
Sets the region described by rect with pixmap for the given observer.
Definition page.cpp:547
bool isBoundingBoxKnown() const
Returns whether the bounding box of the page has been computed.
Definition page.cpp:200
Rotation rotation() const
Returns the rotation of the page as defined by the user.
Definition page.cpp:170
void setDuration(double seconds)
Sets the duration of the page to seconds when displayed in presentation mode.
Definition page.cpp:646
This is a list of NormalizedRect, to describe an area consisting of multiple rectangles using normali...
Definition area.h:933
void transform(const QTransform &matrix)
Transforms the regular area with the operations defined by matrix.
Definition area.h:909
This class describes the object rectangle for a source reference.
Definition area.h:599
Represents a piece of text on a TextPage, containing its textual representation and its bounding box.
Definition textpage.h:53
Represents the textual information of a Page.
Definition textpage.h:102
TextAreaInclusionBehaviour
Defines the behaviour of adding characters to text() result.
Definition textpage.h:113
@ AnyPixelTextAreaInclusionBehaviour
A character is included into text() result if any pixel of his bounding box is in the given area.
Definition textpage.h:114
Wrapper around the information needed to generate the selection area.
Definition misc.h:19
const QList< QKeySequence > & end()
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
global.h
Definition action.h:17
Rotation
A rotation.
Definition global.h:46
@ Rotation0
Not rotated.
Definition global.h:47
SearchDirection
Describes the direction of searching.
Definition global.h:36
QDomElement createElement(const QString &tagName)
QDomNode importNode(const QDomNode &importedNode, bool deep)
QString attribute(const QString &name, const QString &defValue) const const
void setAttribute(const QString &name, const QString &value)
QString tagName() const const
QDomNode appendChild(const QDomNode &newChild)
QDomNode firstChild() const const
bool hasChildNodes() const const
bool isElement() const const
bool isNull() const const
QDomNode nextSibling() const const
QDomElement toElement() const const
const_iterator constEnd() const const
const_iterator constFind(const Key &key) const const
void append(QList< T > &&value)
iterator begin()
iterator end()
iterator erase(const_iterator begin, const_iterator end)
bool hasPrevious() const const
const T & previous()
bool hasNext() const const
const T & value() const const
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
int height() const const
QImage toImage() const const
int width() const const
bool contains(const QSet< T > &other) const const
bool isEmpty() const const
int toInt(bool *ok, int base) const const
CaseSensitivity
QUuid createUuid()
QString toString(StringFormat mode) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:17:35 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.