1 /* SPDX-License-Identifier: Zlib */
2 
3 #include <stdlib.h>
4 #include <ctype.h>
5 #include <girara/datastructures.h>
6 #include <string.h>
7 #include <libdjvu/miniexp.h>
8 #include <glib.h>
9 
10 #include "djvu.h"
11 #include "page-text.h"
12 #include "internal.h"
13 
14 /* forward declarations */
15 static const char* get_extension(const char* path);
16 static void build_index(djvu_document_t *djvu_document, miniexp_t expression, girara_tree_node_t* root);
17 static bool exp_to_str(miniexp_t expression, const char** string);
18 static bool exp_to_int(miniexp_t expression, int* integer);
19 static bool exp_to_rect(miniexp_t expression, zathura_rectangle_t* rect);
20 
21 ZATHURA_PLUGIN_REGISTER_WITH_FUNCTIONS(
22   "djvu",
23   VERSION_MAJOR, VERSION_MINOR, VERSION_REV,
24   ZATHURA_PLUGIN_FUNCTIONS({
25     .document_open           = djvu_document_open,
26     .document_free           = djvu_document_free,
27     .document_index_generate = djvu_document_index_generate,
28     .document_save_as        = djvu_document_save_as,
29     .page_init               = djvu_page_init,
30     .page_clear              = djvu_page_clear,
31     .page_search_text        = djvu_page_search_text,
32     .page_get_text           = djvu_page_get_text,
33     .page_links_get          = djvu_page_links_get,
34     .page_render             = djvu_page_render,
35     .page_render_cairo       = djvu_page_render_cairo
36   }),
37   ZATHURA_PLUGIN_MIMETYPES({
38     "image/vnd.djvu",
39     "image/vnd.djvu+multipage"
40   })
41 )
42 
43 zathura_error_t
djvu_document_open(zathura_document_t * document)44 djvu_document_open(zathura_document_t* document)
45 {
46   zathura_error_t error = ZATHURA_ERROR_OK;
47 
48   if (document == NULL) {
49     error = ZATHURA_ERROR_INVALID_ARGUMENTS;
50     goto error_out;
51   }
52 
53   djvu_document_t* djvu_document = calloc(1, sizeof(djvu_document_t));
54   if (djvu_document == NULL) {
55     error = ZATHURA_ERROR_OUT_OF_MEMORY;
56     goto error_out;
57   }
58 
59   /* setup format */
60   static unsigned int masks[4] = {0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000};
61   djvu_document->format = ddjvu_format_create(DDJVU_FORMAT_RGBMASK32, 4, masks);
62 
63   if (djvu_document->format == NULL) {
64     error = ZATHURA_ERROR_UNKNOWN;
65     goto error_free;
66   }
67 
68   ddjvu_format_set_row_order(djvu_document->format, TRUE);
69 
70   /* setup context */
71   djvu_document->context = ddjvu_context_create("zathura");
72 
73   if (djvu_document->context == NULL) {
74     error = ZATHURA_ERROR_UNKNOWN;
75     goto error_free;
76   }
77 
78   /* setup document */
79   djvu_document->document =
80     ddjvu_document_create_by_filename(
81         djvu_document->context,
82         zathura_document_get_path(document),
83         FALSE
84     );
85 
86   if (djvu_document->document == NULL) {
87     error = ZATHURA_ERROR_UNKNOWN;
88     goto error_free;
89   }
90 
91   /* load document info */
92   ddjvu_message_t* msg;
93   ddjvu_message_wait(djvu_document->context);
94 
95   while ((msg = ddjvu_message_peek(djvu_document->context)) &&
96          (msg->m_any.tag != DDJVU_DOCINFO)) {
97     if (msg->m_any.tag == DDJVU_ERROR) {
98       error = ZATHURA_ERROR_UNKNOWN;
99       goto error_free;
100     }
101 
102     ddjvu_message_pop(djvu_document->context);
103   }
104 
105   /* decoding error */
106   if (ddjvu_document_decoding_error(djvu_document->document)) {
107     handle_messages(djvu_document, true);
108     error = ZATHURA_ERROR_UNKNOWN;
109     goto error_free;
110   }
111 
112   zathura_document_set_data(document, djvu_document);
113   zathura_document_set_number_of_pages(document,
114       ddjvu_document_get_pagenum(djvu_document->document));
115 
116   return error;
117 
118 error_free:
119 
120   if (djvu_document->format != NULL) {
121     ddjvu_format_release(djvu_document->format);
122   }
123 
124   if (djvu_document->context != NULL) {
125     ddjvu_context_release(djvu_document->context);
126   }
127 
128   free(djvu_document);
129 
130 error_out:
131 
132   return error;
133 }
134 
135 zathura_error_t
djvu_document_free(zathura_document_t * document,void * data)136 djvu_document_free(zathura_document_t* document, void* data)
137 {
138   djvu_document_t* djvu_document = data;
139   if (document == NULL) {
140     return ZATHURA_ERROR_INVALID_ARGUMENTS;
141   }
142 
143   if (djvu_document != NULL) {
144     ddjvu_context_release(djvu_document->context);
145     ddjvu_document_release(djvu_document->document);
146     ddjvu_format_release(djvu_document->format);
147     free(djvu_document);
148   }
149 
150   return ZATHURA_ERROR_OK;
151 }
152 
153 girara_tree_node_t*
djvu_document_index_generate(zathura_document_t * document,void * data,zathura_error_t * error)154 djvu_document_index_generate(zathura_document_t* document, void* data, zathura_error_t* error)
155 {
156   djvu_document_t* djvu_document = data;
157   if (document == NULL || djvu_document == NULL) {
158     if (error != NULL) {
159       *error = ZATHURA_ERROR_INVALID_ARGUMENTS;
160     }
161     return NULL;
162   }
163 
164   miniexp_t outline = miniexp_dummy;
165   while ((outline = ddjvu_document_get_outline(djvu_document->document)) ==
166       miniexp_dummy) {
167     handle_messages(djvu_document, true);
168   }
169 
170   if (outline == miniexp_dummy) {
171     return NULL;
172   }
173 
174   if (miniexp_consp(outline) == 0 || miniexp_car(outline) != miniexp_symbol("bookmarks")) {
175     ddjvu_miniexp_release(djvu_document->document, outline);
176     return NULL;
177   }
178 
179   girara_tree_node_t* root = girara_node_new(zathura_index_element_new("ROOT"));
180   build_index(djvu_document, miniexp_cdr(outline), root);
181 
182   ddjvu_miniexp_release(djvu_document->document, outline);
183 
184   return root;
185 }
186 
187 zathura_error_t
djvu_document_save_as(zathura_document_t * document,void * data,const char * path)188 djvu_document_save_as(zathura_document_t* document, void* data, const char* path)
189 {
190   djvu_document_t* djvu_document = data;
191   if (document == NULL || djvu_document == NULL || path == NULL) {
192     return ZATHURA_ERROR_INVALID_ARGUMENTS;
193   }
194 
195   FILE* fp = fopen(path, "w");
196   if (fp == NULL) {
197     return ZATHURA_ERROR_UNKNOWN;
198   }
199 
200   const char* extension = get_extension(path);
201 
202   ddjvu_job_t* job = NULL;
203   if (extension != NULL && g_strcmp0(extension, "ps") == 0) {
204     job = ddjvu_document_print(djvu_document->document, fp, 0, NULL);
205   } else {
206     job = ddjvu_document_save(djvu_document->document, fp, 0, NULL);
207   }
208   while (ddjvu_job_done(job) != true) {
209       handle_messages(djvu_document, true);
210   }
211 
212   fclose(fp);
213 
214   return ZATHURA_ERROR_OK;
215 }
216 
217 zathura_error_t
djvu_page_init(zathura_page_t * page)218 djvu_page_init(zathura_page_t* page)
219 {
220   if (page == NULL) {
221     return ZATHURA_ERROR_INVALID_ARGUMENTS;
222   }
223 
224   zathura_document_t* document   = zathura_page_get_document(page);
225   djvu_document_t* djvu_document = zathura_document_get_data(document);
226 
227   ddjvu_status_t status;
228   ddjvu_pageinfo_t page_info;
229 
230   unsigned int index = zathura_page_get_index(page);
231   while ((status = ddjvu_document_get_pageinfo(djvu_document->document, index,
232           &page_info)) < DDJVU_JOB_OK) {
233     handle_messages(djvu_document, true);
234   }
235 
236   if (status >= DDJVU_JOB_FAILED) {
237     handle_messages(djvu_document, true);
238     return ZATHURA_ERROR_UNKNOWN;
239   }
240 
241   zathura_page_set_width(page,  ZATHURA_DJVU_SCALE * page_info.width);
242   zathura_page_set_height(page, ZATHURA_DJVU_SCALE * page_info.height);
243 
244   return ZATHURA_ERROR_OK;
245 }
246 
247 zathura_error_t
djvu_page_clear(zathura_page_t * page,void * UNUSED (data))248 djvu_page_clear(zathura_page_t* page, void* UNUSED(data))
249 {
250   if (page == NULL) {
251     return ZATHURA_ERROR_INVALID_ARGUMENTS;
252   }
253 
254   return ZATHURA_ERROR_OK;
255 }
256 
257 girara_list_t*
djvu_page_search_text(zathura_page_t * page,void * UNUSED (data),const char * text,zathura_error_t * error)258 djvu_page_search_text(zathura_page_t* page, void* UNUSED(data), const char* text, zathura_error_t* error)
259 {
260   if (page == NULL || text == NULL || strlen(text) == 0) {
261     if (error != NULL) {
262       *error = ZATHURA_ERROR_INVALID_ARGUMENTS;
263     }
264     goto error_ret;
265   }
266 
267   zathura_document_t* document = zathura_page_get_document(page);
268   if (document == NULL) {
269     goto error_ret;
270   }
271 
272   djvu_document_t* djvu_document = zathura_document_get_data(document);
273 
274   djvu_page_text_t* page_text = djvu_page_text_new(djvu_document, page);
275   if (page_text == NULL) {
276     goto error_ret;
277   }
278 
279   girara_list_t* results = djvu_page_text_search(page_text, text);
280   if (results == NULL) {
281     goto error_free;
282   }
283 
284   djvu_page_text_free(page_text);
285 
286   return results;
287 
288 error_free:
289 
290   if (page_text != NULL) {
291     djvu_page_text_free(page_text);
292   }
293 
294 error_ret:
295 
296   if (error != NULL && *error == ZATHURA_ERROR_OK) {
297     *error = ZATHURA_ERROR_UNKNOWN;
298   }
299 
300   return NULL;
301 }
302 
303 char*
djvu_page_get_text(zathura_page_t * page,void * UNUSED (data),zathura_rectangle_t rectangle,zathura_error_t * error)304 djvu_page_get_text(zathura_page_t* page, void* UNUSED(data), zathura_rectangle_t
305     rectangle, zathura_error_t* error)
306 {
307   if (page == NULL) {
308     if (error != NULL) {
309       *error = ZATHURA_ERROR_INVALID_ARGUMENTS;
310     }
311     goto error_ret;
312   }
313 
314   zathura_document_t* document = zathura_page_get_document(page);
315   if (document == NULL) {
316     goto error_ret;
317   }
318 
319   djvu_document_t* djvu_document = zathura_document_get_data(document);
320 
321   djvu_page_text_t* page_text = djvu_page_text_new(djvu_document, page);
322   if (page_text == NULL) {
323     goto error_ret;
324   }
325 
326   double tmp = 0;
327   double page_height = zathura_page_get_height(page);
328   double page_width  = zathura_page_get_width(page);
329 
330   switch (zathura_document_get_rotation(document)) {
331     case 90:
332       tmp = rectangle.x1;
333       rectangle.x1 = rectangle.y1;
334       rectangle.y1 = tmp;
335       tmp = rectangle.x2;
336       rectangle.x2 = rectangle.y2;
337       rectangle.y2 = tmp;
338       break;
339     case 180:
340       tmp = rectangle.x1;
341       rectangle.x1 = (page_width  - rectangle.x2);
342       rectangle.x2 = (page_width  - tmp);
343       break;
344     case 270:
345       tmp = rectangle.y2;
346       rectangle.y2 = (page_height - rectangle.x1);
347       rectangle.x1 = (page_width  - tmp);
348       tmp = rectangle.y1;
349       rectangle.y1 = (page_height - rectangle.x2);
350       rectangle.x2 = (page_width  - tmp);
351       break;
352     default:
353       tmp = rectangle.y1;
354       rectangle.y1 = (page_height - rectangle.y2);
355       rectangle.y2 = (page_height - tmp);
356       break;
357   }
358 
359   /* adjust to scale */
360   rectangle.x1 /= ZATHURA_DJVU_SCALE;
361   rectangle.x2 /= ZATHURA_DJVU_SCALE;
362   rectangle.y1 /= ZATHURA_DJVU_SCALE;
363   rectangle.y2 /= ZATHURA_DJVU_SCALE;
364 
365   char* text = djvu_page_text_select(page_text, rectangle);
366 
367   djvu_page_text_free(page_text);
368 
369   return text;
370 
371 error_ret:
372 
373   if (error != NULL && *error == ZATHURA_ERROR_OK) {
374     *error = ZATHURA_ERROR_UNKNOWN;
375   }
376 
377   return NULL;
378 }
379 
380 girara_list_t*
djvu_page_links_get(zathura_page_t * page,void * UNUSED (data),zathura_error_t * error)381 djvu_page_links_get(zathura_page_t* page, void* UNUSED(data), zathura_error_t* error)
382 {
383   if (page == NULL) {
384     if (error != NULL) {
385       *error = ZATHURA_ERROR_INVALID_ARGUMENTS;
386     }
387     goto error_ret;
388   }
389 
390   zathura_document_t* document = zathura_page_get_document(page);
391   if (document == NULL) {
392     goto error_ret;
393   }
394 
395   girara_list_t* list = girara_list_new2((girara_free_function_t) zathura_link_free);
396   if (list == NULL) {
397     if (error != NULL) {
398       *error = ZATHURA_ERROR_OUT_OF_MEMORY;
399     }
400     goto error_ret;
401   }
402 
403   djvu_document_t* djvu_document = zathura_document_get_data(document);
404 
405   miniexp_t annotations = miniexp_nil;
406   while ((annotations = ddjvu_document_get_pageanno(djvu_document->document,
407           zathura_page_get_index(page))) == miniexp_dummy) {
408     handle_messages(djvu_document, true);
409   }
410 
411   if (annotations == miniexp_nil) {
412     goto error_free;
413   }
414 
415   miniexp_t* hyperlinks = ddjvu_anno_get_hyperlinks(annotations);
416   for (miniexp_t* iter = hyperlinks; *iter != NULL; iter++) {
417     if (miniexp_car(*iter) != miniexp_symbol("maparea")) {
418       continue;
419     }
420 
421     miniexp_t inner = miniexp_cdr(*iter);
422 
423     /* extract url information */
424     const char* target_string = NULL;
425 
426     if (miniexp_caar(inner) == miniexp_symbol("url")) {
427       if (exp_to_str(miniexp_caddr(miniexp_car(inner)), &target_string) == false) {
428         continue;
429       }
430     } else {
431       if (exp_to_str(miniexp_car(inner), &target_string) == false) {
432         continue;
433       }
434     }
435 
436     /* skip comment */
437     inner = miniexp_cdr(inner);
438 
439     /* extract link area */
440     inner = miniexp_cdr(inner);
441 
442     zathura_rectangle_t rect = { 0, 0, 0, 0 };
443     if (exp_to_rect(miniexp_car(inner), &rect) == false) {
444       continue;
445     }
446 
447     /* update rect */
448     unsigned int page_height = zathura_page_get_height(page) / ZATHURA_DJVU_SCALE;
449     rect.x1 = rect.x1 * ZATHURA_DJVU_SCALE;
450     rect.x2 = rect.x2 * ZATHURA_DJVU_SCALE;
451     double tmp = rect.y1;
452     rect.y1 = (page_height - rect.y2) * ZATHURA_DJVU_SCALE;
453     rect.y2 = (page_height - tmp)     * ZATHURA_DJVU_SCALE;
454 
455     /* create zathura link */
456     zathura_link_type_t type = ZATHURA_LINK_INVALID;
457     zathura_link_target_t target = { ZATHURA_LINK_DESTINATION_UNKNOWN, NULL, 0, -1, -1, -1, -1, 0 };;
458 
459     /* goto page */
460     if (target_string[0] == '#' && target_string[1] == 'p') {
461       type = ZATHURA_LINK_GOTO_DEST;
462       target.page_number = atoi(target_string + 2) - 1;
463     /* url or other? */
464     } else if (strstr(target_string, "//") != NULL) {
465       type = ZATHURA_LINK_URI;
466       target.value = (char*) target_string;
467     /* TODO: Parse all different links */
468     } else {
469       continue;
470     }
471 
472     zathura_link_t* link = zathura_link_new(type, rect, target);
473     if (link != NULL) {
474       girara_list_append(list, link);
475     }
476   }
477 
478   return list;
479 
480 error_free:
481 
482   if (list != NULL) {
483     girara_list_free(list);
484   }
485 
486 error_ret:
487 
488   return NULL;
489 }
490 
491 zathura_error_t
djvu_page_render_cairo(zathura_page_t * page,void * UNUSED (data),cairo_t * cairo,bool GIRARA_UNUSED (printing))492 djvu_page_render_cairo(zathura_page_t* page, void* UNUSED(data), cairo_t* cairo,
493     bool GIRARA_UNUSED(printing))
494 {
495   if (page == NULL || cairo == NULL) {
496     return ZATHURA_ERROR_INVALID_ARGUMENTS;
497   }
498 
499   zathura_document_t* document = zathura_page_get_document(page);
500   if (document == NULL) {
501     return ZATHURA_ERROR_UNKNOWN;
502   }
503 
504   /* init ddjvu render data */
505   djvu_document_t* djvu_document = zathura_document_get_data(document);
506   ddjvu_page_t* djvu_page        = ddjvu_page_create_by_pageno(djvu_document->document, zathura_page_get_index(page));
507 
508   if (djvu_page == NULL) {
509     return ZATHURA_ERROR_UNKNOWN;
510   }
511 
512   while (!ddjvu_page_decoding_done(djvu_page)) {
513     handle_messages(djvu_document, true);
514   }
515 
516   cairo_surface_t* surface = cairo_get_target(cairo);
517 
518   if (surface == NULL ||
519       cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ||
520       cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_IMAGE) {
521     ddjvu_page_release(djvu_page);
522     return ZATHURA_ERROR_UNKNOWN;
523   }
524 
525   unsigned int page_width  = cairo_image_surface_get_width(surface);
526   unsigned int page_height = cairo_image_surface_get_height(surface);;
527 
528   ddjvu_rect_t rrect = { 0, 0, page_width, page_height };
529   ddjvu_rect_t prect = { 0, 0, page_width, page_height };
530 
531   char* surface_data = (char*) cairo_image_surface_get_data(surface);
532 
533   if (surface_data == NULL) {
534     ddjvu_page_release(djvu_page);
535     return ZATHURA_ERROR_UNKNOWN;
536   }
537 
538   /* render page */
539   ddjvu_page_render(djvu_page, DDJVU_RENDER_COLOR, &prect, &rrect,
540       djvu_document->format, cairo_image_surface_get_stride(surface), surface_data);
541 
542   ddjvu_page_release(djvu_page);
543 
544   return ZATHURA_ERROR_OK;
545 }
546 
547 zathura_image_buffer_t*
djvu_page_render(zathura_page_t * page,void * UNUSED (data),zathura_error_t * error)548 djvu_page_render(zathura_page_t* page, void* UNUSED(data), zathura_error_t* error)
549 {
550   if (page == NULL) {
551     if (error != NULL) {
552       *error = ZATHURA_ERROR_INVALID_ARGUMENTS;
553     }
554     return NULL;
555   }
556 
557   zathura_document_t* document = zathura_page_get_document(page);
558   if (document == NULL) {
559     return NULL;
560   }
561 
562   /* calculate sizes */
563   unsigned int page_width  = zathura_document_get_scale(document) * zathura_page_get_width(page);
564   unsigned int page_height = zathura_document_get_scale(document) * zathura_page_get_height(page);
565 
566   if (page_width == 0 || page_height == 0) {
567     if (error != NULL) {
568       *error = ZATHURA_ERROR_UNKNOWN;
569     }
570     goto error_out;
571   }
572 
573   /* init ddjvu render data */
574   djvu_document_t* djvu_document = zathura_document_get_data(document);
575   ddjvu_page_t* djvu_page        = ddjvu_page_create_by_pageno(
576       djvu_document->document, zathura_page_get_index(page));
577 
578   if (djvu_page == NULL) {
579     if (error != NULL) {
580       *error = ZATHURA_ERROR_UNKNOWN;
581     }
582     goto error_out;
583   }
584 
585   while (!ddjvu_page_decoding_done(djvu_page)) {
586     handle_messages(djvu_document, true);
587   }
588 
589   ddjvu_rect_t rrect = { 0, 0, page_width, page_height };
590   ddjvu_rect_t prect = { 0, 0, page_width, page_height };
591 
592   zathura_image_buffer_t* image_buffer =
593     zathura_image_buffer_create(page_width, page_height);
594 
595   if (image_buffer == NULL) {
596     if (error != NULL) {
597       *error = ZATHURA_ERROR_OUT_OF_MEMORY;
598     }
599     goto error_free;
600   }
601 
602   /* set rotation */
603   ddjvu_page_set_rotation(djvu_page, DDJVU_ROTATE_0);
604 
605   /* render page */
606   ddjvu_page_render(djvu_page, DDJVU_RENDER_COLOR, &prect, &rrect,
607       djvu_document->format, 3 * page_width, (char*) image_buffer->data);
608 
609   return image_buffer;
610 
611 error_free:
612 
613     ddjvu_page_release(djvu_page);
614     zathura_image_buffer_free(image_buffer);
615 
616 error_out:
617 
618   return NULL;
619 }
620 
621 static const char*
get_extension(const char * path)622 get_extension(const char* path)
623 {
624   if (path == NULL) {
625     return NULL;
626   }
627 
628   unsigned int i = strlen(path);
629   for (; i > 0; i--) {
630     if (*(path + i) != '.') {
631       continue;
632     } else {
633       break;
634     }
635   }
636 
637   if (i == 0) {
638     return NULL;
639   }
640 
641   return path + i + 1;
642 }
643 
644 void
handle_messages(djvu_document_t * document,bool wait)645 handle_messages(djvu_document_t* document, bool wait)
646 {
647   if (document == NULL || document->context == NULL) {
648     return;
649   }
650 
651   ddjvu_context_t* context = document->context;
652   const ddjvu_message_t* message;
653 
654   if (wait == true) {
655     ddjvu_message_wait(context);
656   }
657 
658   while ((message = ddjvu_message_peek(context)) != NULL) {
659     ddjvu_message_pop(context);
660   }
661 }
662 
663 static void
build_index(djvu_document_t * djvu_document,miniexp_t expression,girara_tree_node_t * root)664 build_index(djvu_document_t *djvu_document, miniexp_t expression, girara_tree_node_t* root)
665 {
666   if (expression == miniexp_nil || root == NULL) {
667     return;
668   }
669 
670   int fileno = ddjvu_document_get_filenum(djvu_document->document);
671   int curfile = 0;
672 
673   while (miniexp_consp(expression) != 0) {
674     miniexp_t inner = miniexp_car(expression);
675 
676     if (miniexp_consp(inner)
677         && miniexp_consp(miniexp_cdr(inner))
678         && miniexp_stringp(miniexp_car(inner))
679         && miniexp_stringp(miniexp_car(inner))
680        ) {
681       const char* name = miniexp_to_str(miniexp_car(inner));
682       const char* link = miniexp_to_str(miniexp_cadr(inner));
683 
684       /* TODO: handle other links? */
685       if (link == NULL || link[0] != '#') {
686         expression = miniexp_cdr(expression);
687         continue;
688       }
689 
690       zathura_link_type_t type = ZATHURA_LINK_GOTO_DEST;
691       zathura_rectangle_t rect = { 0 };
692       zathura_link_target_t target = { 0 };
693       target.destination_type = ZATHURA_LINK_DESTINATION_XYZ;
694 
695       /* Check if link+1 contains a number */
696       bool number = true;
697       const size_t linklen = strlen(link);
698       for (unsigned int k = 1; k < linklen; k++) {
699         if (!isdigit(link[k])) {
700           number = false;
701           break;
702         }
703       }
704 
705       /* if link starts with a number assume it is a number */
706       if (number == true) {
707         target.page_number = atoi(link + 1) - 1;
708       } else {
709         /* otherwise assume it is an id for a page */
710         ddjvu_fileinfo_t info;
711         int f, i;
712         for (i=0; i < fileno; i++) {
713           f = (curfile + i) % fileno;
714           ddjvu_document_get_fileinfo(djvu_document->document, f, &info);
715           if (info.id != NULL && !strcmp(link+1, info.id)) {
716             break;
717           }
718         }
719 
720         /* got a page */
721         if (i < fileno && info.pageno >= 0) {
722           curfile = (f+1) % fileno;
723           target.page_number = info.pageno;
724         } else {
725           /* give up */
726           expression = miniexp_cdr(expression);
727           continue;
728         }
729       }
730 
731       zathura_index_element_t* index_element = zathura_index_element_new(name);
732       if (index_element == NULL) {
733         continue;
734       }
735 
736       index_element->link = zathura_link_new(type, rect, target);
737       if (index_element->link == NULL) {
738         zathura_index_element_free(index_element);
739         continue;
740       }
741 
742       girara_tree_node_t* node = girara_node_append_data(root, index_element);
743 
744       /* search recursive */
745       build_index(djvu_document, miniexp_cddr(inner), node);
746     }
747 
748     expression = miniexp_cdr(expression);
749   }
750 }
751 
752 static bool
exp_to_str(miniexp_t expression,const char ** string)753 exp_to_str(miniexp_t expression, const char** string)
754 {
755   if (string == NULL) {
756     return false;
757   }
758 
759   if (miniexp_stringp(expression)) {
760     *string = miniexp_to_str(expression);
761     return true;
762   }
763 
764   return false;
765 }
766 
767 static bool
exp_to_int(miniexp_t expression,int * integer)768 exp_to_int(miniexp_t expression, int* integer)
769 {
770   if (integer == NULL) {
771     return false;
772   }
773 
774   if (miniexp_numberp(expression)) {
775     *integer = miniexp_to_int(expression);
776     return true;
777   }
778 
779   return false;
780 }
781 
782 static bool
exp_to_rect(miniexp_t expression,zathura_rectangle_t * rect)783 exp_to_rect(miniexp_t expression, zathura_rectangle_t* rect)
784 {
785   if ((miniexp_car(expression) == miniexp_symbol("rect")
786         || miniexp_car(expression) == miniexp_symbol("oval"))
787       && miniexp_length(expression) == 5) {
788     int min_x  = 0;
789     int min_y  = 0;
790     int width  = 0;
791     int height = 0;
792 
793     miniexp_t iter = miniexp_cdr(expression);
794     if (exp_to_int(miniexp_car(iter), &min_x) == false) {
795       return false;
796     }
797     iter = miniexp_cdr(iter);
798     if (exp_to_int(miniexp_car(iter), &min_y) == false) {
799       return false;
800     }
801     iter = miniexp_cdr(iter);
802     if (exp_to_int(miniexp_car(iter), &width) == false) {
803       return false;
804     }
805     iter = miniexp_cdr(iter);
806     if (exp_to_int(miniexp_car(iter), &height) == false) {
807       return false;
808     }
809 
810     rect->x1 = min_x;
811     rect->x2 = min_x + width;
812     rect->y1 = min_y;
813     rect->y2 = min_y + height;
814   } else if (miniexp_car(expression) == miniexp_symbol("poly")
815       && miniexp_length(expression) >= 5) {
816     int min_x = 0;
817     int min_y = 0;
818     int max_x = 0;
819     int max_y = 0;
820 
821     miniexp_t iter = miniexp_cdr(expression);
822     while (iter != miniexp_nil) {
823       int x = 0;
824       int y = 0;
825 
826       if (exp_to_int(miniexp_car(iter), &x) == false) {
827         return false;
828       }
829       iter = miniexp_cdr(iter);
830       if (exp_to_int(miniexp_car(iter), &y) == false) {
831         return false;
832       }
833       iter = miniexp_cdr(iter);
834 
835       min_x = MIN(min_x, x);
836       min_y = MIN(min_y, y);
837       max_x = MAX(max_x, x);
838       max_y = MAX(max_y, y);
839     }
840 
841     rect->x1 = min_x;
842     rect->x2 = max_x;
843     rect->y1 = min_y;
844     rect->y2 = max_y;
845   }
846 
847   return true;
848 }
849