[evince/BUG_tooltip_annots] pdf: support 'de facto' tooltip feature



commit 2d2eb2debe1632111de543a5a6a66ebc202bcaec
Author: Nelson Benítez León <nbenitezl gmail com>
Date:   Sun Mar 8 17:56:07 2020 -0400

    pdf: support 'de facto' tooltip feature
    
    Most pdf readers implement a tooltip feature by
    showing the string content of 'TU' field of a
    widget annotation that is not linked to any
    form field.
    
    Normally, widget annotations carry a reference to a
    form field which are used together to implement the
    different form widgets. But, the PDF spec does not
    forbid standalone (i.e. not linked to any form field)
    widget annotations, and the fact is they're been used
    by most pdf readers to show a tooltip when the area
    of that AnnotWidget is hovered.
    
    According to issue reported files, 'pdfTeX' seems to
    be one of the pdf producers incorporating this tooltip
    feature.
    
    The support for this feature has been recently been
    added to poppler-glib with following api:
    
    gchar   *poppler_annot_get_alternate_name (PopplerAnnot *annot)
    gboolean poppler_annot_set_alternate_name (PopplerAnnot *annot,
                                               const gchar  *name)
    
    so this commit uses only the "read" part of that api
    to show tooltips on pdf's that carry them. For that
    we created a new EvAnnotationTooltip type. This new
    annot type is not shown on the annotations sidebar
    list as part of the rest of annotations, instead it's
    just been used to show the tooltips when the annot
    area is hovered.
    
    Evince issue #842
    
    Poppler issue:
    https://gitlab.freedesktop.org/poppler/poppler/issues/34

 backend/pdf/ev-poppler.cc   | 17 +++++++--
 libdocument/ev-annotation.c | 85 +++++++++++++++++++++++++++++++++++++++++++++
 libdocument/ev-annotation.h | 21 ++++++++++-
 libview/ev-view.c           |  8 +++++
 4 files changed, 128 insertions(+), 3 deletions(-)
---
diff --git a/backend/pdf/ev-poppler.cc b/backend/pdf/ev-poppler.cc
index a6383a15..1a85b5a2 100644
--- a/backend/pdf/ev-poppler.cc
+++ b/backend/pdf/ev-poppler.cc
@@ -3204,10 +3204,23 @@ ev_annot_from_poppler_annot (PopplerAnnot *poppler_annot,
                case POPPLER_ANNOT_SQUIGGLY:
                        ev_annot = ev_annotation_text_markup_squiggly_new (page);
                        break;
-               case POPPLER_ANNOT_LINK:
                case POPPLER_ANNOT_WIDGET:
+                       gchar *tooltip = NULL;
+
+#if POPPLER_CHECK_VERSION(0, 87, 0)
+                       tooltip = poppler_annot_get_alternate_name (poppler_annot);
+#endif
+                       /* Widget annots with no form field associated (i.e. standalone) are
+                        * used by pdf readers/producers to show tooltips. If that's the case
+                        * then poppler_annot_get_alternate_name() will return non null. */
+                       if (tooltip) {
+                               ev_annot = ev_annotation_tooltip_new (page);
+                               ev_annotation_tooltip_take_text (EV_ANNOTATION_TOOLTIP (ev_annot), tooltip);
+                       }
+                       break;
+               case POPPLER_ANNOT_LINK:
                case POPPLER_ANNOT_MOVIE:
-                       /* Ignore link, widgets and movie annots since they are already handled */
+                       /* Ignore link and movie annots since they are already handled */
                        break;
                case POPPLER_ANNOT_SCREEN: {
                        PopplerAction *action;
diff --git a/libdocument/ev-annotation.c b/libdocument/ev-annotation.c
index 058a999b..b9115ec7 100644
--- a/libdocument/ev-annotation.c
+++ b/libdocument/ev-annotation.c
@@ -77,6 +77,16 @@ struct _EvAnnotationTextMarkupClass {
        EvAnnotationClass parent_class;
 };
 
+struct _EvAnnotationTooltip {
+       EvAnnotation parent;
+
+       gchar *tooltip;
+};
+
+struct _EvAnnotationTooltipClass {
+       EvAnnotationClass parent_class;
+};
+
 static void ev_annotation_markup_default_init           (EvAnnotationMarkupInterface *iface);
 static void ev_annotation_text_markup_iface_init        (EvAnnotationMarkupInterface *iface);
 static void ev_annotation_attachment_markup_iface_init  (EvAnnotationMarkupInterface *iface);
@@ -138,6 +148,7 @@ G_DEFINE_TYPE_WITH_CODE (EvAnnotationTextMarkup, ev_annotation_text_markup, EV_T
                 G_IMPLEMENT_INTERFACE (EV_TYPE_ANNOTATION_MARKUP,
                                        ev_annotation_text_markup_markup_iface_init);
         });
+G_DEFINE_TYPE (EvAnnotationTooltip, ev_annotation_tooltip, EV_TYPE_ANNOTATION);
 
 /* EvAnnotation */
 static void
@@ -1506,3 +1517,77 @@ ev_annotation_text_markup_set_markup_type (EvAnnotationTextMarkup    *annot,
 
         return TRUE;
 }
+
+/* EvAnnotationTooltip */
+EvAnnotation *
+ev_annotation_tooltip_new (EvPage *page)
+{
+       g_return_val_if_fail (EV_IS_PAGE (page), NULL);
+
+       return EV_ANNOTATION (g_object_new (EV_TYPE_ANNOTATION_TOOLTIP,
+                                           "page", page,
+                                           NULL));
+}
+
+static void
+ev_annotation_tooltip_init (EvAnnotationTooltip *annot)
+{
+       annot->tooltip = NULL;
+}
+
+static void
+ev_annotation_tooltip_finalize (GObject *object)
+{
+        EvAnnotationTooltip *annot = EV_ANNOTATION_TOOLTIP (object);
+
+       g_clear_pointer (&annot->tooltip, g_free);
+
+        G_OBJECT_CLASS (ev_annotation_tooltip_parent_class)->finalize (object);
+}
+
+static void
+ev_annotation_tooltip_class_init (EvAnnotationTooltipClass *klass)
+{
+       GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
+
+       g_object_class->finalize = ev_annotation_tooltip_finalize;
+}
+
+/**
+ * ev_annotation_tooltip_get_text:
+ * @annot: an #EvAnnotationTooltip
+ *
+ * Get the tooltip string of @annot .
+ *
+ * Returns: (transfer none): A string containing the tooltip text.
+ */
+const gchar *
+ev_annotation_tooltip_get_text (EvAnnotationTooltip *annot)
+{
+       g_return_val_if_fail (EV_IS_ANNOTATION_TOOLTIP (annot), NULL);
+
+       return annot->tooltip;
+}
+
+/**
+ * ev_annotation_tooltip_take_text:
+ * @annot: an #EvAnnotationTooltip
+ * @text: (transfer full): an allocated string containing the tooltip text.
+ *
+ * Set the tooltip string of @annot to be @text.
+ *
+ */
+void
+ev_annotation_tooltip_take_text (EvAnnotationTooltip *annot,
+                                 gchar               *text)
+{
+       g_return_if_fail (EV_IS_ANNOTATION_TOOLTIP (annot));
+
+       if (annot->tooltip == text)
+               return;
+
+       if (annot->tooltip)
+               g_free (annot->tooltip);
+
+       annot->tooltip = text;
+}
diff --git a/libdocument/ev-annotation.h b/libdocument/ev-annotation.h
index 1fe98fe3..d436cccc 100644
--- a/libdocument/ev-annotation.h
+++ b/libdocument/ev-annotation.h
@@ -74,6 +74,14 @@ G_BEGIN_DECLS
 #define EV_IS_ANNOTATION_TEXT_MARKUP_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), 
EV_TYPE_ANNOTATION_TEXT_MARKUP))
 #define EV_ANNOTATION_TEXT_MARKUP_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), 
EV_TYPE_ANNOTATION_TEXT_MARKUP, EvAnnotationTextMarkupClass))
 
+/* EvAnnotationTooltip */
+#define EV_TYPE_ANNOTATION_TOOLTIP                  (ev_annotation_tooltip_get_type())
+#define EV_ANNOTATION_TOOLTIP(object)               (G_TYPE_CHECK_INSTANCE_CAST((object), 
EV_TYPE_ANNOTATION_TOOLTIP, EvAnnotationTooltip))
+#define EV_ANNOTATION_TOOLTIP_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST((klass), 
EV_TYPE_ANNOTATION_TOOLTIP, EvAnnotationTooltipClass))
+#define EV_IS_ANNOTATION_TOOLTIP(object)            (G_TYPE_CHECK_INSTANCE_TYPE((object), 
EV_TYPE_ANNOTATION_TOOLTIP))
+#define EV_IS_ANNOTATION_TOOLTIP_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE((klass), 
EV_TYPE_ANNOTATION_TOOLTIP))
+#define EV_ANNOTATION_TOOLTIP_GET_CLASS(object)     (G_TYPE_INSTANCE_GET_CLASS((object), 
EV_TYPE_ANNOTATION_TOOLTIP, EvAnnotationTooltipClass))
+
 typedef struct _EvAnnotation                EvAnnotation;
 typedef struct _EvAnnotationClass           EvAnnotationClass;
 
@@ -89,11 +97,15 @@ typedef struct _EvAnnotationAttachmentClass EvAnnotationAttachmentClass;
 typedef struct _EvAnnotationTextMarkup      EvAnnotationTextMarkup;
 typedef struct _EvAnnotationTextMarkupClass EvAnnotationTextMarkupClass;
 
+typedef struct _EvAnnotationTooltip         EvAnnotationTooltip;
+typedef struct _EvAnnotationTooltipClass    EvAnnotationTooltipClass;
+
 typedef enum {
        EV_ANNOTATION_TYPE_UNKNOWN,
        EV_ANNOTATION_TYPE_TEXT,
        EV_ANNOTATION_TYPE_ATTACHMENT,
-       EV_ANNOTATION_TYPE_TEXT_MARKUP
+       EV_ANNOTATION_TYPE_TEXT_MARKUP,
+       EV_ANNOTATION_TYPE_TOOLTIP
 } EvAnnotationType;
 
 typedef enum {
@@ -197,6 +209,13 @@ EvAnnotationTextMarkupType ev_annotation_text_markup_get_markup_type (EvAnnotati
 gboolean                   ev_annotation_text_markup_set_markup_type (EvAnnotationTextMarkup    *annot,
                                                                       EvAnnotationTextMarkupType 
markup_type);
 
+/* EvAnnotationTooltip */
+GType                ev_annotation_tooltip_get_type          (void) G_GNUC_CONST;
+EvAnnotation        *ev_annotation_tooltip_new               (EvPage              *page);
+const gchar         *ev_annotation_tooltip_get_text          (EvAnnotationTooltip *annot);
+void                 ev_annotation_tooltip_take_text         (EvAnnotationTooltip *annot,
+                                                              gchar               *text);
+
 G_END_DECLS
 
 #endif /* EV_ANNOTATION_H */
diff --git a/libview/ev-view.c b/libview/ev-view.c
index ffbe1704..ec8d5f16 100644
--- a/libview/ev-view.c
+++ b/libview/ev-view.c
@@ -4926,9 +4926,17 @@ ev_view_query_tooltip (GtkWidget  *widget,
        annot = ev_view_get_annotation_at_location (view, x, y);
        if (annot) {
                const gchar *contents;
+               gboolean found = FALSE;
 
                contents = ev_annotation_get_contents (annot);
                if (contents && *contents != '\0') {
+                       found = TRUE;
+               } else if (EV_IS_ANNOTATION_TOOLTIP (annot)) {
+                       contents = ev_annotation_tooltip_get_text (EV_ANNOTATION_TOOLTIP (annot));
+                       found = contents && *contents != '\0';
+               }
+
+               if (found) {
                        GdkRectangle annot_area;
 
                        get_annot_area (view, x, y, annot, &annot_area);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]