[evince/BUG_markup_annots_check_quadrilaterals: 32/32] markup annotations: use markup text region
- From: Nelson Benítez León <nbenitez src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evince/BUG_markup_annots_check_quadrilaterals: 32/32] markup annotations: use markup text region
- Date: Sat, 22 Feb 2020 01:37:30 +0000 (UTC)
commit d3e1c8c85b17f109a022bf29a25952537a964873
Author: Nelson Benítez León <nbenitezl gmail com>
Date: Sat Dec 28 20:31:54 2019 -0400
markup annotations: use markup text region
when determining if the mouse pointer is over
a markup annotation (used by evince to change
the pointer type). This is more accurate than
just using the annot area.
Implemented as a new libdocument method interface
that we pass it the 'annotation' and 'x,y' coords
and returns us whether that location is over the
actual markup text of the annotation. The poppler
implementation works by checking if there's any
quadrilateral under that location.
For pdf, quadrilaterals represent a more precise
shape of the annotation than just the annotation
area (which is currently used) so by checking
the quads region we avoid false positives when
hovering over blank space.
The "point over a quadrilateral" code is based on:
Algorithm: https://math.stackexchange.com/a/190203
Implementation: https://stackoverflow.com/a/37865332
Fixes issue #1275
backend/pdf/ev-poppler.cc | 83 +++++++++++++++++++++++++++++++++++
libdocument/ev-document-annotations.c | 14 ++++++
libdocument/ev-document-annotations.h | 15 +++++++
libview/ev-view.c | 61 +++++++++++++++++++++++--
4 files changed, 169 insertions(+), 4 deletions(-)
---
diff --git a/backend/pdf/ev-poppler.cc b/backend/pdf/ev-poppler.cc
index a6383a15..1a94fad2 100644
--- a/backend/pdf/ev-poppler.cc
+++ b/backend/pdf/ev-poppler.cc
@@ -3921,6 +3921,88 @@ pdf_document_annotations_save_annotation (EvDocumentAnnotations *document_annota
ev_document_set_modified (EV_DOCUMENT (document_annotations), TRUE);
}
+/* Creates a vector from points @p1 and @p2 and stores it on @vector */
+static inline void
+set_vector (PopplerPoint *p1,
+ PopplerPoint *p2,
+ PopplerPoint *vector)
+{
+ vector->x = p2->x - p1->x;
+ vector->y = p2->y - p1->y;
+}
+
+/* Returns the dot product of the passed vectors @v1 and @v2 */
+static inline gdouble
+dot_product (PopplerPoint *v1,
+ PopplerPoint *v2)
+{
+ return v1->x * v2->x + v1->y * v2->y;
+}
+
+/* Algorithm: https://math.stackexchange.com/a/190203
+ Implementation: https://stackoverflow.com/a/37865332 */
+static gboolean
+point_over_poppler_quadrilateral (PopplerQuadrilateral *quad,
+ PopplerPoint *M)
+{
+ PopplerPoint AB, AM, BC, BM;
+ gdouble dotABAM, dotABAB, dotBCBM, dotBCBC;
+
+ /* We interchange p3 and p4 because algorithm expects clockwise eg. p1 -> p2
+ while pdf quadpoints are defined as p1 -> p2 p4 <- p3
+ p3 -> p4 (https://stackoverflow.com/q/9855814) */
+ set_vector (&quad->p1, &quad->p2, &AB);
+ set_vector (&quad->p1, M, &AM);
+ set_vector (&quad->p2, &quad->p4, &BC);
+ set_vector (&quad->p2, M, &BM);
+
+ dotABAM = dot_product (&AB, &AM);
+ dotABAB = dot_product (&AB, &AB);
+ dotBCBM = dot_product (&BC, &BM);
+ dotBCBC = dot_product (&BC, &BC);
+
+ return 0 <= dotABAM && dotABAM <= dotABAB && 0 <= dotBCBM && dotBCBM <= dotBCBC;
+}
+
+static EvAnnotationsOverMarkup
+pdf_document_annotations_over_markup (EvDocumentAnnotations *document_annotations,
+ EvAnnotation *annot,
+ gdouble x,
+ gdouble y)
+{
+ EvPage *page;
+ PopplerAnnot *poppler_annot;
+ GArray *quads;
+ PopplerPoint M;
+ guint quads_len;
+ gdouble height;
+
+ poppler_annot = POPPLER_ANNOT (g_object_get_data (G_OBJECT (annot), "poppler-annot"));
+
+ if (!poppler_annot || !POPPLER_IS_ANNOT_TEXT_MARKUP (poppler_annot))
+ return EV_ANNOTATION_OVER_MARKUP_UNKNOWN;
+
+ quads = poppler_annot_text_markup_get_quadrilaterals (POPPLER_ANNOT_TEXT_MARKUP (poppler_annot));
+ quads_len = quads->len;
+
+ page = ev_annotation_get_page (annot);
+ poppler_page_get_size (POPPLER_PAGE (page->backend_page), NULL, &height);
+ M.x = x;
+ M.y = height - y;
+
+ for (guint i = 0; i < quads_len; ++i) {
+ PopplerQuadrilateral *quadrilateral = &g_array_index (quads, PopplerQuadrilateral, i);
+
+ if (point_over_poppler_quadrilateral (quadrilateral, &M)) {
+ g_array_unref (quads);
+ return EV_ANNOTATION_OVER_MARKUP_YES;
+ }
+ }
+ g_array_unref (quads);
+
+ return EV_ANNOTATION_OVER_MARKUP_NOT;
+}
+
static void
pdf_document_document_annotations_iface_init (EvDocumentAnnotationsInterface *iface)
{
@@ -3929,6 +4011,7 @@ pdf_document_document_annotations_iface_init (EvDocumentAnnotationsInterface *if
iface->add_annotation = pdf_document_annotations_add_annotation;
iface->save_annotation = pdf_document_annotations_save_annotation;
iface->remove_annotation = pdf_document_annotations_remove_annotation;
+ iface->over_markup = pdf_document_annotations_over_markup;
}
/* Media */
diff --git a/libdocument/ev-document-annotations.c b/libdocument/ev-document-annotations.c
index 96ce4f6c..75fb4490 100644
--- a/libdocument/ev-document-annotations.c
+++ b/libdocument/ev-document-annotations.c
@@ -91,3 +91,17 @@ ev_document_annotations_can_remove_annotation (EvDocumentAnnotations *document_a
return iface->remove_annotation != NULL;
}
+
+EvAnnotationsOverMarkup
+ev_document_annotations_over_markup (EvDocumentAnnotations *document_annots,
+ EvAnnotation *annot,
+ gdouble x,
+ gdouble y)
+{
+ EvDocumentAnnotationsInterface *iface = EV_DOCUMENT_ANNOTATIONS_GET_IFACE (document_annots);
+
+ if (iface->over_markup)
+ return iface->over_markup (document_annots, annot, x, y);
+
+ return EV_ANNOTATION_OVER_MARKUP_NOT_IMPLEMENTED;
+}
diff --git a/libdocument/ev-document-annotations.h b/libdocument/ev-document-annotations.h
index 36ec65ab..f0137c2b 100644
--- a/libdocument/ev-document-annotations.h
+++ b/libdocument/ev-document-annotations.h
@@ -66,6 +66,13 @@ typedef enum {
EV_ANNOTATIONS_SAVE_ALL = (1 << 11) - 1
} EvAnnotationsSaveMask;
+typedef enum {
+ EV_ANNOTATION_OVER_MARKUP_NOT_IMPLEMENTED = 0,
+ EV_ANNOTATION_OVER_MARKUP_UNKNOWN,
+ EV_ANNOTATION_OVER_MARKUP_YES,
+ EV_ANNOTATION_OVER_MARKUP_NOT
+} EvAnnotationsOverMarkup;
+
typedef struct _EvDocumentAnnotations EvDocumentAnnotations;
typedef struct _EvDocumentAnnotationsInterface EvDocumentAnnotationsInterface;
@@ -85,6 +92,10 @@ struct _EvDocumentAnnotationsInterface
EvAnnotationsSaveMask mask);
void (* remove_annotation) (EvDocumentAnnotations *document_annots,
EvAnnotation *annot);
+ EvAnnotationsOverMarkup (* over_markup) (EvDocumentAnnotations *document_annots,
+ EvAnnotation *annot,
+ gdouble x,
+ gdouble y);
};
GType ev_document_annotations_get_type (void) G_GNUC_CONST;
@@ -102,6 +113,10 @@ void ev_document_annotations_save_annotation (EvDocumentAnnotatio
EvAnnotationsSaveMask mask);
gboolean ev_document_annotations_can_add_annotation (EvDocumentAnnotations *document_annots);
gboolean ev_document_annotations_can_remove_annotation (EvDocumentAnnotations *document_annots);
+EvAnnotationsOverMarkup ev_document_annotations_over_markup (EvDocumentAnnotations *document_annots,
+ EvAnnotation *annot,
+ gdouble x,
+ gdouble y);
G_END_DECLS
diff --git a/libview/ev-view.c b/libview/ev-view.c
index 802ca493..82db8e5b 100644
--- a/libview/ev-view.c
+++ b/libview/ev-view.c
@@ -3304,6 +3304,30 @@ hide_annotation_windows (EvView *view,
}
}
+static int
+cmp_mapping_area_size (EvMapping *a,
+ EvMapping *b)
+{
+ gdouble wa, ha, wb, hb;
+
+ wa = a->area.x2 - a->area.x1;
+ ha = a->area.y2 - a->area.y1;
+ wb = b->area.x2 - b->area.x1;
+ hb = b->area.y2 - b->area.y1;
+
+ if (wa == wb) {
+ if (ha == hb)
+ return 0;
+ return (ha < hb) ? -1 : 1;
+ }
+
+ if (ha == hb) {
+ return (wa < wb) ? -1 : 1;
+ }
+
+ return (wa * ha < wb * hb) ? -1 : 1;
+}
+
static EvMapping *
get_annotation_mapping_at_location (EvView *view,
gdouble x,
@@ -3312,8 +3336,14 @@ get_annotation_mapping_at_location (EvView *view,
{
gint x_new = 0, y_new = 0;
EvMappingList *annotations_mapping;
+ EvDocumentAnnotations *doc_annots;
+ EvAnnotation *annot;
+ EvMapping *best;
+ GList *list;
- if (!EV_IS_DOCUMENT_ANNOTATIONS (view->document))
+ doc_annots = EV_DOCUMENT_ANNOTATIONS (view->document);
+
+ if (!doc_annots)
return NULL;
if (!get_doc_point_from_location (view, x, y, page, &x_new, &y_new))
@@ -3321,10 +3351,33 @@ get_annotation_mapping_at_location (EvView *view,
annotations_mapping = ev_page_cache_get_annot_mapping (view->page_cache, *page);
- if (annotations_mapping)
- return ev_mapping_list_get (annotations_mapping, x_new, y_new);
+ if (!annotations_mapping)
+ return NULL;
- return NULL;
+ best = NULL;
+ for (list = ev_mapping_list_get_list (annotations_mapping); list; list = list->next) {
+ EvMapping *mapping = list->data;
+
+ if ((x_new >= mapping->area.x1) &&
+ (y_new >= mapping->area.y1) &&
+ (x_new <= mapping->area.x2) &&
+ (y_new <= mapping->area.y2)) {
+
+ annot = EV_ANNOTATION (mapping->data);
+
+ if (ev_annotation_get_annotation_type (annot) == EV_ANNOTATION_TYPE_TEXT_MARKUP &&
+ ev_document_annotations_over_markup (doc_annots, annot, (gdouble) x_new,
(gdouble) y_new)
+ == EV_ANNOTATION_OVER_MARKUP_NOT)
+ continue; /* ignore markup annots clicked outside the markup text */
+
+ /* In case of only one match choose that. Otherwise
+ * compare the area of the bounding boxes and return the
+ * smallest element */
+ if (best == NULL || cmp_mapping_area_size (mapping, best) < 0)
+ best = mapping;
+ }
+ }
+ return best;
}
static EvAnnotation *
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]