]> www.fi.muni.cz Git - evince.git/blob - backend/djvu/djvu-document.c
Check whether there are missing files in indirect multipage djvu
[evince.git] / backend / djvu / djvu-document.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /*
3  * Copyright (C) 2005, Nickolay V. Shmyrev <nshmyrev@yandex.ru>
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 "djvu-document.h"
21 #include "djvu-text.h"
22 #include "djvu-links.h"
23 #include "djvu-document-private.h"
24 #include "ev-document-thumbnails.h"
25 #include "ev-document-misc.h"
26 #include "ev-document-find.h"
27 #include "ev-document-links.h"
28
29 #include <libdjvu/ddjvuapi.h>
30 #include <libdjvu/miniexp.h>
31 #include <gdk-pixbuf/gdk-pixbuf-core.h>
32 #include <glib/gi18n.h>
33 #include <glib/gunicode.h>
34 #include <string.h>
35
36 #define SCALE_FACTOR 0.2
37
38 enum {
39         PROP_0,
40         PROP_TITLE
41 };
42
43 struct _DjvuDocumentClass
44 {
45         GObjectClass parent_class;
46 };
47
48 typedef struct _DjvuDocumentClass DjvuDocumentClass;
49
50 static void djvu_document_document_iface_init (EvDocumentIface *iface);
51 static void djvu_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface);
52 static void djvu_document_find_iface_init (EvDocumentFindIface *iface);
53 static void djvu_document_document_links_iface_init  (EvDocumentLinksIface *iface);
54
55 G_DEFINE_TYPE_WITH_CODE 
56     (DjvuDocument, djvu_document, G_TYPE_OBJECT, 
57     {
58       G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT, djvu_document_document_iface_init);    
59       G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS, djvu_document_document_thumbnails_iface_init)
60       G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND, djvu_document_find_iface_init);
61       G_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS, djvu_document_document_links_iface_init);
62      });
63
64
65 void 
66 djvu_handle_events (DjvuDocument *djvu_document, int wait)
67 {
68         ddjvu_context_t *ctx = djvu_document->d_context;
69         const ddjvu_message_t *msg;
70         
71         if (!ctx)
72                 return;
73
74         if (wait)
75                 msg = ddjvu_message_wait (ctx);
76
77         while ((msg = ddjvu_message_peek (ctx))) {
78                 switch (msg->m_any.tag) {
79                         case DDJVU_ERROR:
80                                 g_warning ("DjvuLibre error: %s", 
81                                            msg->m_error.message);
82                                 if (msg->m_error.filename)
83                                         g_warning ("DjvuLibre error: %s:%d", 
84                                                    msg->m_error.filename,
85                                                    msg->m_error.lineno);
86                                 break;
87                         default:
88                                 break;
89                 }
90                 ddjvu_message_pop (ctx);
91         }
92 }
93
94 static gboolean
95 djvu_document_load (EvDocument  *document,
96                     const char  *uri,
97                     GError     **error)
98 {
99         DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
100         ddjvu_document_t *doc;
101         gchar *filename;
102         gboolean missing_files = FALSE;
103
104         /* FIXME: We could actually load uris  */
105         filename = g_filename_from_uri (uri, NULL, error);
106         if (!filename)
107                 return FALSE;
108         
109         doc = ddjvu_document_create_by_filename (djvu_document->d_context, filename, TRUE);
110
111         if (!doc) {
112                 g_free (filename);
113                 return FALSE;
114         }
115
116         if (djvu_document->d_document)
117             ddjvu_document_release (djvu_document->d_document);
118
119         djvu_document->d_document = doc;
120
121         while (!ddjvu_document_decoding_done (djvu_document->d_document)) 
122                 djvu_handle_events (djvu_document, TRUE);
123         g_free (djvu_document->uri);
124         djvu_document->uri = g_strdup (uri);
125
126         if (ddjvu_document_get_type (djvu_document->d_document) == DDJVU_DOCTYPE_INDIRECT) {
127                 gint n_files;
128                 gint i;
129                 gchar *base;
130
131                 base = g_path_get_dirname (filename);
132
133                 n_files = ddjvu_document_get_filenum (djvu_document->d_document);
134                 for (i = 0; i < n_files; i++) {
135                         struct ddjvu_fileinfo_s fileinfo;
136                         gchar *file;
137                         
138                         ddjvu_document_get_fileinfo (djvu_document->d_document,
139                                                      i, &fileinfo);
140
141                         if (fileinfo.type != 'P')
142                                 continue;
143
144                         file = g_build_filename (base, fileinfo.id, NULL);
145                         if (!g_file_test (file, G_FILE_TEST_EXISTS)) {
146                                 missing_files = TRUE;
147                                 g_free (file);
148                                 
149                                 break;
150                         }
151                         g_free (file);
152                 }
153                 g_free (base);
154         }
155         g_free (filename);
156
157         if (missing_files) {
158                 g_set_error (error,
159                              G_FILE_ERROR,
160                              G_FILE_ERROR_EXIST,
161                              _("The document is composed by several files. "
162                                "One or more of such files cannot be accessed."));
163
164                 return FALSE;
165         }
166
167         return TRUE;
168 }
169
170
171 static gboolean
172 djvu_document_save (EvDocument  *document,
173                     const char  *uri,
174                     GError     **error)
175 {
176         DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
177
178         return ev_xfer_uri_simple (djvu_document->uri, uri, error);
179 }
180
181 int
182 djvu_document_get_n_pages (EvDocument  *document)
183 {
184         DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
185         
186         g_return_val_if_fail (djvu_document->d_document, 0);
187         
188         return ddjvu_document_get_pagenum (djvu_document->d_document);
189 }
190
191 static void
192 djvu_document_get_page_size (EvDocument   *document,
193                              int           page,
194                              double       *width,
195                              double       *height)
196 {
197         DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
198         ddjvu_pageinfo_t info;
199         ddjvu_status_t r;
200         
201         g_return_if_fail (djvu_document->d_document);
202
203         while ((r = ddjvu_document_get_pageinfo(djvu_document->d_document, page, &info)) < DDJVU_JOB_OK)
204                 djvu_handle_events(djvu_document, TRUE);
205
206         if (r >= DDJVU_JOB_FAILED)
207                 djvu_handle_events(djvu_document, TRUE);
208
209         *width = info.width * SCALE_FACTOR; 
210         *height = info.height * SCALE_FACTOR;
211 }
212
213 static GdkPixbuf *
214 djvu_document_render_pixbuf (EvDocument  *document, 
215                              EvRenderContext *rc)
216 {
217         DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
218         GdkPixbuf *pixbuf;
219         GdkPixbuf *rotated_pixbuf;
220         
221         ddjvu_rect_t rrect;
222         ddjvu_rect_t prect;
223         ddjvu_page_t *d_page;
224         
225         double page_width, page_height;
226
227         d_page = ddjvu_page_create_by_pageno (djvu_document->d_document, rc->page);
228         
229         while (!ddjvu_page_decoding_done (d_page))
230                 djvu_handle_events(djvu_document, TRUE);
231         
232         page_width = ddjvu_page_get_width (d_page) * rc->scale * SCALE_FACTOR;
233         page_height = ddjvu_page_get_height (d_page) * rc->scale * SCALE_FACTOR;
234
235         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, page_width, page_height);
236
237         prect.x = 0; prect.y = 0;
238         prect.w = page_width; prect.h = page_height;
239         rrect = prect;
240
241         ddjvu_page_render(d_page, DDJVU_RENDER_COLOR,
242                           &prect,
243                           &rrect,
244                           djvu_document->d_format,
245                           gdk_pixbuf_get_rowstride (pixbuf),
246                           (gchar *)gdk_pixbuf_get_pixels (pixbuf));
247         
248         rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rc->rotation);
249         g_object_unref (pixbuf);
250         
251         return rotated_pixbuf;
252 }
253
254 static void
255 djvu_document_finalize (GObject *object)
256 {
257         DjvuDocument *djvu_document = DJVU_DOCUMENT (object);
258
259         if (djvu_document->d_document)
260             ddjvu_document_release (djvu_document->d_document);
261
262         ddjvu_context_release (djvu_document->d_context);
263         ddjvu_format_release (djvu_document->d_format);
264         g_free (djvu_document->uri);
265         
266         G_OBJECT_CLASS (djvu_document_parent_class)->finalize (object);
267 }
268
269 static void
270 djvu_document_class_init (DjvuDocumentClass *klass)
271 {
272         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
273
274         gobject_class->finalize = djvu_document_finalize;
275 }
276
277 static gboolean
278 djvu_document_can_get_text (EvDocument *document)
279 {
280         return TRUE;
281 }
282
283
284 static char *
285 djvu_document_get_text (EvDocument *document, int page, EvRectangle *rect)
286 {
287         DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
288         double width, height;
289         EvRectangle rectangle;
290         char* text;
291              
292         djvu_document_get_page_size (document, page, &width, &height);          
293         rectangle.x1 = rect->x1 / SCALE_FACTOR;
294         rectangle.y1 = (height - rect->y2) / SCALE_FACTOR;
295         rectangle.x2 = rect->x2 / SCALE_FACTOR;
296         rectangle.y2 = (height - rect->y1) / SCALE_FACTOR;
297                 
298         text = djvu_text_copy (djvu_document, page, &rectangle);
299       
300         if (text == NULL)
301                 text = g_strdup ("");
302                 
303         return text;
304 }
305
306 static EvDocumentInfo *
307 djvu_document_get_info (EvDocument *document)
308 {
309         EvDocumentInfo *info;
310
311         info = g_new0 (EvDocumentInfo, 1);
312
313         return info;
314 }
315
316 static void
317 djvu_document_document_iface_init (EvDocumentIface *iface)
318 {
319         iface->load = djvu_document_load;
320         iface->save = djvu_document_save;
321         iface->can_get_text = djvu_document_can_get_text;
322         iface->get_text = djvu_document_get_text;
323         iface->get_n_pages = djvu_document_get_n_pages;
324         iface->get_page_size = djvu_document_get_page_size;
325         iface->render_pixbuf = djvu_document_render_pixbuf;
326         iface->get_info = djvu_document_get_info;
327 }
328
329 static void
330 djvu_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
331                                            gint                  page,
332                                            gint                  suggested_width,
333                                            gint                  *width,
334                                            gint                  *height)
335 {
336         DjvuDocument *djvu_document = DJVU_DOCUMENT (document); 
337         gdouble p_width, p_height;
338         gdouble page_ratio;
339         
340         djvu_document_get_page_size (EV_DOCUMENT(djvu_document), page, &p_width, &p_height);
341
342         page_ratio = p_height / p_width;
343         *width = suggested_width;
344         *height = (gint) (suggested_width * page_ratio);
345         
346         return;
347 }
348
349 static GdkPixbuf *
350 djvu_document_thumbnails_get_thumbnail (EvDocumentThumbnails   *document,
351                                           gint                   page,
352                                           gint                   rotation,
353                                           gint                   width,
354                                           gboolean               border)
355 {
356         DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
357         GdkPixbuf *pixbuf, *rotated_pixbuf;
358         gint thumb_width, thumb_height;
359
360         guchar *pixels;
361         
362         g_return_val_if_fail (djvu_document->d_document, NULL);
363         
364         djvu_document_thumbnails_get_dimensions (document, page, width, &thumb_width, &thumb_height);
365         
366         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
367                                  thumb_width, thumb_height);
368         gdk_pixbuf_fill (pixbuf, 0xffffffff);
369         pixels = gdk_pixbuf_get_pixels (pixbuf);
370         
371         while (ddjvu_thumbnail_status (djvu_document->d_document, page, 1) < DDJVU_JOB_OK)
372                 djvu_handle_events(djvu_document, TRUE);
373                     
374         ddjvu_thumbnail_render (djvu_document->d_document, page, 
375                                 &thumb_width, &thumb_height,
376                                 djvu_document->d_format,
377                                 gdk_pixbuf_get_rowstride (pixbuf), 
378                                 (gchar *)pixels);
379
380         rotated_pixbuf = gdk_pixbuf_rotate_simple (pixbuf, 360 - rotation);
381         g_object_unref (pixbuf);
382
383         if (border) {
384               GdkPixbuf *tmp_pixbuf = rotated_pixbuf;
385               rotated_pixbuf = ev_document_misc_get_thumbnail_frame (-1, -1, 0, tmp_pixbuf);
386               g_object_unref (tmp_pixbuf);
387         }
388         
389         return rotated_pixbuf;
390 }
391
392 static void
393 djvu_document_document_thumbnails_iface_init (EvDocumentThumbnailsIface *iface)
394 {
395         iface->get_thumbnail = djvu_document_thumbnails_get_thumbnail;
396         iface->get_dimensions = djvu_document_thumbnails_get_dimensions;
397 }
398
399 static void
400 djvu_document_init (DjvuDocument *djvu_document)
401 {
402         djvu_document->d_context = ddjvu_context_create ("Evince");
403         djvu_document->d_format = ddjvu_format_create (DDJVU_FORMAT_RGB24, 0, 0);
404         ddjvu_format_set_row_order (djvu_document->d_format,1);
405         
406         djvu_document->d_document = NULL;
407 }
408
409 static void
410 djvu_document_find_begin (EvDocumentFind   *document,
411                           int               page,
412                           const char       *search_string,
413                           gboolean          case_sensitive)
414 {
415         DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
416
417         if (djvu_document->search && 
418             strcmp (search_string, djvu_text_get_text (djvu_document->search)) == 0)
419                 return;
420
421         if (djvu_document->search)
422                 djvu_text_free (djvu_document->search);
423
424         djvu_document->search = djvu_text_new (djvu_document,
425                                                           page,
426                                                           case_sensitive,
427                                                           search_string);
428 }
429
430 static int
431 djvu_document_find_get_n_results (EvDocumentFind *document_find, int page)
432 {
433         DjvuText *search = DJVU_DOCUMENT (document_find)->search;
434
435         if (search) {
436                 return djvu_text_n_results (search, page);
437         } else {
438                 return 0;
439         }
440 }
441
442 static gboolean
443 djvu_document_find_get_result (EvDocumentFind *document_find,
444                                int             page,
445                                int             n_result,
446                                EvRectangle    *rectangle)
447 {
448         DjvuDocument *djvu_document = DJVU_DOCUMENT (document_find);
449         DjvuText *search = djvu_document->search;
450         EvRectangle *r;
451         double width, height;
452
453         if (search == NULL)
454                 return FALSE;
455
456         r = djvu_text_get_result (search, page, n_result);
457         if (r == NULL)
458                 return FALSE;
459
460         djvu_document_get_page_size (EV_DOCUMENT (djvu_document), 
461                 page, &width, &height);
462         rectangle->x1 = r->x1 * SCALE_FACTOR;
463         rectangle->y1 = height - r->y2 * SCALE_FACTOR;
464         rectangle->x2 = r->x2 * SCALE_FACTOR;
465         rectangle->y2 = height - r->y1 * SCALE_FACTOR;
466                 
467         return TRUE;
468 }
469
470 static int
471 djvu_document_find_page_has_results (EvDocumentFind *document_find,
472                                     int             page)
473 {
474         DjvuText *search = DJVU_DOCUMENT (document_find)->search;
475
476         return search && djvu_text_has_results (search, page);
477 }
478
479 static double
480 djvu_document_find_get_progress (EvDocumentFind *document_find)
481 {
482         DjvuText *search = DJVU_DOCUMENT (document_find)->search;
483         
484         if (search == NULL) {
485                 return 0;
486         }
487
488         return djvu_text_get_progress (search);
489 }
490
491 static void
492 djvu_document_find_cancel (EvDocumentFind *document)
493 {
494         DjvuDocument *djvu_document = DJVU_DOCUMENT (document);
495
496         if (djvu_document->search) {
497                 djvu_text_free (djvu_document->search);
498                 djvu_document->search = NULL;
499         }
500 }
501
502 static void
503 djvu_document_find_iface_init (EvDocumentFindIface *iface)
504 {
505         iface->begin = djvu_document_find_begin;
506         iface->get_n_results = djvu_document_find_get_n_results;
507         iface->get_result = djvu_document_find_get_result;
508         iface->page_has_results = djvu_document_find_page_has_results;
509         iface->get_progress = djvu_document_find_get_progress;
510         iface->cancel = djvu_document_find_cancel;
511 }
512
513 static GList *
514 djvu_document_links_get_links (EvDocumentLinks *document_links,
515                                gint             page)
516 {
517         return djvu_links_get_links (document_links, page, SCALE_FACTOR);
518 }
519
520 static void
521 djvu_document_document_links_iface_init  (EvDocumentLinksIface *iface)
522 {
523         iface->has_document_links = djvu_links_has_document_links;
524         iface->get_links_model = djvu_links_get_links_model;
525         iface->get_links = djvu_document_links_get_links;
526         iface->find_link_dest = djvu_links_find_link_dest;
527 }