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)
121 {
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)
135 {
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)
166 {
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)
192 {
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)
204 {
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)
221 {
222 /* nothing to do for now */
223 } /* gtk_tpdfv_init */
225 /*
226 * Public API.
227 */
229 GtkWidget *
230 gtk_tpdfv_new(const char *filename)
231 {
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)
245 {
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)
257 {
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)
269 {
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)
279 {
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)
289 {
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)
299 {
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)
312 {
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)
322 {
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)
332 {
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)
342 {
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)
352 {
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 : */