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