mirror of
https://gitlab.gnome.org/GNOME/evince
synced 2024-06-30 22:54:23 +00:00
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
This commit is contained in:
parent
2cb8817857
commit
4cc58097a2
|
@ -3926,6 +3926,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)
|
||||
{
|
||||
|
@ -3934,6 +4016,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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -3318,6 +3318,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,
|
||||
|
@ -3326,8 +3350,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))
|
||||
|
@ -3335,10 +3365,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 *
|
||||
|
|
Loading…
Reference in New Issue
Block a user