]> www.fi.muni.cz Git - evince.git/blob - backend/djvu/djvu-text.c
8b457d863e922023a32be384f74f2dbe89f7f109
[evince.git] / backend / djvu / djvu-text.c
1 /*
2  * Implements search and copy functionality for Djvu files.
3  * Copyright (C) 2006 Michael Hofmann <mh21@piware.de>
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 <string.h>
21 #include <glib.h>
22 #include <libdjvu/miniexp.h>
23
24 #include "djvu-document-private.h"
25 #include "djvu-document.h"
26 #include "djvu-text.h"
27 #include "djvu-text-page.h"
28 #include "ev-document-find.h"
29 #include "ev-document.h"
30
31
32
33 struct _DjvuText {
34         DjvuDocument *document;
35         gboolean case_sensitive;
36         char *text;
37         GList **pages;
38         guint idle;
39         int start_page;
40         int search_page;
41 };
42
43 /**
44  * djvu_text_idle_callback:
45  * @data: #DjvuText instance
46  * 
47  * Idle callback that processes one page at a time.
48  * 
49  * Returns: whether there are more pages to be processed
50  */
51 static gboolean 
52 djvu_text_idle_callback (void *data)
53 {
54         DjvuText *djvu_text = (DjvuText *) data;
55         DjvuDocument *djvu_document = djvu_text->document;
56         int n_pages;
57         miniexp_t page_text;
58
59         ev_document_doc_mutex_lock ();
60         while ((page_text =
61                 ddjvu_document_get_pagetext (djvu_document->d_document,
62                                              djvu_text->search_page,
63                                              "char")) == miniexp_dummy)
64                 djvu_handle_events (djvu_document, TRUE);
65
66         if (page_text != miniexp_nil) {
67                 DjvuTextPage *page = djvu_text_page_new (page_text);
68                 djvu_text_page_prepare_search (page, djvu_text->case_sensitive); 
69                 if (page->links->len > 0) {
70                         djvu_text_page_search (page, djvu_text->text);
71                         djvu_text->pages[djvu_text->search_page] = page->results;
72                         ev_document_find_changed (EV_DOCUMENT_FIND
73                                                   (djvu_document),
74                                                   djvu_text->search_page);
75                 }
76                 djvu_text_page_free (page);
77                 ddjvu_miniexp_release (djvu_document->d_document,
78                                        page_text);
79         }
80         ev_document_doc_mutex_unlock ();
81
82         n_pages =
83             djvu_document_get_n_pages (EV_DOCUMENT (djvu_text->document));
84         djvu_text->search_page += 1;
85         if (djvu_text->search_page == n_pages) {
86                 /* wrap around */
87                 djvu_text->search_page = 0;
88         }
89
90         if (djvu_text->search_page != djvu_text->start_page)
91                 return TRUE;
92
93         /* We're done. */
94         djvu_text->idle = 0;
95         /* will return FALSE to remove */
96         return FALSE;
97 }
98
99 /**
100  * djvu_text_new:
101  * @djvu_document: document to search
102  * @start_page: first page to search
103  * @case_sensitive: uses g_utf8_case_fold() to enable case-insensitive 
104  *      searching
105  * @text: text to search
106  * 
107  * Creates a new #DjvuText instance to enable searching. An idle call
108  * is used to process all pages starting from @start_page.
109  * 
110  * Returns: newly created instance
111  */
112 DjvuText *
113 djvu_text_new (DjvuDocument *djvu_document,
114                int           start_page,
115                gboolean      case_sensitive, 
116                const char   *text)
117 {
118         DjvuText *djvu_text;
119         int n_pages;
120         int i;
121
122         n_pages = djvu_document_get_n_pages (EV_DOCUMENT (djvu_document));
123
124         djvu_text = g_new0 (DjvuText, 1);
125
126         if (case_sensitive)
127                 djvu_text->text = g_strdup (text);
128         else
129                 djvu_text->text = g_utf8_casefold (text, -1);
130         djvu_text->pages = g_new0 (GList *, n_pages);
131         for (i = 0; i < n_pages; i++) {
132                 djvu_text->pages[i] = NULL;
133         }
134
135         djvu_text->document = djvu_document;
136
137         /* We add at low priority so the progress bar repaints */
138         djvu_text->idle = g_idle_add_full (G_PRIORITY_LOW,
139                                         djvu_text_idle_callback,
140                                         djvu_text, NULL);
141
142         djvu_text->case_sensitive = case_sensitive;
143         djvu_text->start_page = start_page;
144         djvu_text->search_page = start_page;
145
146         return djvu_text;
147 }
148
149 /**
150  * djvu_text_copy:
151  * @djvu_document: document to search
152  * @page: page to search
153  * @rectangle: rectangle to copy
154  * 
155  * Copies and returns the text in the given rectangle.
156  * 
157  * Returns: newly allocated text or NULL of none is available
158  */
159 char *
160 djvu_text_copy (DjvuDocument *djvu_document,
161                 int           page,
162                 EvRectangle  *rectangle)
163 {
164         miniexp_t page_text;
165         char* text = NULL;
166
167         while ((page_text =
168                 ddjvu_document_get_pagetext (djvu_document->d_document,
169                                              page, "char")) == miniexp_dummy)
170                 djvu_handle_events (djvu_document, TRUE);
171
172         if (page_text != miniexp_nil) {
173                 DjvuTextPage *page = djvu_text_page_new (page_text);
174                 text = djvu_text_page_copy (page, rectangle);
175                 djvu_text_page_free (page);
176                 ddjvu_miniexp_release (djvu_document->d_document, page_text);
177         }
178         
179         return text;
180 }
181
182 /**
183  * djvu_text_free:
184  * @djvu_text: instance to free
185  * 
186  * Frees the given #DjvuText instance.
187  */
188 void djvu_text_free (DjvuText * djvu_text)
189 {
190         DjvuDocument *djvu_document = djvu_text->document;
191         int n_pages;
192         int i;
193
194         if (djvu_text->idle != 0)
195                 g_source_remove (djvu_text->idle);
196
197         n_pages = djvu_document_get_n_pages (EV_DOCUMENT (djvu_document));
198         for (i = 0; i < n_pages; i++) {
199                 g_list_foreach (djvu_text->pages[i], (GFunc) g_free, NULL);
200                 g_list_free (djvu_text->pages[i]);
201         }
202
203         g_free (djvu_text->text);
204 }
205
206 /**
207  * djvu_text_get_text:
208  * @djvu_text: #DjvuText instance
209  * 
210  * Returns the search text. This is mainly to be able to avoid reinstantiation 
211  * for the same search text.
212  * 
213  * Returns: the text this instance of #DjvuText is looking for
214  */
215 const char *
216 djvu_text_get_text (DjvuText *djvu_text)
217 {
218         return djvu_text->text;
219 }
220
221 /**
222  * djvu_text_n_results:
223  * @djvu_text: #DjvuText instance
224  * @page: page number
225  * 
226  * Returns the number of search results available for the given page.
227  * 
228  * Returns: number of search results
229  */
230 int 
231 djvu_text_n_results (DjvuText *djvu_text, 
232                      int       page)
233 {
234         return g_list_length (djvu_text->pages[page]);
235 }
236
237 /**
238  * djvu_text_has_results:
239  * @djvu_text: #DjvuText instance
240  * @page: page number
241  * 
242  * Returns whether there are search results available for the given page.
243  * This method executes faster than djvu_text_n_results().
244  * 
245  * Returns: whether there are search results
246  */
247 int 
248 djvu_text_has_results (DjvuText *djvu_text, 
249                        int       page)
250 {
251         return djvu_text->pages[page] != NULL;
252 }
253
254 /**
255  * djvu_text_get_result:
256  * @djvu_text: #DjvuText instance
257  * @page: page number
258  * @n_result: result number
259  * 
260  * Returns the n-th search result of a given page. The coordinates are 
261  * Djvu-specific and need to be processed to be compatible with the Evince
262  * coordinate system. The result may span several lines!
263  * 
264  * Returns: the rectangle for the search result
265  */
266 EvRectangle *
267 djvu_text_get_result (DjvuText *djvu_text, 
268                       int       page,
269                       int       n_result)
270 {
271         return (EvRectangle *) g_list_nth_data (djvu_text->pages[page],
272                                                 n_result);
273 }
274
275 /**
276  * djvu_text_get_progress:
277  * @djvu_text: #DjvuText instance
278  * 
279  * Returns the percentage of pages done searching.
280  * 
281  * Returns: the progress as value between 0 and 1
282  */
283 double
284 djvu_text_get_progress (DjvuText *djvu_text)
285 {
286         int pages_done;
287         int n_pages;
288
289         n_pages =
290             djvu_document_get_n_pages (EV_DOCUMENT (djvu_text->document));
291         if (djvu_text->search_page > djvu_text->start_page) {
292                 pages_done = djvu_text->search_page - djvu_text->start_page + 1;
293         } else if (djvu_text->search_page == djvu_text->start_page) {
294                 pages_done = n_pages;
295         } else {
296                 pages_done =
297                     n_pages - djvu_text->start_page + djvu_text->search_page;
298         }
299         return pages_done / (double) n_pages;
300 }
301