0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

gtk3でpdf表示とスワイプ検知のサンプル

Posted at
#include <gtk/gtk.h>
#include <stdbool.h>
#include <mupdf/fitz.h>

const char *Explain = R"(
# Preparation

Change PDF_FILE in the source code to the pdf file you want.

# Build 

## Requirement

- [gtk3](https://www.gtk.org/docs/installations/linux/)
- [mupdf](https://mupdf.com/)

## Command

```
gcc -g -O0  `pkg-config --cflags gtk+-3.0` -c gtk_sample.c
gcc `pkg-config --libs gtk+-3.0` -lmupdf gtk_sample.o -o gtk_sample
```

# Execute
```
./gtk_sample
```
)";

#define PDF_FILE "/home/tyano/workspace/pdf/mtex_sample/sample.pdf"
#define SWIPE_MARGIN (50)

#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

#define d(x, ...)                                                                      \
    do                                                                                 \
    {                                                                                  \
        g_print("%s(%d) %s " x "\n", __FILENAME__, __LINE__, __func__, ##__VA_ARGS__); \
    } while (0)

#define ERR_RETn(c)            \
    do                         \
    {                          \
        if (c)                 \
            goto error_return; \
    } while (0)


/**
 * There is no button definition in gtk.
 * 
 * The following is the gtk explanation.
 * 
 * /usr/include/gtk-3.0/gdk/gdkevents.h:706L
 * 
 * @button: the button which was pressed or released, numbered from 1 to 5.
 *   Normally button 1 is the left mouse button, 2 is the middle button,
 *   and 3 is the right button. On 2-button mice, the middle button can
 *   often be simulated by pressing both mouse buttons together.
 * 
 */
#define MOUSE_BUTTON_LEFT 1
#define MOUSE_BUTTON_MIDDLE 2
#define MOUSE_BUTTON_RIGHT 3

typedef struct
{
    fz_context *ctx;
    fz_document *doc;
    fz_page *page;
    int width;
    int height;
} PdfData;

static void draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data)
{
    PdfData *pdf = (PdfData *)data;
    fz_pixmap *pix;
    fz_matrix ctm;

    ctm = fz_scale(1, 1);

    pix = fz_new_pixmap_from_page_number(pdf->ctx, pdf->doc, 0, ctm, fz_device_rgb(pdf->ctx), 0);

    cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, pix->w, pix->h);
    unsigned char *surface_data = cairo_image_surface_get_data(surface);
    int rowstride = cairo_image_surface_get_stride(surface);

    for (int i = 0; i < pix->h; i++)
    {
        uint8_t *s = &pix->samples[i * pix->stride];
        for (int j = 0; j < pix->w; j++)
        {
            guchar *p = surface_data + i * rowstride + j * 4;
            if (pix->n == 4)
            {
                p[0] = s[2];
                p[1] = s[1];
                p[2] = s[0];
            }
            else
            {
                p[0] = p[1] = p[2] = s[0];
            }
            s += pix->n;
        }
    }
    cairo_set_source_surface(cr, surface, 0, 0);
    cairo_paint(cr);
    cairo_surface_destroy(surface);

    fz_drop_pixmap(pdf->ctx, pix);
}

//static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer data)
static gboolean motion_notify_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
    static gdouble start_x = -1;
    static gdouble start_y = -1;
    gdouble dx, dy;

    ERR_RETn (event->button != MOUSE_BUTTON_LEFT);

    if (event->type == GDK_BUTTON_PRESS)
    {
        // d("push %x %x (%.2f,%.2f)", event->button, event->state, event->x, event->y);
        start_x = event->x;
        start_y = event->y;
    }
    else if (event->type == GDK_BUTTON_RELEASE)
    {
        // d("release %x %x (%.2f,%.2f)", event->button, event->state, event->x, event->y);
        dx = event->x - start_x;
        dy = event->y - start_y;
        if (abs(dx) < abs(dy))
        {
        }
        else
        {
            if (dx < -SWIPE_MARGIN)
            {
                d("left swiped");
            }
            else if (dx > SWIPE_MARGIN)
            {
                d("right swiped");
            }
        }
    }

error_return:
    return TRUE;
}

static void activate(GtkApplication *app, gpointer user_data)
{
    GtkWidget *window;
    GtkDrawingArea *drawing_area;
    PdfData *pdf = g_new(PdfData, 1);

    pdf->ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
    if (!pdf->ctx)
    {
        g_error("Could not create MuPDF context\n");
    }

    fz_register_document_handlers(pdf->ctx);

    // Open the PDF document
    fz_try(pdf->ctx)
        pdf->doc = fz_open_document(pdf->ctx, PDF_FILE);
    fz_catch(pdf->ctx)
    {
        fz_report_error(pdf->ctx);
        d("cannot open document");
        fz_drop_context(pdf->ctx);
        return;
    }

    int page_count = fz_count_pages(pdf->ctx, pdf->doc);

    d("ttl page:%d", page_count);
    // Load the first page
    pdf->page = fz_load_page(pdf->ctx, pdf->doc, 0);
    fz_rect bounds = fz_bound_page(pdf->ctx, pdf->page);
    pdf->width = bounds.x1 - bounds.x0;
    pdf->height = bounds.y1 - bounds.y0;

    d("w:%d h:%d", pdf->width, pdf->height);
    // Create GTK window
    window = gtk_application_window_new(app);
    gtk_window_set_default_size(GTK_WINDOW(window), pdf->width, pdf->height);
    gtk_window_set_decorated(GTK_WINDOW(window), FALSE);

    // Create a drawing area and set the draw callback
    drawing_area = (GtkDrawingArea *)gtk_drawing_area_new();
    gtk_container_add(GTK_CONTAINER(window), (GtkWidget *)drawing_area);
    //    gtk_widget_set_events((GtkWidget*)drawing_area, gtk_widget_get_events ((GtkWidget*)drawing_area) | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK );
    gtk_widget_set_events((GtkWidget *)drawing_area, gtk_widget_get_events((GtkWidget *)drawing_area) | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
    //     g_signal_connect(G_OBJECT(drawing_area), "motion-notify-event", G_CALLBACK(motion_notify_event), NULL);
    g_signal_connect(G_OBJECT(drawing_area), "button-press-event", G_CALLBACK(motion_notify_event), NULL);
    g_signal_connect(G_OBJECT(drawing_area), "button-release-event", G_CALLBACK(motion_notify_event), NULL);

    g_signal_connect((GtkWidget *)drawing_area, "draw", G_CALLBACK(draw_callback), pdf);

    gtk_widget_show_all(window);
}

int main(int argc, char **argv)
{
    GtkApplication *app;
    int status;

    (void)Explain;

    app = gtk_application_new("A.B", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
    status = g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);

    return status;
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?