Okular

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

KDE's Doxygen guidelines are available online.