Code

b04d2c6f9cb3ffb177e039dc45f10fa268b3f84d
[tpdfview.git] / src / gtk-tpdfv.c
1 /*
2  * tpdfview - src/gtk-tpdfv.c
3  * Copyright (C) 2011 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 /*
29  * This module provides a Gtk widget to display PDF files.
30  */
32 #include "gtk-tpdfv.h"
34 #include <assert.h>
36 #include <errno.h>
38 #include <cairo.h>
39 #include <gtk/gtk.h>
41 #include <poppler.h>
43 #include <stdlib.h>
44 #include <string.h>
46 typedef struct {
47         char *filename;
49         PopplerDocument *doc;
50         PopplerPage     *current_page;
52         int current_page_no;
53         int total_pages;
55         double zoom_factor;
57         double delta_x;
58         double delta_y;
59 } gtk_tpdfv_t;
61 GType gtk_tpdfv_get_type(void);
62 G_DEFINE_TYPE(GtkTPDFV, gtk_tpdfv, GTK_TYPE_DRAWING_AREA);
64 #define GTK_TPDFV_GET_PRIVATE(obj) \
65         (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_TPDFV, gtk_tpdfv_t))
67 /*
68  * Private helper functions.
69  */
71 static gboolean
72 tpdfv_init(gtk_tpdfv_t *pdf, const char *filename)
73 {
74         GError *err = NULL;
76         if (strstr(filename, "://"))
77                 pdf->filename = strdup(filename);
78         else {
79                 size_t len = strlen("file://") + strlen(filename);
80                 pdf->filename = (char *)malloc(len + 1);
81                 if (pdf->filename) {
82                         *pdf->filename = '\0';
83                         strncat(pdf->filename, "file://", len);
84                         strncat(pdf->filename, filename, len - strlen("file://"));
85                 }
86         }
88         /* XXX: error reporting mechanism */
90         if (! pdf->filename) {
91                 char errbuf[1024];
92                 strerror_r(errno, errbuf, sizeof(errbuf));
93                 fprintf(stderr, "Failed to allocate string: %s.\n", errbuf);
94                 return FALSE;
95         }
97         pdf->doc = poppler_document_new_from_file(pdf->filename,
98                         /* password = */ NULL, &err);
99         if (! pdf->doc) {
100                 fprintf(stderr, "Failed to open PDF: %s.\n", err->message);
101                 return FALSE;
102         }
104         pdf->current_page_no = 0;
105         pdf->current_page = poppler_document_get_page(pdf->doc,
106                         pdf->current_page_no);
107         if (! pdf->current_page) {
108                 fprintf(stderr, "Failed to open first page of the document.\n");
109                 return FALSE;
110         }
112         pdf->total_pages = poppler_document_get_n_pages(pdf->doc);
114         pdf->zoom_factor = 1.0;
115         pdf->delta_x = pdf->delta_y = 0.0;
116         return TRUE;
117 } /* tpdfv_init */
119 static void
120 tpdfv_clean(gtk_tpdfv_t *pdf)
122         if (! pdf)
123                 return;
125         g_object_unref(pdf->doc);
126         pdf->doc          = NULL;
127         pdf->current_page = NULL;
128         free(pdf->filename);
129         pdf->filename = NULL;
130         return;
131 } /* tpdfv_clean */
133 static void
134 do_redraw(GtkWidget *widget)
136         GdkRegion *region;
137         gtk_tpdfv_t *pdf;
139         pdf = GTK_TPDFV_GET_PRIVATE(widget);
141         if (poppler_page_get_index(pdf->current_page)
142                         != pdf->current_page_no) {
143                 pdf->current_page = poppler_document_get_page(pdf->doc,
144                 pdf->current_page_no);
146                 if (! pdf->current_page) {
147                         fprintf(stderr, "Failed to open page %i of the document.\n",
148                                         pdf->current_page_no + 1);
149                         return;
150                 }
151         }
153         region = gdk_drawable_get_clip_region(widget->window);
154         gdk_window_invalidate_region(widget->window, region, TRUE);
155         gdk_window_process_updates(widget->window, TRUE);
157         gdk_region_destroy(region);
158 } /* do_redraw */
160 /*
161  * Gtk+ class definition.
162  */
164 static gboolean
165 gtk_tpdfv_expose(GtkWidget *tpdfv, GdkEventExpose *event)
167         gtk_tpdfv_t *pdf;
168         cairo_t *cr;
170         pdf = GTK_TPDFV_GET_PRIVATE(tpdfv);
172         if (! pdf)
173                 return FALSE;
175         cr = gdk_cairo_create(tpdfv->window);
176         cairo_rectangle(cr, event->area.x, event->area.y,
177                         event->area.width, event->area.height);
178         cairo_clip(cr);
180         /* zoom, scrolling */
181         cairo_scale(cr, pdf->zoom_factor, pdf->zoom_factor);
182         cairo_translate(cr, pdf->delta_x, pdf->delta_y);
184         poppler_page_render(pdf->current_page, cr);
186         cairo_destroy(cr);
187         return FALSE;
188 } /* gtk_tpdfv_expose */
190 static void
191 gtk_tpdfv_destroy(GtkObject *obj)
193         GtkTPDFV    *tpdfv = GTK_TPDFV(obj);
194         gtk_tpdfv_t *pdf;
196         pdf = GTK_TPDFV_GET_PRIVATE(tpdfv);
197         tpdfv_clean(pdf);
199         GTK_OBJECT_CLASS(gtk_tpdfv_parent_class)->destroy(obj);
200 } /* gtk_tpdfv_destroy */
202 static void
203 gtk_tpdfv_class_init(GtkTPDFVClass *class)
205         GObjectClass   *goclass;
206         GtkObjectClass *oclass;
207         GtkWidgetClass *wclass;
209         goclass = G_OBJECT_CLASS(class);
210         g_type_class_add_private(goclass, sizeof(gtk_tpdfv_t));
212         oclass = GTK_OBJECT_CLASS(class);
213         oclass->destroy = gtk_tpdfv_destroy;
215         wclass = GTK_WIDGET_CLASS(class);
216         wclass->expose_event = gtk_tpdfv_expose;
217 } /* gtk_tpdfv_class_init */
219 static void
220 gtk_tpdfv_init(GtkTPDFV __attribute__((unused)) *tpdfv)
222         /* nothing to do for now */
223 } /* gtk_tpdfv_init */
225 /*
226  * Public API.
227  */
229 GtkWidget *
230 gtk_tpdfv_new(const char *filename)
232         GtkTPDFV    *tpdfv;
233         gtk_tpdfv_t *pdf;
235         tpdfv = g_object_new(GTK_TYPE_TPDFV, NULL);
236         pdf   = GTK_TPDFV_GET_PRIVATE(tpdfv);
238         if (TRUE != tpdfv_init(pdf, filename))
239                 return NULL;
240         return GTK_WIDGET(tpdfv);
241 } /* gtk_tpdfv_new */
243 void
244 gtk_tpdfv_page_up(GtkWidget *widget)
246         gtk_tpdfv_t *pdf;
248         pdf = GTK_TPDFV_GET_PRIVATE(widget);
250         if (pdf->current_page_no)
251                 --pdf->current_page_no;
252         do_redraw(widget);
253 } /* gtk_tpdfv_page_up */
255 void
256 gtk_tpdfv_page_down(GtkWidget *widget)
258         gtk_tpdfv_t *pdf;
260         pdf = GTK_TPDFV_GET_PRIVATE(widget);
262         if (pdf->current_page_no < pdf->total_pages - 1)
263                 ++pdf->current_page_no;
264         do_redraw(widget);
265 } /* gtk_tpdfv_page_down */
267 void
268 gtk_tpdfv_first_page(GtkWidget *widget)
270         gtk_tpdfv_t *pdf;
272         pdf = GTK_TPDFV_GET_PRIVATE(widget);
273         pdf->current_page_no = 0;
274         do_redraw(widget);
275 } /* gtk_tpdfv_first_page */
277 void
278 gtk_tpdfv_last_page(GtkWidget *widget)
280         gtk_tpdfv_t *pdf;
282         pdf = GTK_TPDFV_GET_PRIVATE(widget);
283         pdf->current_page_no = pdf->total_pages - 1;
284         do_redraw(widget);
285 } /* gtk_tpdfv_last_page */
287 void
288 gtk_tpdfv_zoom_in(GtkWidget *widget)
290         gtk_tpdfv_t *pdf;
292         pdf = GTK_TPDFV_GET_PRIVATE(widget);
293         pdf->zoom_factor *= 1.2;
294         do_redraw(widget);
295 } /* gtk_tpdfv_zoom_in */
297 void
298 gtk_tpdfv_zoom_out(GtkWidget *widget)
300         gtk_tpdfv_t *pdf;
302         pdf = GTK_TPDFV_GET_PRIVATE(widget);
304         if (pdf->zoom_factor > DBL_EPSILON * 2.0) {
305                 pdf->zoom_factor /= 1.2;
306                 do_redraw(widget);
307         }
308 } /* gtk_tpdfv_zoom_out */
310 void
311 gtk_tpdfv_zoom_1(GtkWidget *widget)
313         gtk_tpdfv_t *pdf;
315         pdf = GTK_TPDFV_GET_PRIVATE(widget);
316         pdf->zoom_factor = 1.0;
317         do_redraw(widget);
318 } /* gtk_tpdfv_zoom_1 */
320 void
321 gtk_tpdfv_scroll_up(GtkWidget *widget)
323         gtk_tpdfv_t *pdf;
325         pdf = GTK_TPDFV_GET_PRIVATE(widget);
326         pdf->delta_y += 10.0;
327         do_redraw(widget);
328 } /* gtk_tpdfv_scroll_up */
330 void
331 gtk_tpdfv_scroll_down(GtkWidget *widget)
333         gtk_tpdfv_t *pdf;
335         pdf = GTK_TPDFV_GET_PRIVATE(widget);
336         pdf->delta_y -= 10.0;
337         do_redraw(widget);
338 } /* gtk_tpdfv_scroll_down */
340 void
341 gtk_tpdfv_scroll_left(GtkWidget *widget)
343         gtk_tpdfv_t *pdf;
345         pdf = GTK_TPDFV_GET_PRIVATE(widget);
346         pdf->delta_x += 10.0;
347         do_redraw(widget);
348 } /* gtk_tpdfv_scroll_left */
350 void
351 gtk_tpdfv_scroll_right(GtkWidget *widget)
353         gtk_tpdfv_t *pdf;
355         pdf = GTK_TPDFV_GET_PRIVATE(widget);
356         pdf->delta_x -= 10.0;
357         do_redraw(widget);
358 } /* gtk_tpdfv_scroll_right */
360 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */