]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/pdf-document.cc
add zooming.
[evince.git] / pdf / xpdf / pdf-document.cc
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /* pdfdocument.h: Implementation of EvDocument for PDF
3  * Copyright (C) 2004, Red Hat, Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include "gpdf-g-switch.h"
21 #include "pdf-document.h"
22 #include "gpdf-g-switch.h"
23
24 #include "GlobalParams.h"
25 #include "GDKSplashOutputDev.h"
26 #include "PDFDoc.h"
27
28 typedef struct _PdfDocumentClass PdfDocumentClass;
29
30 #define PDF_DOCUMENT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), PDF_TYPE_DOCUMENT, PdfDocumentClass))
31 #define PDF_IS_DOCUMENT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), PDF_TYPE_DOCUMENT))
32 #define PDF_DOCUMENT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), PDF_TYPE_DOCUMENT, PdfDocumentClass))
33
34 struct _PdfDocumentClass
35 {
36         GObjectClass parent_class;
37 };
38
39 struct _PdfDocument
40 {
41         GObject parent_instance;
42
43         int page;
44         int page_x_offset;
45         int page_y_offset;
46         double scale;
47         GdkDrawable *target;
48
49         GDKSplashOutputDev *out;
50         PDFDoc *doc;
51
52         gboolean page_valid;
53 };
54
55 static void pdf_document_document_iface_init (EvDocumentIface *iface);
56
57 G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT,
58                          { G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
59                                                   pdf_document_document_iface_init) });
60
61 static gboolean
62 document_validate_page (PdfDocument *pdf_document)
63 {
64         if (!pdf_document->page_valid) {
65                 pdf_document->doc->displayPage (pdf_document->out, pdf_document->page,
66                                                 72 * pdf_document->scale,
67                                                 72 * pdf_document->scale,
68                                                 0, gTrue, gTrue);
69                 
70                 pdf_document->page_valid = TRUE;
71         }
72 }
73
74 static gboolean
75 pdf_document_load (EvDocument  *document,
76                    const char  *uri,
77                    GError     **error)
78 {
79         PdfDocument *pdf_document = PDF_DOCUMENT (document);
80         PDFDoc *newDoc;
81         int err;
82         char *filename;
83         GString *filename_g;
84         
85         if (!globalParams) {
86                 globalParams = new GlobalParams("/etc/xpdfrc");
87                 globalParams->setupBaseFontsFc(NULL);
88         }
89
90         filename = g_filename_from_uri (uri, NULL, error);
91         if (!filename)
92                 return FALSE;
93
94         filename_g = new GString (filename);
95         g_free (filename);
96
97         // open the PDF file, assumes ownership of filename_g
98         newDoc = new PDFDoc(filename_g, 0, 0);
99
100         if (!newDoc->isOk()) {
101                 err = newDoc->getErrorCode();
102                 delete newDoc;
103
104                 /* FIXME: Add a real error enum to EvDocument */
105                 g_set_error (error, G_FILE_ERROR,
106                              G_FILE_ERROR_FAILED,
107                              "Failed to load document (error %d) '%s'\n",
108                              err,
109                              uri);
110                 
111                 return FALSE;
112         }
113
114         if (pdf_document->doc)
115                 delete pdf_document->doc;
116         pdf_document->doc = newDoc;
117
118         pdf_document->page = 1;
119
120         if (pdf_document->out)
121                 pdf_document->out->startDoc(pdf_document->doc->getXRef());
122
123         pdf_document->page_valid = FALSE;
124         
125         return TRUE;
126 }
127
128 static int
129 pdf_document_get_n_pages (EvDocument  *document)
130 {
131         PdfDocument *pdf_document = PDF_DOCUMENT (document);
132
133         if (pdf_document->doc)
134                 return pdf_document->doc->getNumPages();
135         else
136                 return 1;
137 }
138
139 static void
140 pdf_document_set_page (EvDocument  *document,
141                        int          page)
142 {
143         PdfDocument *pdf_document = PDF_DOCUMENT (document);
144
145         page = CLAMP (page, 1, ev_document_get_n_pages (document));
146
147         if (page != pdf_document->page) {
148                 pdf_document->page = page;
149                 pdf_document->page_valid = FALSE;
150         }
151
152 }
153
154 static int
155 pdf_document_get_page (EvDocument  *document)
156 {
157         PdfDocument *pdf_document = PDF_DOCUMENT (document);
158
159         return pdf_document->page;
160 }
161
162 static void
163 redraw_callback (void *data)
164 {
165         /* Need to hook up through a EvDocument callback? */
166 }
167
168 static void
169 pdf_document_set_target (EvDocument  *document,
170                          GdkDrawable *target)
171 {
172         PdfDocument *pdf_document = PDF_DOCUMENT (document);
173         
174         if (pdf_document->target != target) {
175                 if (pdf_document->target)
176                         g_object_unref (pdf_document->target);
177                 
178                 pdf_document->target = target;
179
180                 if (pdf_document->target)
181                         g_object_ref (pdf_document->target);
182
183                 if (pdf_document->out) {
184                         delete pdf_document->out;
185                         pdf_document->out = NULL;
186                 }
187
188                 if (pdf_document->target) {
189                         pdf_document->out = new GDKSplashOutputDev (gdk_drawable_get_screen (pdf_document->target),
190                                                          redraw_callback, (void*) document);
191
192                         if (pdf_document->doc)
193                                 pdf_document->out->startDoc(pdf_document->doc->getXRef());
194
195                 }
196
197                 pdf_document->page_valid = FALSE;
198         }
199 }
200
201 static void
202 pdf_document_set_scale (EvDocument  *document,
203                         double       scale)
204 {
205         PdfDocument *pdf_document = PDF_DOCUMENT (document);
206         
207         if (pdf_document->scale != scale) {
208                 pdf_document->scale = scale;
209                 pdf_document->page_valid = FALSE;
210         }
211 }
212
213 static void
214 pdf_document_set_page_offset (EvDocument  *document,
215                               int          x,
216                               int          y)
217 {
218         PdfDocument *pdf_document = PDF_DOCUMENT (document);
219         
220         pdf_document->page_x_offset = x;
221         pdf_document->page_y_offset = y;
222 }
223
224 static void
225 pdf_document_get_page_size (EvDocument   *document,
226                             int          *width,
227                             int          *height)
228 {
229         PdfDocument *pdf_document = PDF_DOCUMENT (document);
230
231         if (document_validate_page (pdf_document)) {
232                 if (width)
233                         *width = pdf_document->out->getBitmapWidth();
234                 if (height)
235                         *height = pdf_document->out->getBitmapHeight();
236         } else {
237                 if (width)
238                         *width = 1;
239                 if (height)
240                         *height = 1;
241         }
242 }
243
244 static void
245 pdf_document_render (EvDocument  *document,
246                      int          clip_x,
247                      int          clip_y,
248                      int          clip_width,
249                      int          clip_height)
250 {
251         PdfDocument *pdf_document = PDF_DOCUMENT (document);
252         GdkRectangle page;
253         GdkRectangle draw;
254
255         if (!document_validate_page (pdf_document) || !pdf_document->target)
256                 return;
257         
258         page.x = pdf_document->page_x_offset;
259         page.y = pdf_document->page_y_offset;
260         page.width = pdf_document->out->getBitmapWidth();
261         page.height = pdf_document->out->getBitmapHeight();
262
263         draw.x = clip_x;
264         draw.y = clip_y;
265         draw.width = clip_width;
266         draw.height = clip_height;
267         
268         if (gdk_rectangle_intersect (&page, &draw, &draw))
269                 pdf_document->out->redraw (draw.x - page.x, draw.y - page.y,
270                                            pdf_document->target,
271                                            draw.x, draw.y,
272                                            draw.width, draw.height);
273 }
274
275 static void
276 pdf_document_begin_find (EvDocument   *document,
277                          const char   *search_string,
278                          gboolean      case_sensitive)
279 {
280         /* FIXME make this incremental (idle handler) and multi-page */
281         /* Right now it's fully synchronous plus only does the current page */
282         
283         PdfDocument *pdf_document = PDF_DOCUMENT (document);
284         gunichar *ucs4;
285         glong ucs4_len;
286         int xMin, yMin, xMax, yMax;
287         GArray *results;
288         EvFindResult result;
289
290         /* FIXME case_sensitive (right now XPDF
291          * code is always case insensitive for ASCII
292          * and case sensitive for all other languaages)
293          */
294         
295         g_assert (sizeof (gunichar) == sizeof (Unicode));
296         ucs4 = g_utf8_to_ucs4_fast (search_string, -1,
297                                     &ucs4_len);
298
299         results = g_array_new (FALSE,
300                                FALSE,
301                                sizeof (EvFindResult));
302
303         if (pdf_document->out->findText (ucs4, ucs4_len,
304                                          gTrue, gTrue, // startAtTop, stopAtBottom
305                                          gFalse, gFalse, // startAtLast, stopAtLast
306                                          &xMin, &yMin, &xMax, &yMax)) {
307
308                 result.page_num = pdf_document->page;
309
310                 result.highlight_area.x = xMin;
311                 result.highlight_area.y = yMin;
312                 result.highlight_area.width = xMax - xMin;
313                 result.highlight_area.height = yMax - yMin;
314
315                 g_array_append_val (results, result);
316         
317                 /* Now find further results */
318
319                 while (pdf_document->out->findText (ucs4, ucs4_len,
320                                                     gFalse, gTrue,
321                                                     gTrue, gFalse,
322                                                     &xMin, &yMin, &xMax, &yMax)) {
323                         
324                         result.page_num = pdf_document->page;
325                         
326                         result.highlight_area.x = xMin;
327                         result.highlight_area.y = yMin;
328                         result.highlight_area.width = xMax - xMin;
329                         result.highlight_area.height = yMax - yMin;
330                         
331                         g_array_append_val (results, result);
332                 }
333         }
334
335         ev_document_found (document,
336                            (EvFindResult*) results->data,
337                            results->len,
338                            1.0);
339
340         g_array_free (results, TRUE);
341 }
342
343 static void
344 pdf_document_end_find (EvDocument   *document)
345 {
346         PdfDocument *pdf_document = PDF_DOCUMENT (document);
347
348         /* FIXME this will do something once begin_find queues
349          * an incremental find
350          */
351
352         // this should probably be shared among EvDocument
353         // implementations somehow?
354         ev_document_found (document, NULL, 0, 1.0);
355 }
356
357 static void
358 pdf_document_finalize (GObject *object)
359 {
360         PdfDocument *pdf_document = PDF_DOCUMENT (object);
361
362         if (pdf_document->target)
363                 g_object_unref (pdf_document->target);
364
365         if (pdf_document->out)
366                 delete pdf_document->out;
367         if (pdf_document->doc)
368                 delete pdf_document->doc;
369
370 }
371
372 static void
373 pdf_document_class_init (PdfDocumentClass *klass)
374 {
375         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
376   
377         gobject_class->finalize = pdf_document_finalize;
378 }
379
380 static void
381 pdf_document_document_iface_init (EvDocumentIface *iface)
382 {
383         iface->load = pdf_document_load;
384         iface->get_n_pages = pdf_document_get_n_pages;
385         iface->set_page = pdf_document_set_page;
386         iface->get_page = pdf_document_get_page;
387         iface->set_scale = pdf_document_set_scale;
388         iface->set_target = pdf_document_set_target;
389         iface->set_page_offset = pdf_document_set_page_offset;
390         iface->get_page_size = pdf_document_get_page_size;
391         iface->render = pdf_document_render;
392         iface->begin_find = pdf_document_begin_find;
393         iface->end_find = pdf_document_end_find;
394 }
395
396 static void
397 pdf_document_init (PdfDocument *pdf_document)
398 {
399         pdf_document->page = 1;
400         pdf_document->page_x_offset = 0;
401         pdf_document->page_y_offset = 0;
402         pdf_document->scale = 1.;
403         
404         pdf_document->page_valid = FALSE;
405 }
406