[poppler] glib/demo glib/poppler-annot.cc glib/poppler-annot.h glib/poppler.h glib/poppler-page.cc glib/poppler-private.h glib/reference

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Sun Jul 3 21:51:42 UTC 2022


 glib/demo/annots.c                  |  171 ++++++++++++++++++++-
 glib/poppler-annot.cc               |  288 ++++++++++++++++++++++++++++++++++++
 glib/poppler-annot.h                |   35 ++++
 glib/poppler-page.cc                |    3 
 glib/poppler-private.h              |    2 
 glib/poppler.h                      |    1 
 glib/reference/poppler-sections.txt |   10 +
 glib/reference/poppler.types        |    1 
 8 files changed, 507 insertions(+), 4 deletions(-)

New commits:
commit a0e4ff30be462cae010d22d291ab282f5d86a5e3
Author: Nelson Benítez León <nbenitezl at gmail.com>
Date:   Wed May 11 21:07:30 2022 -0400

    glib: add support for stamp annotation
    
    Creates new PopplerAnnotStamp type with the
    following public api:
    
    PopplerAnnot *poppler_annot_stamp_new(PopplerDocument *doc, PopplerRectangle *rect);
    PopplerAnnotStampIcon poppler_annot_stamp_get_icon(PopplerAnnotStamp *poppler_annot);
    void poppler_annot_stamp_set_icon(PopplerAnnotStamp *poppler_annot, PopplerAnnotStampIcon icon);
    gboolean poppler_annot_stamp_set_custom_image(PopplerAnnotStamp *poppler_annot, cairo_surface_t *image, GError **error);
    
    Updates poppler-glib-demo to test PopplerAnnotStamp
    including the custom image support.

diff --git a/glib/demo/annots.c b/glib/demo/annots.c
index 799fe434..645a7210 100644
--- a/glib/demo/annots.c
+++ b/glib/demo/annots.c
@@ -22,6 +22,8 @@
 #include "annots.h"
 #include "utils.h"
 
+#define STAMP_CUSTOM_IMAGE "Custom image"
+
 enum
 {
     ANNOTS_TYPE_COLUMN,
@@ -47,10 +49,26 @@ typedef struct
 } Annotations;
 
 static const Annotations supported_annots[] = {
-    { POPPLER_ANNOT_TEXT, "Text" },           { POPPLER_ANNOT_LINE, "Line" },           { POPPLER_ANNOT_SQUARE, "Square" },     { POPPLER_ANNOT_CIRCLE, "Circle" },
-    { POPPLER_ANNOT_HIGHLIGHT, "Highlight" }, { POPPLER_ANNOT_UNDERLINE, "Underline" }, { POPPLER_ANNOT_SQUIGGLY, "Squiggly" }, { POPPLER_ANNOT_STRIKE_OUT, "Strike Out" },
+    { POPPLER_ANNOT_TEXT, "Text" },           { POPPLER_ANNOT_LINE, "Line" },         { POPPLER_ANNOT_SQUARE, "Square" },         { POPPLER_ANNOT_CIRCLE, "Circle" }, { POPPLER_ANNOT_HIGHLIGHT, "Highlight" },
+    { POPPLER_ANNOT_UNDERLINE, "Underline" }, { POPPLER_ANNOT_SQUIGGLY, "Squiggly" }, { POPPLER_ANNOT_STRIKE_OUT, "Strike Out" }, { POPPLER_ANNOT_STAMP, "Stamp" },
 };
 
+static const char *stamp_types[] = { [POPPLER_ANNOT_STAMP_ICON_UNKNOWN] = "Unknown",
+                                     [POPPLER_ANNOT_STAMP_ICON_APPROVED] = "APPROVED",
+                                     [POPPLER_ANNOT_STAMP_ICON_AS_IS] = "AS_IS",
+                                     [POPPLER_ANNOT_STAMP_ICON_CONFIDENTIAL] = "CONFIDENTIAL",
+                                     [POPPLER_ANNOT_STAMP_ICON_FINAL] = "FINAL",
+                                     [POPPLER_ANNOT_STAMP_ICON_EXPERIMENTAL] = "EXPERIMENTAL",
+                                     [POPPLER_ANNOT_STAMP_ICON_EXPIRED] = "EXPIRED",
+                                     [POPPLER_ANNOT_STAMP_ICON_NOT_APPROVED] = "NOT_APPROVED",
+                                     [POPPLER_ANNOT_STAMP_ICON_NOT_FOR_PUBLIC_RELEASE] = "NOT_FOR_PUBLIC_RELEASE",
+                                     [POPPLER_ANNOT_STAMP_ICON_SOLD] = "SOLD",
+                                     [POPPLER_ANNOT_STAMP_ICON_DEPARTMENTAL] = "DEPARTMENTAL",
+                                     [POPPLER_ANNOT_STAMP_ICON_FOR_COMMENT] = "FOR_COMMENT",
+                                     [POPPLER_ANNOT_STAMP_ICON_FOR_PUBLIC_RELEASE] = "FOR_PUBLIC_RELEASE",
+                                     [POPPLER_ANNOT_STAMP_ICON_TOP_SECRET] = "TOP_SECRET",
+                                     [POPPLER_ANNOT_STAMP_ICON_NONE] = "None" };
+
 typedef enum
 {
     MODE_NORMAL, /* Regular use as pointer in the page */
@@ -72,10 +90,12 @@ typedef struct
     GtkWidget *timer_label;
     GtkWidget *remove_button;
     GtkWidget *type_selector;
+    GtkWidget *stamp_selector;
     GtkWidget *main_box;
 
     gint num_page;
     gint annot_type;
+    char *custom_image_filename;
     ModeType mode;
 
     cairo_surface_t *surface;
@@ -460,6 +480,14 @@ static void pgd_annot_view_set_annot_free_text(GtkWidget *table, PopplerAnnotFre
     g_free(text);
 }
 
+static void pgd_annot_view_set_annot_stamp(GtkWidget *table, PopplerAnnotStamp *annot, gint *row)
+{
+    PopplerAnnotStampIcon icon;
+
+    icon = poppler_annot_stamp_get_icon(annot);
+    pgd_table_add_property(GTK_GRID(table), "<b>Icon Name:</b>", stamp_types[icon], row);
+}
+
 static void pgd_annots_file_attachment_save_dialog_response(GtkFileChooser *file_chooser, gint response, PopplerAttachment *attachment)
 {
     gchar *filename;
@@ -614,6 +642,9 @@ static void pgd_annot_view_set_annot(PgdAnnotsDemo *demo, PopplerAnnot *annot)
     case POPPLER_ANNOT_SCREEN:
         pgd_annot_view_set_annot_screen(table, POPPLER_ANNOT_SCREEN(annot), &row);
         break;
+    case POPPLER_ANNOT_STAMP:
+        pgd_annot_view_set_annot_stamp(table, POPPLER_ANNOT_STAMP(annot), &row);
+        break;
     default:
         break;
     }
@@ -791,6 +822,19 @@ static GArray *pgd_annots_create_quads_array_for_rectangle(PopplerRectangle *rec
     return quads_array;
 }
 
+static PopplerAnnotStampIcon get_icon_from_stamp_text(gchar *icon_text)
+{
+    int i;
+
+    for (i = 1; i < G_N_ELEMENTS(stamp_types) - 1; i++) {
+        if (strcmp(stamp_types[i], icon_text) == 0) {
+            return (PopplerAnnotStampIcon)i;
+        }
+    }
+
+    return POPPLER_ANNOT_STAMP_ICON_UNKNOWN;
+}
+
 static void pgd_annots_add_annot(PgdAnnotsDemo *demo)
 {
     PopplerRectangle rect;
@@ -860,13 +904,35 @@ static void pgd_annots_add_annot(PgdAnnotsDemo *demo)
         annot = poppler_annot_text_markup_new_strikeout(demo->doc, &rect, quads_array);
         g_array_free(quads_array, TRUE);
     } break;
+    case POPPLER_ANNOT_STAMP: {
+        annot = poppler_annot_stamp_new(demo->doc, &rect);
+        gchar *stamp_type = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(demo->stamp_selector));
+        GError *error = NULL;
+
+        if (strcmp(stamp_type, STAMP_CUSTOM_IMAGE) == 0 && demo->custom_image_filename) {
+            cairo_surface_t *img = cairo_image_surface_create_from_png(demo->custom_image_filename);
+            if (cairo_surface_status(img) == CAIRO_STATUS_SUCCESS) {
+                poppler_annot_stamp_set_custom_image(POPPLER_ANNOT_STAMP(annot), img, &error);
+                if (error) {
+                    g_warning("%s", error->message);
+                    g_error_free(error);
+                }
+            }
+            cairo_surface_destroy(img);
+        } else {
+            poppler_annot_stamp_set_icon(POPPLER_ANNOT_STAMP(annot), get_icon_from_stamp_text(stamp_type));
+        }
+        g_free(stamp_type);
+    } break;
     default:
         g_assert_not_reached();
     }
 
     demo->active_annot = annot;
 
-    poppler_annot_set_color(annot, &color);
+    if (demo->annot_type != POPPLER_ANNOT_STAMP) {
+        poppler_annot_set_color(annot, &color);
+    }
     poppler_page_add_annot(demo->page, annot);
     pgd_annots_add_annot_to_model(demo, annot, rect, TRUE);
     g_object_unref(annot);
@@ -1056,6 +1122,83 @@ static gboolean pgd_annots_drawing_area_button_press(GtkWidget *area, GdkEventBu
     return TRUE;
 }
 
+static void choose_custom_image(PgdAnnotsDemo *demo)
+{
+    GtkFileChooser *chooser_dialog;
+    gint response;
+    const gchar *chooser_dir = "/usr/share/pixmaps";
+    const gchar *pics_dir;
+    GtkFileFilter *filter;
+
+    chooser_dialog = GTK_FILE_CHOOSER(gtk_file_chooser_dialog_new("Select PNG Image", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, "gtk-cancel", GTK_RESPONSE_CANCEL, "gtk-open", GTK_RESPONSE_ACCEPT, NULL));
+    gtk_window_set_modal(GTK_WINDOW(chooser_dialog), TRUE);
+    gtk_dialog_set_default_response(GTK_DIALOG(chooser_dialog), GTK_RESPONSE_ACCEPT);
+
+    gtk_file_chooser_add_shortcut_folder(chooser_dialog, chooser_dir, NULL);
+    pics_dir = g_get_user_special_dir(G_USER_DIRECTORY_PICTURES);
+    if (pics_dir != NULL) {
+        gtk_file_chooser_add_shortcut_folder(chooser_dialog, pics_dir, NULL);
+    }
+
+    if (!g_file_test(chooser_dir, G_FILE_TEST_IS_DIR)) {
+        chooser_dir = g_get_home_dir();
+    }
+
+    gtk_file_chooser_set_current_folder(chooser_dialog, chooser_dir);
+
+    filter = gtk_file_filter_new();
+    gtk_file_filter_set_name(filter, "PNG images");
+    gtk_file_filter_add_mime_type(filter, "image/png");
+    gtk_file_chooser_add_filter(chooser_dialog, filter);
+    filter = gtk_file_filter_new();
+    gtk_file_filter_set_name(filter, "All Files");
+    gtk_file_filter_add_pattern(filter, "*");
+    gtk_file_chooser_add_filter(chooser_dialog, filter);
+
+    response = gtk_dialog_run(GTK_DIALOG(chooser_dialog));
+
+    if (response == GTK_RESPONSE_ACCEPT) {
+        if (demo->custom_image_filename) {
+            g_free(demo->custom_image_filename);
+        }
+
+        demo->custom_image_filename = gtk_file_chooser_get_filename(chooser_dialog);
+    } else {
+        if (demo->custom_image_filename) {
+            g_free(demo->custom_image_filename);
+            demo->custom_image_filename = NULL;
+        }
+    }
+
+    gtk_widget_destroy(GTK_WIDGET(chooser_dialog));
+}
+
+static void stamp_selector_changed(GtkComboBox *combo_box, PgdAnnotsDemo *demo)
+{
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    gchar *active;
+
+    model = gtk_combo_box_get_model(combo_box);
+    gtk_combo_box_get_active_iter(combo_box, &iter);
+    gtk_tree_model_get(model, &iter, 0, &active, -1);
+    if (strcmp(active, STAMP_CUSTOM_IMAGE) == 0) {
+        choose_custom_image(demo);
+    }
+}
+
+static void type_selector_changed(GtkComboBox *combo_box, PgdAnnotsDemo *demo)
+{
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    int active;
+
+    model = gtk_combo_box_get_model(combo_box);
+    gtk_combo_box_get_active_iter(combo_box, &iter);
+    gtk_tree_model_get(model, &iter, SELECTED_TYPE_COLUMN, &active, -1);
+    gtk_widget_set_sensitive(demo->stamp_selector, active == POPPLER_ANNOT_STAMP);
+}
+
 static gboolean pgd_annots_drawing_area_motion_notify(GtkWidget *area, GdkEventMotion *event, PgdAnnotsDemo *demo)
 {
     PopplerRectangle rect;
@@ -1114,7 +1257,7 @@ GtkWidget *pgd_annots_create_widget(PopplerDocument *document)
     GtkWidget *label;
     GtkWidget *vbox, *vbox2;
     GtkWidget *button;
-    GtkWidget *hbox, *page_selector;
+    GtkWidget *hbox, *hbox2, *page_selector;
     GtkWidget *hpaned;
     GtkWidget *swindow, *treeview;
     GtkTreeSelection *selection;
@@ -1138,6 +1281,7 @@ GtkWidget *pgd_annots_create_widget(PopplerDocument *document)
     vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
 
     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+    hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
 
     label = gtk_label_new("Page:");
     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
@@ -1161,6 +1305,7 @@ GtkWidget *pgd_annots_create_widget(PopplerDocument *document)
     gtk_widget_show(demo->remove_button);
 
     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, TRUE, 0);
 
     button = gtk_button_new_with_mnemonic("_Add");
     g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_annots_start_add_annot), (gpointer)demo);
@@ -1175,8 +1320,25 @@ GtkWidget *pgd_annots_create_widget(PopplerDocument *document)
     }
 
     demo->type_selector = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
+    g_signal_connect(demo->type_selector, "changed", G_CALLBACK(type_selector_changed), (gpointer)demo);
     g_object_unref(model);
 
+    demo->stamp_selector = gtk_combo_box_text_new();
+    for (i = 1; i < G_N_ELEMENTS(stamp_types) - 1; i++) {
+        gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(demo->stamp_selector), stamp_types[i]);
+    }
+    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(demo->stamp_selector), STAMP_CUSTOM_IMAGE);
+
+    gtk_combo_box_set_active(GTK_COMBO_BOX(demo->stamp_selector), 0);
+    g_signal_connect(demo->stamp_selector, "changed", G_CALLBACK(stamp_selector_changed), (gpointer)demo);
+    gtk_widget_set_sensitive(demo->stamp_selector, FALSE);
+    label = gtk_label_new("Stamp type: ");
+    gtk_widget_set_sensitive(label, FALSE);
+    g_object_bind_property(demo->stamp_selector, "sensitive", label, "sensitive", 0);
+    gtk_box_pack_end(GTK_BOX(hbox2), demo->stamp_selector, FALSE, FALSE, 0);
+    gtk_box_pack_end(GTK_BOX(hbox2), label, FALSE, TRUE, 0);
+    gtk_widget_show_all(hbox2);
+
     renderer = gtk_cell_renderer_text_new();
     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(demo->type_selector), renderer, TRUE);
     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(demo->type_selector), renderer, "text", SELECTED_LABEL_COLUMN, NULL);
@@ -1193,6 +1355,7 @@ GtkWidget *pgd_annots_create_widget(PopplerDocument *document)
     gtk_widget_show(button);
 
     gtk_widget_show(hbox);
+    gtk_widget_show(hbox2);
 
     demo->timer_label = gtk_label_new(NULL);
     gtk_label_set_markup(GTK_LABEL(demo->timer_label), "<i>No annotations found</i>");
diff --git a/glib/poppler-annot.cc b/glib/poppler-annot.cc
index 2c9a7607..b995f037 100644
--- a/glib/poppler-annot.cc
+++ b/glib/poppler-annot.cc
@@ -26,6 +26,7 @@
 #define ZERO_CROPBOX(c) (!(c && (c->x1 > 0.01 || c->y1 > 0.01)))
 
 const PDFRectangle *_poppler_annot_get_cropbox_and_page(PopplerAnnot *poppler_annot, Page **page_out);
+AnnotStampImageHelper *_poppler_convert_cairo_image_to_stamp_image_helper(cairo_surface_t *image, PDFDoc *doc, GError **error);
 
 /**
  * SECTION:poppler-annot
@@ -44,6 +45,7 @@ typedef struct _PopplerAnnotScreenClass PopplerAnnotScreenClass;
 typedef struct _PopplerAnnotLineClass PopplerAnnotLineClass;
 typedef struct _PopplerAnnotCircleClass PopplerAnnotCircleClass;
 typedef struct _PopplerAnnotSquareClass PopplerAnnotSquareClass;
+typedef struct _PopplerAnnotStampClass PopplerAnnotStampClass;
 
 struct _PopplerAnnotClass
 {
@@ -153,6 +155,14 @@ struct _PopplerAnnotSquareClass
 {
     PopplerAnnotMarkupClass parent_class;
 };
+struct _PopplerAnnotStamp
+{
+    PopplerAnnot parent_instance;
+};
+struct _PopplerAnnotStampClass
+{
+    PopplerAnnotClass parent_class;
+};
 
 G_DEFINE_TYPE(PopplerAnnot, poppler_annot, G_TYPE_OBJECT)
 G_DEFINE_TYPE(PopplerAnnotMarkup, poppler_annot_markup, POPPLER_TYPE_ANNOT)
@@ -165,6 +175,7 @@ G_DEFINE_TYPE(PopplerAnnotScreen, poppler_annot_screen, POPPLER_TYPE_ANNOT)
 G_DEFINE_TYPE(PopplerAnnotLine, poppler_annot_line, POPPLER_TYPE_ANNOT_MARKUP)
 G_DEFINE_TYPE(PopplerAnnotCircle, poppler_annot_circle, POPPLER_TYPE_ANNOT_MARKUP)
 G_DEFINE_TYPE(PopplerAnnotSquare, poppler_annot_square, POPPLER_TYPE_ANNOT_MARKUP)
+G_DEFINE_TYPE(PopplerAnnotStamp, poppler_annot_stamp, POPPLER_TYPE_ANNOT)
 
 static PopplerAnnot *_poppler_create_annot(GType annot_type, Annot *annot)
 {
@@ -598,6 +609,141 @@ PopplerAnnot *poppler_annot_square_new(PopplerDocument *doc, PopplerRectangle *r
     return _poppler_annot_square_new(annot);
 }
 
+static void poppler_annot_stamp_finalize(GObject *object)
+{
+    G_OBJECT_CLASS(poppler_annot_stamp_parent_class)->finalize(object);
+}
+
+static void poppler_annot_stamp_init(PopplerAnnotStamp *poppler_annot) { }
+
+static void poppler_annot_stamp_class_init(PopplerAnnotStampClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+    gobject_class->finalize = poppler_annot_stamp_finalize;
+}
+
+PopplerAnnot *_poppler_annot_stamp_new(Annot *annot)
+{
+    PopplerAnnot *poppler_annot;
+
+    poppler_annot = _poppler_create_annot(POPPLER_TYPE_ANNOT_STAMP, annot);
+
+    return poppler_annot;
+}
+
+/**
+ * poppler_annot_stamp_new:
+ * @doc: a #PopplerDocument
+ * @rect: a #PopplerRectangle
+ *
+ * Creates a new Stamp annotation that will be
+ * located on @rect when added to a page. See
+ * poppler_page_add_annot()
+ *
+ * Return value: a newly created #PopplerAnnotStamp annotation
+ *
+ * Since: 22.07.0
+ **/
+PopplerAnnot *poppler_annot_stamp_new(PopplerDocument *doc, PopplerRectangle *rect)
+{
+    Annot *annot;
+    PDFRectangle pdf_rect(rect->x1, rect->y1, rect->x2, rect->y2);
+
+    annot = new AnnotStamp(doc->doc, &pdf_rect);
+
+    return _poppler_annot_stamp_new(annot);
+}
+
+static gboolean get_raw_data_from_cairo_image(cairo_surface_t *image, cairo_format_t format, const int width, const int height, const size_t rowstride_c, GByteArray *data, GByteArray *soft_mask_data)
+{
+    gboolean has_alpha = format == CAIRO_FORMAT_ARGB32;
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+    static const size_t CAIRO_B = 0;
+    static const size_t CAIRO_G = 1;
+    static const size_t CAIRO_R = 2;
+    static const size_t CAIRO_A = 3;
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+    static const size_t CAIRO_A = 0;
+    static const size_t CAIRO_R = 1;
+    static const size_t CAIRO_G = 2;
+    static const size_t CAIRO_B = 3;
+#else
+#    error "Unsupported endian type"
+#endif
+
+    cairo_surface_flush(image);
+    unsigned char *pixels_c = cairo_image_surface_get_data(image);
+
+    if (format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24) {
+        unsigned char pixel[3];
+
+        for (int h = 0; h < height; h++) {
+            unsigned char *iter_c = pixels_c + h * rowstride_c;
+            for (int w = 0; w < width; w++) {
+                pixel[0] = iter_c[CAIRO_R];
+                pixel[1] = iter_c[CAIRO_G];
+                pixel[2] = iter_c[CAIRO_B];
+                iter_c += 4;
+
+                g_byte_array_append(data, (guint8 *)pixel, 3);
+                if (has_alpha) {
+                    g_byte_array_append(soft_mask_data, (guint8 *)&iter_c[CAIRO_A], 1);
+                }
+            }
+        }
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+AnnotStampImageHelper *_poppler_convert_cairo_image_to_stamp_image_helper(cairo_surface_t *image, PDFDoc *doc, GError **error)
+{
+    AnnotStampImageHelper *annotImg;
+    GByteArray *data;
+    GByteArray *sMaskData;
+
+    int bitsPerComponent;
+    const int width = cairo_image_surface_get_width(image);
+    const int height = cairo_image_surface_get_height(image);
+    const size_t rowstride_c = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
+    cairo_format_t format = cairo_image_surface_get_format(image);
+
+    ColorSpace colorSpace;
+
+    if (format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24) {
+        colorSpace = ColorSpace::DeviceRGB;
+        bitsPerComponent = 8;
+    } else {
+        g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_INVALID, "Invalid or unsupported cairo image type %u", (unsigned int)format);
+        return nullptr;
+    }
+
+    data = g_byte_array_sized_new((guint)((width * 4) + rowstride_c) * height);
+    sMaskData = g_byte_array_sized_new((guint)((width * 4) + rowstride_c) * height);
+
+    if (!get_raw_data_from_cairo_image(image, format, width, height, rowstride_c, data, sMaskData)) {
+        g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_INVALID, "Failed to get raw data from cairo image");
+        g_byte_array_unref(data);
+        g_byte_array_unref(sMaskData);
+        return nullptr;
+    }
+
+    if (sMaskData->len > 0) {
+        AnnotStampImageHelper sMask(doc, width, height, ColorSpace::DeviceGray, 8, (char *)sMaskData->data, (int)sMaskData->len);
+        annotImg = new AnnotStampImageHelper(doc, width, height, colorSpace, bitsPerComponent, (char *)data->data, (int)data->len, sMask.getRef());
+    } else {
+        annotImg = new AnnotStampImageHelper(doc, width, height, colorSpace, bitsPerComponent, (char *)data->data, (int)data->len);
+    }
+
+    g_byte_array_unref(data);
+    g_byte_array_unref(sMaskData);
+
+    return annotImg;
+}
+
 /* Public methods */
 /**
  * poppler_annot_get_annot_type:
@@ -1911,3 +2057,145 @@ void poppler_annot_square_set_interior_color(PopplerAnnotSquare *poppler_annot,
 
     poppler_annot_geometry_set_interior_color(POPPLER_ANNOT(poppler_annot), poppler_color);
 }
+
+/**
+ * poppler_annot_stamp_get_icon:
+ * @poppler_annot: a #PopplerAnnotStamp
+ *
+ * Return value: the corresponding #PopplerAnnotStampIcon of the icon
+ *
+ * Since: 22.07.0
+ */
+PopplerAnnotStampIcon poppler_annot_stamp_get_icon(PopplerAnnotStamp *poppler_annot)
+{
+    AnnotStamp *annot;
+    const GooString *text;
+
+    g_return_val_if_fail(POPPLER_IS_ANNOT_STAMP(poppler_annot), POPPLER_ANNOT_STAMP_ICON_UNKNOWN);
+
+    annot = static_cast<AnnotStamp *>(POPPLER_ANNOT(poppler_annot)->annot);
+
+    text = annot->getIcon();
+
+    if (!text) {
+        return POPPLER_ANNOT_STAMP_ICON_NONE;
+    }
+
+    if (!text->cmp("Approved")) {
+        return POPPLER_ANNOT_STAMP_ICON_APPROVED;
+    } else if (!text->cmp("AsIs")) {
+        return POPPLER_ANNOT_STAMP_ICON_AS_IS;
+    } else if (!text->cmp("Confidential")) {
+        return POPPLER_ANNOT_STAMP_ICON_CONFIDENTIAL;
+    } else if (!text->cmp("Final")) {
+        return POPPLER_ANNOT_STAMP_ICON_FINAL;
+    } else if (!text->cmp("Experimental")) {
+        return POPPLER_ANNOT_STAMP_ICON_EXPERIMENTAL;
+    } else if (!text->cmp("Expired")) {
+        return POPPLER_ANNOT_STAMP_ICON_EXPIRED;
+    } else if (!text->cmp("NotApproved")) {
+        return POPPLER_ANNOT_STAMP_ICON_NOT_APPROVED;
+    } else if (!text->cmp("NotForPublicRelease")) {
+        return POPPLER_ANNOT_STAMP_ICON_NOT_FOR_PUBLIC_RELEASE;
+    } else if (!text->cmp("Sold")) {
+        return POPPLER_ANNOT_STAMP_ICON_SOLD;
+    } else if (!text->cmp("Departmental")) {
+        return POPPLER_ANNOT_STAMP_ICON_DEPARTMENTAL;
+    } else if (!text->cmp("ForComment")) {
+        return POPPLER_ANNOT_STAMP_ICON_FOR_COMMENT;
+    } else if (!text->cmp("ForPublicRelease")) {
+        return POPPLER_ANNOT_STAMP_ICON_FOR_PUBLIC_RELEASE;
+    } else if (!text->cmp("TopSecret")) {
+        return POPPLER_ANNOT_STAMP_ICON_TOP_SECRET;
+    }
+
+    return POPPLER_ANNOT_STAMP_ICON_UNKNOWN;
+}
+
+/**
+ * poppler_annot_stamp_set_icon:
+ * @poppler_annot: a #PopplerAnnotStamp
+ * @icon: the #PopplerAnnotStampIcon type of the icon
+ *
+ * Sets the icon of @poppler_annot to be one of the predefined values in #PopplerAnnotStampIcon
+ *
+ * Since: 22.07.0
+ */
+void poppler_annot_stamp_set_icon(PopplerAnnotStamp *poppler_annot, PopplerAnnotStampIcon icon)
+{
+    AnnotStamp *annot;
+    GooString *goo_str;
+    const gchar *text;
+
+    g_return_if_fail(POPPLER_IS_ANNOT_STAMP(poppler_annot));
+
+    annot = static_cast<AnnotStamp *>(POPPLER_ANNOT(poppler_annot)->annot);
+
+    if (icon == POPPLER_ANNOT_STAMP_ICON_NONE) {
+        annot->setIcon(nullptr);
+        return;
+    }
+
+    if (icon == POPPLER_ANNOT_STAMP_ICON_APPROVED) {
+        text = "Approved";
+    } else if (icon == POPPLER_ANNOT_STAMP_ICON_AS_IS) {
+        text = "AsIs";
+    } else if (icon == POPPLER_ANNOT_STAMP_ICON_CONFIDENTIAL) {
+        text = "Confidential";
+    } else if (icon == POPPLER_ANNOT_STAMP_ICON_FINAL) {
+        text = "Final";
+    } else if (icon == POPPLER_ANNOT_STAMP_ICON_EXPERIMENTAL) {
+        text = "Experimental";
+    } else if (icon == POPPLER_ANNOT_STAMP_ICON_EXPIRED) {
+        text = "Expired";
+    } else if (icon == POPPLER_ANNOT_STAMP_ICON_NOT_APPROVED) {
+        text = "NotApproved";
+    } else if (icon == POPPLER_ANNOT_STAMP_ICON_NOT_FOR_PUBLIC_RELEASE) {
+        text = "NotForPublicRelease";
+    } else if (icon == POPPLER_ANNOT_STAMP_ICON_SOLD) {
+        text = "Sold";
+    } else if (icon == POPPLER_ANNOT_STAMP_ICON_DEPARTMENTAL) {
+        text = "Departmental";
+    } else if (icon == POPPLER_ANNOT_STAMP_ICON_FOR_COMMENT) {
+        text = "ForComment";
+    } else if (icon == POPPLER_ANNOT_STAMP_ICON_FOR_PUBLIC_RELEASE) {
+        text = "ForPublicRelease";
+    } else if (icon == POPPLER_ANNOT_STAMP_ICON_TOP_SECRET) {
+        text = "TopSecret";
+    } else {
+        return; /* POPPLER_ANNOT_STAMP_ICON_UNKNOWN */
+    }
+
+    goo_str = new GooString(text);
+    annot->setIcon(goo_str);
+    delete goo_str;
+}
+
+/**
+ * poppler_annot_stamp_set_custom_image:
+ * @poppler_annot: a #PopplerAnnotStamp
+ * @image: an image cairo surface
+ * @error: (nullable): return location for error, or %NULL.
+ *
+ * Sets the custom image of @poppler_annot to be @image
+ *
+ * Return value: %TRUE on success, %FALSE otherwise.
+ *
+ * Since: 22.07.0
+ */
+gboolean poppler_annot_stamp_set_custom_image(PopplerAnnotStamp *poppler_annot, cairo_surface_t *image, GError **error)
+{
+    AnnotStamp *annot;
+    AnnotStampImageHelper *annot_image_helper;
+
+    g_return_val_if_fail(POPPLER_IS_ANNOT_STAMP(poppler_annot), FALSE);
+
+    annot = static_cast<AnnotStamp *>(POPPLER_ANNOT(poppler_annot)->annot);
+    annot_image_helper = _poppler_convert_cairo_image_to_stamp_image_helper(image, annot->getDoc(), error);
+    if (!annot_image_helper) {
+        return FALSE;
+    }
+    annot->setCustomImage(annot_image_helper);
+
+    return TRUE;
+}
diff --git a/glib/poppler-annot.h b/glib/poppler-annot.h
index da55172d..509ecdff 100644
--- a/glib/poppler-annot.h
+++ b/glib/poppler-annot.h
@@ -72,6 +72,10 @@ G_BEGIN_DECLS
 #define POPPLER_ANNOT_SQUARE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_SQUARE, PopplerAnnotSquare))
 #define POPPLER_IS_ANNOT_SQUARE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_SQUARE))
 
+#define POPPLER_TYPE_ANNOT_STAMP (poppler_annot_stamp_get_type())
+#define POPPLER_ANNOT_STAMP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_STAMP, PopplerAnnotStamp))
+#define POPPLER_IS_ANNOT_STAMP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_STAMP))
+
 typedef enum
 {
     POPPLER_ANNOT_UNKNOWN,
@@ -169,6 +173,25 @@ struct _PopplerAnnotCalloutLine
     gdouble y3;
 };
 
+typedef enum
+{
+    POPPLER_ANNOT_STAMP_ICON_UNKNOWN = 0,
+    POPPLER_ANNOT_STAMP_ICON_APPROVED,
+    POPPLER_ANNOT_STAMP_ICON_AS_IS,
+    POPPLER_ANNOT_STAMP_ICON_CONFIDENTIAL,
+    POPPLER_ANNOT_STAMP_ICON_FINAL,
+    POPPLER_ANNOT_STAMP_ICON_EXPERIMENTAL,
+    POPPLER_ANNOT_STAMP_ICON_EXPIRED,
+    POPPLER_ANNOT_STAMP_ICON_NOT_APPROVED,
+    POPPLER_ANNOT_STAMP_ICON_NOT_FOR_PUBLIC_RELEASE,
+    POPPLER_ANNOT_STAMP_ICON_SOLD,
+    POPPLER_ANNOT_STAMP_ICON_DEPARTMENTAL,
+    POPPLER_ANNOT_STAMP_ICON_FOR_COMMENT,
+    POPPLER_ANNOT_STAMP_ICON_FOR_PUBLIC_RELEASE,
+    POPPLER_ANNOT_STAMP_ICON_TOP_SECRET,
+    POPPLER_ANNOT_STAMP_ICON_NONE
+} PopplerAnnotStampIcon;
+
 POPPLER_PUBLIC
 GType poppler_annot_get_type(void) G_GNUC_CONST;
 POPPLER_PUBLIC
@@ -328,6 +351,18 @@ void poppler_annot_square_set_interior_color(PopplerAnnotSquare *poppler_annot,
 POPPLER_PUBLIC
 PopplerColor *poppler_annot_square_get_interior_color(PopplerAnnotSquare *poppler_annot);
 
+/* PopplerAnnotStamp */
+POPPLER_PUBLIC
+GType poppler_annot_stamp_get_type(void) G_GNUC_CONST;
+POPPLER_PUBLIC
+PopplerAnnot *poppler_annot_stamp_new(PopplerDocument *doc, PopplerRectangle *rect);
+POPPLER_PUBLIC
+PopplerAnnotStampIcon poppler_annot_stamp_get_icon(PopplerAnnotStamp *poppler_annot);
+POPPLER_PUBLIC
+void poppler_annot_stamp_set_icon(PopplerAnnotStamp *poppler_annot, PopplerAnnotStampIcon icon);
+POPPLER_PUBLIC
+gboolean poppler_annot_stamp_set_custom_image(PopplerAnnotStamp *poppler_annot, cairo_surface_t *image, GError **error);
+
 G_END_DECLS
 
 #endif /* __POPPLER_ANNOT_H__ */
diff --git a/glib/poppler-page.cc b/glib/poppler-page.cc
index 8e361a58..7f47cd5b 100644
--- a/glib/poppler-page.cc
+++ b/glib/poppler-page.cc
@@ -1337,6 +1337,9 @@ GList *poppler_page_get_annot_mapping(PopplerPage *page)
         case Annot::typeStrikeOut:
             mapping->annot = _poppler_annot_text_markup_new(annot);
             break;
+        case Annot::typeStamp:
+            mapping->annot = _poppler_annot_stamp_new(annot);
+            break;
         default:
             mapping->annot = _poppler_annot_new(annot);
             break;
diff --git a/glib/poppler-private.h b/glib/poppler-private.h
index ce2c4733..758c7021 100644
--- a/glib/poppler-private.h
+++ b/glib/poppler-private.h
@@ -156,6 +156,7 @@ PopplerAnnot *_poppler_annot_screen_new(PopplerDocument *doc, Annot *annot);
 PopplerAnnot *_poppler_annot_line_new(Annot *annot);
 PopplerAnnot *_poppler_annot_circle_new(Annot *annot);
 PopplerAnnot *_poppler_annot_square_new(Annot *annot);
+PopplerAnnot *_poppler_annot_stamp_new(Annot *annot);
 
 const PDFRectangle *_poppler_annot_get_cropbox(PopplerAnnot *poppler_annot);
 
@@ -163,6 +164,7 @@ char *_poppler_goo_string_to_utf8(const GooString *s);
 gboolean _poppler_convert_pdf_date_to_gtime(const GooString *date, time_t *gdate);
 GDateTime *_poppler_convert_pdf_date_to_date_time(const GooString *date);
 GooString *_poppler_convert_date_time_to_pdf_date(GDateTime *datetime);
+AnnotStampImageHelper *_poppler_convert_cairo_image_to_stamp_image_helper(const cairo_surface_t *image);
 
 void _poppler_error_cb(ErrorCategory category, Goffset pos, const char *message);
 
diff --git a/glib/poppler.h b/glib/poppler.h
index e2001504..710b7b23 100644
--- a/glib/poppler.h
+++ b/glib/poppler.h
@@ -219,6 +219,7 @@ typedef struct _PopplerStructureElementIter PopplerStructureElementIter;
 typedef struct _PopplerTextSpan PopplerTextSpan;
 typedef struct _PopplerPageRange PopplerPageRange;
 typedef struct _PopplerSignatureInfo PopplerSignatureInfo;
+typedef struct _PopplerAnnotStamp PopplerAnnotStamp;
 
 /**
  * PopplerBackend:
diff --git a/glib/reference/poppler-sections.txt b/glib/reference/poppler-sections.txt
index 0a20ffae..f027cd03 100644
--- a/glib/reference/poppler-sections.txt
+++ b/glib/reference/poppler-sections.txt
@@ -455,6 +455,7 @@ PopplerAnnotMarkup
 PopplerAnnotMovie
 PopplerAnnotScreen
 PopplerAnnotSquare
+PopplerAnnotStamp
 PopplerAnnotText
 PopplerAnnotTextMarkup
 PopplerAnnotExternalDataType
@@ -462,6 +463,7 @@ PopplerAnnotFlag
 PopplerAnnotFreeTextQuadding
 PopplerAnnotMarkupReplyType
 PopplerAnnotTextState
+PopplerAnnotStampIcon
 PopplerAnnotType
 poppler_annot_get_annot_type
 POPPLER_ANNOT_TEXT_ICON_CIRCLE
@@ -516,6 +518,10 @@ poppler_annot_set_rectangle
 poppler_annot_square_get_interior_color
 poppler_annot_square_new
 poppler_annot_square_set_interior_color
+poppler_annot_stamp_get_icon
+poppler_annot_stamp_new
+poppler_annot_stamp_set_custom_image
+poppler_annot_stamp_set_icon
 poppler_annot_text_get_icon
 poppler_annot_text_get_is_open
 poppler_annot_text_get_state
@@ -539,6 +545,7 @@ POPPLER_ANNOT_MARKUP
 POPPLER_ANNOT_MOVIE
 POPPLER_ANNOT_SCREEN
 POPPLER_ANNOT_SQUARE
+POPPLER_ANNOT_STAMP
 POPPLER_ANNOT_TEXT
 POPPLER_ANNOT_TEXT_MARKUP
 POPPLER_IS_ANNOT
@@ -550,6 +557,7 @@ POPPLER_IS_ANNOT_MARKUP
 POPPLER_IS_ANNOT_MOVIE
 POPPLER_IS_ANNOT_SCREEN
 POPPLER_IS_ANNOT_SQUARE
+POPPLER_IS_ANNOT_STAMP
 POPPLER_IS_ANNOT_TEXT
 POPPLER_IS_ANNOT_TEXT_MARKUP
 POPPLER_TYPE_ANNOT
@@ -566,6 +574,7 @@ POPPLER_TYPE_ANNOT_MARKUP_REPLY_TYPE
 POPPLER_TYPE_ANNOT_MOVIE
 POPPLER_TYPE_ANNOT_SCREEN
 POPPLER_TYPE_ANNOT_SQUARE
+POPPLER_TYPE_ANNOT_STAMP
 POPPLER_TYPE_ANNOT_TEXT
 POPPLER_TYPE_ANNOT_TEXT_MARKUP
 POPPLER_TYPE_ANNOT_TEXT_STATE
@@ -584,6 +593,7 @@ poppler_annot_markup_reply_type_get_type
 poppler_annot_movie_get_type
 poppler_annot_screen_get_type
 poppler_annot_square_get_type
+poppler_annot_stamp_get_type
 poppler_annot_text_get_type
 poppler_annot_text_markup_get_type
 poppler_annot_text_state_get_type
diff --git a/glib/reference/poppler.types b/glib/reference/poppler.types
index da524e0e..1ab636b3 100644
--- a/glib/reference/poppler.types
+++ b/glib/reference/poppler.types
@@ -18,6 +18,7 @@ poppler_annot_markup_reply_type_get_type
 poppler_annot_movie_get_type
 poppler_annot_screen_get_type
 poppler_annot_square_get_type
+poppler_annot_stamp_get_type
 poppler_annot_text_get_type
 poppler_annot_text_markup_get_type
 poppler_annot_text_state_get_type


More information about the poppler mailing list