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
262{
263 if (d->m_text) {
264 return d->m_text->wordAt(p, word);
265 }
266
267 return nullptr;
268}
269
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(RegularAreaRect *r, const QColor &color)
630{
631 deleteTextSelections();
632 if (r) {
634 hr->s_id = -1;
635 hr->color = color;
636 m_textSelections = hr;
637 delete r;
638 }
639}
640
642{
644 for (SourceRefObjectRect *rect : refRects) {
645 m_rects << rect;
646 }
647}
648
649void Page::setDuration(double seconds)
650{
651 d->m_duration = seconds;
652}
653
654double Page::duration() const
655{
656 return d->m_duration;
657}
658
659void Page::setLabel(const QString &label)
660{
661 d->m_label = label;
662}
663
665{
666 return d->m_label;
667}
668
670{
671 return d->m_textSelections;
672}
673
675{
676 return d->m_textSelections ? d->m_textSelections->color : QColor();
677}
678
680{
681 // Generate uniqueName: okular-{UUID}
682 if (annotation->uniqueName().isEmpty()) {
683 QString uniqueName = QStringLiteral("okular-") + QUuid::createUuid().toString();
684 annotation->setUniqueName(uniqueName);
685 }
686 annotation->d_ptr->m_page = d;
687 m_annotations.append(annotation);
688
690
691 // Rotate the annotation on the page.
692 const QTransform matrix = d->rotationMatrix();
693 annotation->d_ptr->annotationTransform(matrix);
694
695 m_rects.append(rect);
696}
697
699{
700 if (!d->m_doc->m_parent->canRemovePageAnnotation(annotation)) {
701 return false;
702 }
703
704 QList<Annotation *>::iterator aIt = m_annotations.begin();
705 for (; aIt != m_annotations.end(); ++aIt) {
706 if ((*aIt) && (*aIt)->uniqueName() == annotation->uniqueName()) {
707 int rectfound = false;
708 QList<ObjectRect *>::iterator it = m_rects.begin();
709 for (; it != m_rects.end() && !rectfound; ++it) {
710 if (((*it)->objectType() == ObjectRect::OAnnotation) && ((*it)->object() == (*aIt))) {
711 delete *it;
712 it = m_rects.erase(it);
713 rectfound = true;
714 }
715 }
716 qCDebug(OkularCoreDebug) << "removed annotation:" << annotation->uniqueName();
717 annotation->d_ptr->m_page = nullptr;
718 m_annotations.erase(aIt);
719 break;
720 }
721 }
722
723 return true;
724}
725
727{
728 delete d->m_transition;
729 d->m_transition = transition;
730}
731
733{
734 switch (action) {
735 case Page::Opening:
736 delete d->m_openingAction;
737 d->m_openingAction = link;
738 break;
739 case Page::Closing:
740 delete d->m_closingAction;
741 d->m_closingAction = link;
742 break;
743 }
744}
745
747{
748 qDeleteAll(d->formfields);
749 d->formfields = fields;
750 for (FormField *ff : std::as_const(d->formfields)) {
751 ff->d_ptr->setDefault();
752 ff->d_ptr->m_page = this;
753 }
754}
755
757{
758 TilesManager *tm = d->tilesManager(observer);
759 if (tm) {
760 delete tm;
761 d->m_tilesManagers.remove(observer);
762 } else {
763 PagePrivate::PixmapObject object = d->m_pixmaps.take(observer);
764 delete object.m_pixmap;
765 }
766}
767
769{
771 while (it.hasNext()) {
772 it.next();
773 delete it.value().m_pixmap;
774 }
775
776 d->m_pixmaps.clear();
777
778 qDeleteAll(d->m_tilesManagers);
779 d->m_tilesManagers.clear();
780}
781
783{
784 // delete ObjectRects of type Link and Image
787 deleteObjectRects(m_rects, which);
788}
789
790void PagePrivate::deleteHighlights(int s_id)
791{
792 // delete highlights by ID
793 QList<HighlightAreaRect *>::iterator it = m_page->m_highlights.begin();
794 while (it != m_page->m_highlights.end()) {
795 HighlightAreaRect *highlight = *it;
796 if (s_id == -1 || highlight->s_id == s_id) {
797 it = m_page->m_highlights.erase(it);
798 delete highlight;
799 } else {
800 ++it;
801 }
802 }
803}
804
805void PagePrivate::deleteTextSelections()
806{
807 delete m_textSelections;
808 m_textSelections = nullptr;
809}
810
812{
813 deleteObjectRects(m_rects, QSet<ObjectRect::ObjectType>() << ObjectRect::SourceRef);
814}
815
817{
818 // delete ObjectRects of type Annotation
819 deleteObjectRects(m_rects, QSet<ObjectRect::ObjectType>() << ObjectRect::OAnnotation);
820 // delete all stored annotations
821 qDeleteAll(m_annotations);
822 m_annotations.clear();
823}
824
825bool PagePrivate::restoreLocalContents(const QDomNode &pageNode)
826{
827 bool loadedAnything = false; // set if something actually gets loaded
828
829 // iterate over all children (annotationList, ...)
830 QDomNode childNode = pageNode.firstChild();
831 while (childNode.isElement()) {
832 QDomElement childElement = childNode.toElement();
833 childNode = childNode.nextSibling();
834
835 // parse annotationList child element
836 if (childElement.tagName() == QLatin1String("annotationList")) {
837#ifdef PAGE_PROFILE
838 QTime time;
839 time.start();
840#endif
841 // Clone annotationList as root node in restoredLocalAnnotationList
842 const QDomNode clonedNode = restoredLocalAnnotationList.importNode(childElement, true);
843 restoredLocalAnnotationList.appendChild(clonedNode);
844
845 // iterate over all annotations
846 QDomNode annotationNode = childElement.firstChild();
847 while (annotationNode.isElement()) {
848 // get annotation element and advance to next annot
849 QDomElement annotElement = annotationNode.toElement();
850 annotationNode = annotationNode.nextSibling();
851
852 // get annotation from the dom element
853 Annotation *annotation = AnnotationUtils::createAnnotation(annotElement);
854
855 // append annotation to the list or show warning
856 if (annotation) {
857 m_doc->performAddPageAnnotation(m_number, annotation);
858 qCDebug(OkularCoreDebug) << "restored annot:" << annotation->uniqueName();
859 loadedAnything = true;
860 } else {
861 qCWarning(OkularCoreDebug).nospace() << "page (" << m_number << "): can't restore an annotation from XML.";
862 }
863 }
864#ifdef PAGE_PROFILE
865 qCDebug(OkularCoreDebug).nospace() << "annots: XML Load time: " << time.elapsed() << "ms";
866#endif
867 }
868 // parse formList child element
869 else if (childElement.tagName() == QLatin1String("forms")) {
870 // Clone forms as root node in restoredFormFieldList
871 const QDomNode clonedNode = restoredFormFieldList.importNode(childElement, true);
872 restoredFormFieldList.appendChild(clonedNode);
873
874 if (formfields.isEmpty()) {
875 continue;
876 }
877
878 QHash<int, FormField *> hashedforms;
879 for (FormField *ff : std::as_const(formfields)) {
880 hashedforms[ff->id()] = ff;
881 }
882
883 // iterate over all forms
884 QDomNode formsNode = childElement.firstChild();
885 while (formsNode.isElement()) {
886 // get annotation element and advance to next annot
887 QDomElement formElement = formsNode.toElement();
888 formsNode = formsNode.nextSibling();
889
890 if (formElement.tagName() != QLatin1String("form")) {
891 continue;
892 }
893
894 bool ok = true;
895 int index = formElement.attribute(QStringLiteral("id")).toInt(&ok);
896 if (!ok) {
897 continue;
898 }
899
900 QHash<int, FormField *>::const_iterator wantedIt = hashedforms.constFind(index);
901 if (wantedIt == hashedforms.constEnd()) {
902 continue;
903 }
904
905 QString value = formElement.attribute(QStringLiteral("value"));
906 (*wantedIt)->d_ptr->setValue(value);
907 loadedAnything = true;
908 }
909 }
910 }
911
912 return loadedAnything;
913}
914
915void PagePrivate::saveLocalContents(QDomNode &parentNode, QDomDocument &document, PageItems what) const
916{
917 // create the page node and set the 'number' attribute
918 QDomElement pageElement = document.createElement(QStringLiteral("page"));
919 pageElement.setAttribute(QStringLiteral("number"), m_number);
920
921 // add annotations info if has got any
922 if ((what & AnnotationPageItems) && (what & OriginalAnnotationPageItems)) {
923 const QDomElement savedDocRoot = restoredLocalAnnotationList.documentElement();
924 if (!savedDocRoot.isNull()) {
925 // Import and append node in target document
926 const QDomNode importedNode = document.importNode(savedDocRoot, true);
927 pageElement.appendChild(importedNode);
928 }
929 } else if ((what & AnnotationPageItems) && !m_page->m_annotations.isEmpty()) {
930 // create the annotationList
931 QDomElement annotListElement = document.createElement(QStringLiteral("annotationList"));
932
933 // add every annotation to the annotationList
934 for (const Annotation *a : std::as_const(m_page->m_annotations)) {
935 // only save okular annotations (not the embedded in file ones)
936 if (!(a->flags() & Annotation::External)) {
937 // append an filled-up element called 'annotation' to the list
938 QDomElement annElement = document.createElement(QStringLiteral("annotation"));
939 AnnotationUtils::storeAnnotation(a, annElement, document);
940 annotListElement.appendChild(annElement);
941 qCDebug(OkularCoreDebug) << "save annotation:" << a->uniqueName();
942 }
943 }
944
945 // append the annotationList element if annotations have been set
946 if (annotListElement.hasChildNodes()) {
947 pageElement.appendChild(annotListElement);
948 }
949 }
950
951 // add forms info if has got any
952 if ((what & FormFieldPageItems) && (what & OriginalFormFieldPageItems)) {
953 const QDomElement savedDocRoot = restoredFormFieldList.documentElement();
954 if (!savedDocRoot.isNull()) {
955 // Import and append node in target document
956 const QDomNode importedNode = document.importNode(savedDocRoot, true);
957 pageElement.appendChild(importedNode);
958 }
959 } else if ((what & FormFieldPageItems) && !formfields.isEmpty()) {
960 // create the formList
961 QDomElement formListElement = document.createElement(QStringLiteral("forms"));
962
963 // add every form data to the formList
964 for (const FormField *f : formfields) {
965 QString newvalue = f->d_ptr->value();
966 if (f->d_ptr->m_default == newvalue) {
967 continue;
968 }
969
970 // append an filled-up element called 'annotation' to the list
971 QDomElement formElement = document.createElement(QStringLiteral("form"));
972 formElement.setAttribute(QStringLiteral("id"), f->id());
973 formElement.setAttribute(QStringLiteral("value"), newvalue);
974 formListElement.appendChild(formElement);
975 }
976
977 // append the annotationList element if annotations have been set
978 if (formListElement.hasChildNodes()) {
979 pageElement.appendChild(formListElement);
980 }
981 }
982
983 // append the page element only if has children
984 if (pageElement.hasChildNodes()) {
985 parentNode.appendChild(pageElement);
986 }
987}
988
989const QPixmap *Page::_o_nearestPixmap(DocumentObserver *observer, int w, int h) const
990{
991 Q_UNUSED(h)
992
993 const QPixmap *pixmap = nullptr;
994
995 // if a pixmap is present for given id, use it
996 QMap<DocumentObserver *, PagePrivate::PixmapObject>::const_iterator itPixmap = d->m_pixmaps.constFind(observer);
997 if (itPixmap != d->m_pixmaps.constEnd()) {
998 pixmap = itPixmap.value().m_pixmap;
999 } else if (!d->m_pixmaps.isEmpty()) {
1000 // else find the closest match using pixmaps of other IDs (great optim!)
1001 int minDistance = -1;
1002 QMap<DocumentObserver *, PagePrivate::PixmapObject>::const_iterator it = d->m_pixmaps.constBegin(), end = d->m_pixmaps.constEnd();
1003 for (; it != end; ++it) {
1004 int pixWidth = (*it).m_pixmap->width(), distance = pixWidth > w ? pixWidth - w : w - pixWidth;
1005 if (minDistance == -1 || distance < minDistance) {
1006 pixmap = (*it).m_pixmap;
1007 minDistance = distance;
1008 }
1009 }
1010 }
1011
1012 return pixmap;
1013}
1014
1015bool Page::hasTilesManager(const DocumentObserver *observer) const
1016{
1017 return d->tilesManager(observer) != nullptr;
1018}
1019
1021{
1022 TilesManager *tm = d->m_tilesManagers.value(observer);
1023 if (tm) {
1024 return tm->tilesAt(rect, TilesManager::PixmapTile);
1025 } else {
1026 return QList<Tile>();
1027 }
1028}
1029
1030TilesManager *PagePrivate::tilesManager(const DocumentObserver *observer) const
1031{
1032 return m_tilesManagers.value(observer);
1033}
1034
1035void PagePrivate::setTilesManager(const DocumentObserver *observer, TilesManager *tm)
1036{
1037 TilesManager *old = m_tilesManagers.value(observer);
1038 delete old;
1039
1040 m_tilesManagers.insert(observer, tm);
1041}
1042
1043void PagePrivate::adoptGeneratedContents(PagePrivate *oldPage)
1044{
1045 rotateAt(oldPage->m_rotation);
1046
1047 m_pixmaps = oldPage->m_pixmaps;
1048 oldPage->m_pixmaps.clear();
1049
1050 m_tilesManagers = oldPage->m_tilesManagers;
1051 oldPage->m_tilesManagers.clear();
1052
1053 m_boundingBox = oldPage->m_boundingBox;
1054 m_isBoundingBoxKnown = oldPage->m_isBoundingBoxKnown;
1055 m_text = oldPage->m_text;
1056 oldPage->m_text = nullptr;
1057
1058 m_textSelections = oldPage->m_textSelections;
1059 oldPage->m_textSelections = nullptr;
1060
1061 restoredLocalAnnotationList = oldPage->restoredLocalAnnotationList;
1062 restoredFormFieldList = oldPage->restoredFormFieldList;
1063}
1064
1065FormField *PagePrivate::findEquivalentForm(const Page *p, FormField *oldField)
1066{
1067 // given how id is not very good of id (at least for pdf) we do a few passes
1068 // same rect, type and id
1069 for (FormField *f : std::as_const(p->d->formfields)) {
1070 if (f->rect() == oldField->rect() && f->type() == oldField->type() && f->id() == oldField->id()) {
1071 return f;
1072 }
1073 }
1074 // same rect and type
1075 for (FormField *f : std::as_const(p->d->formfields)) {
1076 if (f->rect() == oldField->rect() && f->type() == oldField->type()) {
1077 return f;
1078 }
1079 }
1080 // fuzzy rect, same type and id
1081 for (FormField *f : std::as_const(p->d->formfields)) {
1082 if (f->type() == oldField->type() && f->id() == oldField->id() && qFuzzyCompare(f->rect().left, oldField->rect().left) && qFuzzyCompare(f->rect().top, oldField->rect().top) &&
1083 qFuzzyCompare(f->rect().right, oldField->rect().right) && qFuzzyCompare(f->rect().bottom, oldField->rect().bottom)) {
1084 return f;
1085 }
1086 }
1087 // fuzzy rect and same type
1088 for (FormField *f : std::as_const(p->d->formfields)) {
1089 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) &&
1090 qFuzzyCompare(f->rect().bottom, oldField->rect().bottom)) {
1091 return f;
1092 }
1093 }
1094 return nullptr;
1095}
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:756
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:1015
~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:726
void deleteSourceReferences()
Deletes all source reference objects of the page.
Definition page.cpp:811
RegularAreaRect * wordAt(const NormalizedPoint &p, QString *word=nullptr) 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
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:698
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:816
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:768
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:664
double duration() const
Returns the duration in seconds of the page when displayed in presentation mode.
Definition page.cpp:654
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:679
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:674
void setSourceReferences(const QList< SourceRefObjectRect * > &rects)
Sets the list of source reference objects rects.
Definition page.cpp:641
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:669
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:746
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:659
QList< Tile > tilesAt(const DocumentObserver *observer, const NormalizedRect &rect) const
Returns a list of all tiles intersecting with rect.
Definition page.cpp:1020
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:782
RegularAreaRect * textArea(TextSelection *selection) const
Returns the rectangular area of the given selection.
Definition page.cpp:270
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:732
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:649
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 There are two assumptions inside...
Definition misc.h:34
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
QHash::const_iterator constEnd() const const
QHash::const_iterator constFind(const Key &key) const const
void append(const T &value)
QList::iterator begin()
QList::iterator end()
QList::iterator erase(QList::iterator pos)
bool hasPrevious() const const
const T & previous()
bool hasNext() const const
QMapIterator::Item next()
const T & value() const const
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
int height() const const
QImage toImage() const const
int width() const const
bool contains(const T &value) const const
bool isEmpty() const const
int toInt(bool *ok, int base) const const
CaseSensitivity
int elapsed() const const
void start()
QUuid createUuid()
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sun Feb 25 2024 18:44:05 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.