]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/pdf-document.cc
8323ebe380677bfc646cf4f6abe92d2b8cce787f
[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 "ev-ps-exporter.h"
23 #include "gpdf-g-switch.h"
24
25 #include "GlobalParams.h"
26 #include "GDKSplashOutputDev.h"
27 #include "PDFDoc.h"
28 #include "PSOutputDev.h"
29
30 typedef struct _PdfDocumentClass PdfDocumentClass;
31
32 #define PDF_DOCUMENT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), PDF_TYPE_DOCUMENT, PdfDocumentClass))
33 #define PDF_IS_DOCUMENT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), PDF_TYPE_DOCUMENT))
34 #define PDF_DOCUMENT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), PDF_TYPE_DOCUMENT, PdfDocumentClass))
35
36 struct _PdfDocumentClass
37 {
38         GObjectClass parent_class;
39 };
40
41 struct _PdfDocument
42 {
43         GObject parent_instance;
44
45         int page;
46         int page_x_offset;
47         int page_y_offset;
48         double scale;
49         GdkDrawable *target;
50
51         GDKSplashOutputDev *out;
52         PSOutputDev *ps_out;
53         PDFDoc *doc;
54
55         gboolean page_valid;
56 };
57
58 static void pdf_document_document_iface_init (EvDocumentIface *iface);
59 static void pdf_document_ps_exporter_iface_init (EvPSExporterIface *iface);
60
61 G_DEFINE_TYPE_WITH_CODE (PdfDocument, pdf_document, G_TYPE_OBJECT,
62                          {
63                                  G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT,
64                                                         pdf_document_document_iface_init);
65                                  G_IMPLEMENT_INTERFACE (EV_TYPE_PS_EXPORTER,
66                                                         pdf_document_ps_exporter_iface_init);
67                          });
68
69 static gboolean
70 document_validate_page (PdfDocument *pdf_document)
71 {
72         if (!pdf_document->page_valid) {
73                 pdf_document->doc->displayPage (pdf_document->out, pdf_document->page,
74                                                 72 * pdf_document->scale,
75                                                 72 * pdf_document->scale,
76                                                 0, gTrue, gTrue);
77                 
78                 pdf_document->page_valid = TRUE;
79         }
80 }
81
82 static gboolean
83 pdf_document_load (EvDocument  *document,
84                    const char  *uri,
85                    GError     **error)
86 {
87         PdfDocument *pdf_document = PDF_DOCUMENT (document);
88         PDFDoc *newDoc;
89         int err;
90         char *filename;
91         GString *filename_g;
92         
93         if (!globalParams) {
94                 globalParams = new GlobalParams("/etc/xpdfrc");
95                 globalParams->setupBaseFontsFc(NULL);
96         }
97
98         filename = g_filename_from_uri (uri, NULL, error);
99         if (!filename)
100                 return FALSE;
101
102         filename_g = new GString (filename);
103         g_free (filename);
104
105         // open the PDF file, assumes ownership of filename_g
106         newDoc = new PDFDoc(filename_g, 0, 0);
107
108         if (!newDoc->isOk()) {
109                 err = newDoc->getErrorCode();
110                 delete newDoc;
111
112                 /* FIXME: Add a real error enum to EvDocument */
113                 g_set_error (error, G_FILE_ERROR,
114                              G_FILE_ERROR_FAILED,
115                              "Failed to load document (error %d) '%s'\n",
116                              err,
117                              uri);
118                 
119                 return FALSE;
120         }
121
122         if (pdf_document->doc)
123                 delete pdf_document->doc;
124         pdf_document->doc = newDoc;
125
126         pdf_document->page = 1;
127
128         if (pdf_document->out)
129                 pdf_document->out->startDoc(pdf_document->doc->getXRef());
130
131         pdf_document->page_valid = FALSE;
132         
133         return TRUE;
134 }
135
136 static int
137 pdf_document_get_n_pages (EvDocument  *document)
138 {
139         PdfDocument *pdf_document = PDF_DOCUMENT (document);
140
141         if (pdf_document->doc)
142                 return pdf_document->doc->getNumPages();
143         else
144                 return 1;
145 }
146
147 static void
148 pdf_document_set_page (EvDocument  *document,
149                        int          page)
150 {
151         PdfDocument *pdf_document = PDF_DOCUMENT (document);
152
153         page = CLAMP (page, 1, ev_document_get_n_pages (document));
154
155         if (page != pdf_document->page) {
156                 pdf_document->page = page;
157                 pdf_document->page_valid = FALSE;
158         }
159
160 }
161
162 static int
163 pdf_document_get_page (EvDocument  *document)
164 {
165         PdfDocument *pdf_document = PDF_DOCUMENT (document);
166
167         return pdf_document->page;
168 }
169
170 static void
171 redraw_callback (void *data)
172 {
173         /* Need to hook up through a EvDocument callback? */
174 }
175
176 static void
177 pdf_document_set_target (EvDocument  *document,
178                          GdkDrawable *target)
179 {
180         PdfDocument *pdf_document = PDF_DOCUMENT (document);
181         
182         if (pdf_document->target != target) {
183                 if (pdf_document->target)
184                         g_object_unref (pdf_document->target);
185                 
186                 pdf_document->target = target;
187
188                 if (pdf_document->target)
189                         g_object_ref (pdf_document->target);
190
191                 if (pdf_document->out) {
192                         delete pdf_document->out;
193                         pdf_document->out = NULL;
194                 }
195
196                 if (pdf_document->target) {
197                         pdf_document->out = new GDKSplashOutputDev (gdk_drawable_get_screen (pdf_document->target),
198                                                          redraw_callback, (void*) document);
199
200                         if (pdf_document->doc)
201                                 pdf_document->out->startDoc(pdf_document->doc->getXRef());
202
203                 }
204
205                 pdf_document->page_valid = FALSE;
206         }
207 }
208
209 static void
210 pdf_document_set_scale (EvDocument  *document,
211                         double       scale)
212 {
213         PdfDocument *pdf_document = PDF_DOCUMENT (document);
214         
215         if (pdf_document->scale != scale) {
216                 pdf_document->scale = scale;
217                 pdf_document->page_valid = FALSE;
218         }
219 }
220
221 static void
222 pdf_document_set_page_offset (EvDocument  *document,
223                               int          x,
224                               int          y)
225 {
226         PdfDocument *pdf_document = PDF_DOCUMENT (document);
227         
228         pdf_document->page_x_offset = x;
229         pdf_document->page_y_offset = y;
230 }
231
232 static void
233 pdf_document_get_page_size (EvDocument   *document,
234                             int          *width,
235                             int          *height)
236 {
237         PdfDocument *pdf_document = PDF_DOCUMENT (document);
238
239         if (document_validate_page (pdf_document)) {
240                 if (width)
241                         *width = pdf_document->out->getBitmapWidth();
242                 if (height)
243                         *height = pdf_document->out->getBitmapHeight();
244         } else {
245                 if (width)
246                         *width = 1;
247                 if (height)
248                         *height = 1;
249         }
250 }
251
252 static void
253 pdf_document_render (EvDocument  *document,
254                      int          clip_x,
255                      int          clip_y,
256                      int          clip_width,
257                      int          clip_height)
258 {
259         PdfDocument *pdf_document = PDF_DOCUMENT (document);
260         GdkRectangle page;
261         GdkRectangle draw;
262
263         if (!document_validate_page (pdf_document) || !pdf_document->target)
264                 return;
265         
266         page.x = pdf_document->page_x_offset;
267         page.y = pdf_document->page_y_offset;
268         page.width = pdf_document->out->getBitmapWidth();
269         page.height = pdf_document->out->getBitmapHeight();
270
271         draw.x = clip_x;
272         draw.y = clip_y;
273         draw.width = clip_width;
274         draw.height = clip_height;
275         
276         if (gdk_rectangle_intersect (&page, &draw, &draw))
277                 pdf_document->out->redraw (draw.x - page.x, draw.y - page.y,
278                                            pdf_document->target,
279                                            draw.x, draw.y,
280                                            draw.width, draw.height);
281 }
282
283 static void
284 pdf_document_begin_find (EvDocument   *document,
285                          const char   *search_string,
286                          gboolean      case_sensitive)
287 {
288         /* FIXME make this incremental (idle handler) and multi-page */
289         /* Right now it's fully synchronous plus only does the current page */
290         
291         PdfDocument *pdf_document = PDF_DOCUMENT (document);
292         gunichar *ucs4;
293         glong ucs4_len;
294         int xMin, yMin, xMax, yMax;
295         GArray *results;
296         EvFindResult result;
297
298         /* FIXME case_sensitive (right now XPDF
299          * code is always case insensitive for ASCII
300          * and case sensitive for all other languaages)
301          */
302         
303         g_assert (sizeof (gunichar) == sizeof (Unicode));
304         ucs4 = g_utf8_to_ucs4_fast (search_string, -1,
305                                     &ucs4_len);
306
307         results = g_array_new (FALSE,
308                                FALSE,
309                                sizeof (EvFindResult));
310
311         if (pdf_document->out->findText (ucs4, ucs4_len,
312                                          gTrue, gTrue, // startAtTop, stopAtBottom
313                                          gFalse, gFalse, // startAtLast, stopAtLast
314                                          &xMin, &yMin, &xMax, &yMax)) {
315
316                 result.page_num = pdf_document->page;
317
318                 result.highlight_area.x = xMin;
319                 result.highlight_area.y = yMin;
320                 result.highlight_area.width = xMax - xMin;
321                 result.highlight_area.height = yMax - yMin;
322
323                 g_array_append_val (results, result);
324         
325                 /* Now find further results */
326
327                 while (pdf_document->out->findText (ucs4, ucs4_len,
328                                                     gFalse, gTrue,
329                                                     gTrue, gFalse,
330                                                     &xMin, &yMin, &xMax, &yMax)) {
331                         
332                         result.page_num = pdf_document->page;
333                         
334                         result.highlight_area.x = xMin;
335                         result.highlight_area.y = yMin;
336                         result.highlight_area.width = xMax - xMin;
337                         result.highlight_area.height = yMax - yMin;
338                         
339                         g_array_append_val (results, result);
340                 }
341         }
342
343         ev_document_found (document,
344                            (EvFindResult*) results->data,
345                            results->len,
346                            1.0);
347
348         g_array_free (results, TRUE);
349 }
350
351 static void
352 pdf_document_end_find (EvDocument   *document)
353 {
354         PdfDocument *pdf_document = PDF_DOCUMENT (document);
355
356         /* FIXME this will do something once begin_find queues
357          * an incremental find
358          */
359
360         // this should probably be shared among EvDocument
361         // implementations somehow?
362         ev_document_found (document, NULL, 0, 1.0);
363 }
364
365 static void
366 pdf_document_ps_export_begin (EvPSExporter *exporter, const char *filename)
367 {
368         PdfDocument *document = PDF_DOCUMENT (exporter);
369
370         if (document->ps_out)
371                 delete document->ps_out;
372
373         document->ps_out = new PSOutputDev ((char *)filename, document->doc->getXRef(),
374                                             document->doc->getCatalog(), 1,
375                                             ev_document_get_n_pages (EV_DOCUMENT (document)),
376                                             psModePS);  
377 }
378
379 static void
380 pdf_document_ps_export_do_page (EvPSExporter *exporter, int page)
381 {
382         PdfDocument *document = PDF_DOCUMENT (exporter);
383
384         document->doc->displayPage (document->ps_out, page,
385                                     72.0, 72.0, 0, gTrue, gFalse);
386 }
387
388 static void
389 pdf_document_ps_export_end (EvPSExporter *exporter)
390 {
391         PdfDocument *document = PDF_DOCUMENT (exporter);
392
393         delete document->ps_out;
394         document->ps_out = NULL;
395 }
396
397 static void
398 pdf_document_finalize (GObject *object)
399 {
400         PdfDocument *pdf_document = PDF_DOCUMENT (object);
401
402         if (pdf_document->target)
403                 g_object_unref (pdf_document->target);
404
405         if (pdf_document->out)
406                 delete pdf_document->out;
407         if (pdf_document->ps_out)
408                 delete pdf_document->ps_out;
409         if (pdf_document->doc)
410                 delete pdf_document->doc;
411
412 }
413
414 static void
415 pdf_document_class_init (PdfDocumentClass *klass)
416 {
417         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
418   
419         gobject_class->finalize = pdf_document_finalize;
420 }
421
422 static void
423 pdf_document_document_iface_init (EvDocumentIface *iface)
424 {
425         iface->load = pdf_document_load;
426         iface->get_n_pages = pdf_document_get_n_pages;
427         iface->set_page = pdf_document_set_page;
428         iface->get_page = pdf_document_get_page;
429         iface->set_scale = pdf_document_set_scale;
430         iface->set_target = pdf_document_set_target;
431         iface->set_page_offset = pdf_document_set_page_offset;
432         iface->get_page_size = pdf_document_get_page_size;
433         iface->render = pdf_document_render;
434         iface->begin_find = pdf_document_begin_find;
435         iface->end_find = pdf_document_end_find;
436 }
437
438 static void
439 pdf_document_ps_exporter_iface_init (EvPSExporterIface *iface)
440 {
441         iface->begin = pdf_document_ps_export_begin;
442         iface->do_page = pdf_document_ps_export_do_page;
443         iface->end = pdf_document_ps_export_end;
444 }
445
446 static void
447 pdf_document_init (PdfDocument *pdf_document)
448 {
449         pdf_document->page = 1;
450         pdf_document->page_x_offset = 0;
451         pdf_document->page_y_offset = 0;
452         pdf_document->scale = 1.;
453         
454         pdf_document->page_valid = FALSE;
455 }
456