document.c 11.8 KB
Newer Older
Moritz Lipp's avatar
Moritz Lipp committed
1
2
/* See LICENSE file for license and copyright information */

Moritz Lipp's avatar
Moritz Lipp committed
3
4
5
6
#define _BSD_SOURCE
#define _XOPEN_SOURCE 500
// TODO: Implement realpath

Moritz Lipp's avatar
Moritz Lipp committed
7
#include <stdlib.h>
Moritz Lipp's avatar
Moritz Lipp committed
8
#include <stdio.h>
Moritz Lipp's avatar
Moritz Lipp committed
9
#include <string.h>
Moritz Lipp's avatar
Moritz Lipp committed
10
#include <limits.h>
Moritz Lipp's avatar
Moritz Lipp committed
11
12
13
#include <sys/types.h>
#include <dirent.h>
#include <dlfcn.h>
Moritz Lipp's avatar
Moritz Lipp committed
14
15

#include "document.h"
16
17
#include "utils.h"
#include "zathura.h"
Moritz Lipp's avatar
Moritz Lipp committed
18
19
20

#define LENGTH(x) (sizeof(x)/sizeof((x)[0]))

21
22
zathura_document_plugin_t* zathura_document_plugins = NULL;

Moritz Lipp's avatar
Moritz Lipp committed
23
24
25
26
27
28
29
30
31
32
33
34
35
void
zathura_document_plugins_load(void)
{
  /* read all files in the plugin directory */
  DIR* dir = opendir(PLUGIN_DIR);
  if (dir == NULL) {
    fprintf(stderr, "error: could not open plugin directory: %s\n", PLUGIN_DIR);
    return;
  }

  struct dirent* entry;
  while ((entry = readdir(dir)) != NULL) {
    /* check if entry is a file */
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    if (entry->d_type != 0x8) {
      continue;
    }

    void* handle                      = NULL;
    zathura_document_plugin_t* plugin = NULL;
    char* path                        = NULL;

    /* get full path */
    path = string_concat(PLUGIN_DIR, "/", entry->d_name, NULL);

    if (path == NULL) {
      goto error_continue;
    }

    /* load plugin */
    handle = dlopen(path, RTLD_NOW);
Moritz Lipp's avatar
Moritz Lipp committed
53

54
55
56
57
58
59
60
61
    if (handle == NULL) {
      fprintf(stderr, "error: could not load plugin (%s)\n", dlerror());
      goto error_free;
    }

    /* resolve symbol */
    zathura_plugin_register_service_t register_plugin;
    *(void**)(&register_plugin) = dlsym(handle, PLUGIN_REGISTER_FUNCTION);
Moritz Lipp's avatar
Moritz Lipp committed
62

63
64
65
66
    if (register_plugin == NULL) {
      fprintf(stderr, "error: could not find '%s' function in the plugin\n", PLUGIN_REGISTER_FUNCTION);
      goto error_free;
    }
Moritz Lipp's avatar
Moritz Lipp committed
67

68
    plugin = malloc(sizeof(zathura_document_plugin_t));
Moritz Lipp's avatar
Moritz Lipp committed
69

70
71
72
    if (plugin == NULL) {
      goto error_free;
    }
Moritz Lipp's avatar
Moritz Lipp committed
73

74
75
    plugin->file_extension = NULL;
    plugin->open_function  = NULL;
Moritz Lipp's avatar
Moritz Lipp committed
76

77
    register_plugin(plugin);
Moritz Lipp's avatar
Moritz Lipp committed
78

79
    bool r = zathura_document_plugin_register(plugin, handle);
Moritz Lipp's avatar
Moritz Lipp committed
80

81
82
83
    if (r == false) {
      fprintf(stderr, "error: could not register plugin (%s)\n", path);
      goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
84
    }
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

    free(path);

    continue;

error_free:

    free(path);
    free(plugin);

    if (handle) {
      dlclose(handle);
    }

error_continue:

    continue;
Moritz Lipp's avatar
Moritz Lipp committed
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  }

  if (closedir(dir) == -1) {
    fprintf(stderr, "error: could not close plugin directory: %s\n", PLUGIN_DIR);
  }
}

void
zathura_document_plugins_free(void)
{
  /* free registered plugins */
  zathura_document_plugin_t* plugin = zathura_document_plugins;
  while (plugin) {
    zathura_document_plugin_t* tmp = plugin->next;
Moritz Lipp's avatar
Moritz Lipp committed
116
    free(plugin->file_extension);
Moritz Lipp's avatar
Moritz Lipp committed
117
118
119
120
121
122
123
    free(plugin);
    plugin = tmp;
  }

  zathura_document_plugins = NULL;
}

124
bool
125
zathura_document_plugin_register(zathura_document_plugin_t* new_plugin, void* handle)
126
{
127
  if( (new_plugin == NULL) || (new_plugin->file_extension == NULL) || (new_plugin->open_function == NULL)
128
      || (handle == NULL) ) {
129
130
131
132
133
134
135
    fprintf(stderr, "plugin: could not register\n");
    return false;
  }

  /* search existing plugins */
  zathura_document_plugin_t* plugin = zathura_document_plugins;
  while (plugin) {
136
137
    if (!strcmp(plugin->file_extension, new_plugin->file_extension)) {
      fprintf(stderr, "plugin: already registered for filetype %s\n", plugin->file_extension);
138
139
140
141
142
143
144
145
146
147
148
      return false;
    }

    if (plugin->next == NULL) {
      break;
    }

    plugin = plugin->next;
  }

  /* create new plugin */
149
150
  new_plugin->handle = handle;
  new_plugin->next   = NULL;
151
152
153
154
155
156
157
158
159
160
161

  /* append to list */
  if (plugin == NULL) {
    zathura_document_plugins = new_plugin;
  } else {
    plugin->next = new_plugin;
  }

  return true;
}

Moritz Lipp's avatar
Moritz Lipp committed
162
zathura_document_t*
Moritz Lipp's avatar
Moritz Lipp committed
163
zathura_document_open(const char* path, const char* password)
Moritz Lipp's avatar
Moritz Lipp committed
164
{
Moritz Lipp's avatar
Moritz Lipp committed
165
  if (!path) {
Moritz Lipp's avatar
Moritz Lipp committed
166
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
167
168
  }

Moritz Lipp's avatar
Moritz Lipp committed
169
  if (!file_exists(path)) {
Moritz Lipp's avatar
Moritz Lipp committed
170
    fprintf(stderr, "error: file does not exist\n");
Moritz Lipp's avatar
Moritz Lipp committed
171
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
172
173
174
  }

  const char* file_extension = file_get_extension(path);
Moritz Lipp's avatar
Moritz Lipp committed
175
  if (!file_extension) {
Moritz Lipp's avatar
Moritz Lipp committed
176
    fprintf(stderr, "error: could not determine file type\n");
Moritz Lipp's avatar
Moritz Lipp committed
177
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
178
179
  }

Moritz Lipp's avatar
Moritz Lipp committed
180
181
182
183
184
185
  /* determine real path */
  size_t path_max;
#ifdef PATH_MAX
  path_max = PATH_MAX;
#else
  path_max = pathconf(path,_PC_PATH_MAX);
Moritz Lipp's avatar
Moritz Lipp committed
186
  if (path_max <= 0)
Moritz Lipp's avatar
Moritz Lipp committed
187
188
189
    path_max = 4096;
#endif

Moritz Lipp's avatar
Moritz Lipp committed
190
191
192
193
  char* real_path              = NULL;
  zathura_document_t* document = NULL;

  real_path = malloc(sizeof(char) * path_max);
Moritz Lipp's avatar
Moritz Lipp committed
194
  if (!real_path) {
Moritz Lipp's avatar
Moritz Lipp committed
195
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
196
197
  }

Moritz Lipp's avatar
Moritz Lipp committed
198
  if (!realpath(path, real_path)) {
Moritz Lipp's avatar
Moritz Lipp committed
199
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
200
201
  }

Moritz Lipp's avatar
Moritz Lipp committed
202
  document = malloc(sizeof(zathura_document_t));
Moritz Lipp's avatar
Moritz Lipp committed
203
  if (!document) {
Moritz Lipp's avatar
Moritz Lipp committed
204
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
205
206
  }

Moritz Lipp's avatar
Moritz Lipp committed
207
  document->file_path           = real_path;
Moritz Lipp's avatar
Moritz Lipp committed
208
209
210
  document->password            = password;
  document->current_page_number = 0;
  document->number_of_pages     = 0;
Moritz Lipp's avatar
Moritz Lipp committed
211
  document->scale               = 1.0;
Moritz Lipp's avatar
Moritz Lipp committed
212
  document->rotate              = 0;
Moritz Lipp's avatar
Moritz Lipp committed
213
  document->data                = NULL;
Moritz Lipp's avatar
Moritz Lipp committed
214
  document->pages               = NULL;
Moritz Lipp's avatar
Moritz Lipp committed
215
216
217
218
219
220
221
222
223
224
225
226
227

  document->functions.document_free            = NULL;
  document->functions.document_index_generate  = NULL;
  document->functions.document_save_as         = NULL;
  document->functions.document_attachments_get = NULL;
  document->functions.page_get                 = NULL;
  document->functions.page_free                = NULL;
  document->functions.page_search_text         = NULL;
  document->functions.page_links_get           = NULL;
  document->functions.page_form_fields_get     = NULL;
  document->functions.page_render              = NULL;

  /* init plugin with associated file type */
228
229
230
231
232
  zathura_document_plugin_t* plugin = zathura_document_plugins;
  while (plugin) {
    if (!strcmp(file_extension, plugin->file_extension)) {
      if (plugin->open_function) {
        if (plugin->open_function(document)) {
Moritz Lipp's avatar
Moritz Lipp committed
233
          /* update statusbar */
Moritz Lipp's avatar
Moritz Lipp committed
234
          girara_statusbar_item_set_text(Zathura.UI.session, Zathura.UI.statusbar.file, real_path);
Moritz Lipp's avatar
Moritz Lipp committed
235
236
237

          /* read all pages */
          document->pages = calloc(document->number_of_pages, sizeof(zathura_page_t*));
Moritz Lipp's avatar
Moritz Lipp committed
238
          if (!document->pages) {
Moritz Lipp's avatar
Moritz Lipp committed
239
240
241
            goto error_free;
          }

242
          for (unsigned int page_id = 0; page_id < document->number_of_pages; page_id++) {
Moritz Lipp's avatar
Moritz Lipp committed
243
            zathura_page_t* page = zathura_page_get(document, page_id);
Moritz Lipp's avatar
Moritz Lipp committed
244
            if (!page) {
Moritz Lipp's avatar
Moritz Lipp committed
245
246
247
248
249
250
              goto error_free;
            }

            document->pages[page_id] = page;
          }

Moritz Lipp's avatar
Moritz Lipp committed
251
          return document;
Moritz Lipp's avatar
Moritz Lipp committed
252
253
        } else {
          fprintf(stderr, "error: could not open file\n");
Moritz Lipp's avatar
Moritz Lipp committed
254
          goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
255
256
257
        }
      }
    }
258
259

    plugin = plugin->next;
Moritz Lipp's avatar
Moritz Lipp committed
260
261
  }

Moritz Lipp's avatar
Moritz Lipp committed
262
263
  fprintf(stderr, "error: unknown file type\n");

Moritz Lipp's avatar
Moritz Lipp committed
264
265
error_free:

Moritz Lipp's avatar
Moritz Lipp committed
266
  free(real_path);
Moritz Lipp's avatar
Moritz Lipp committed
267

Moritz Lipp's avatar
Moritz Lipp committed
268
269
  if (document && document->pages) {
    for (unsigned int page_id = 0; page_id < document->number_of_pages; page_id++)
Moritz Lipp's avatar
Moritz Lipp committed
270
271
272
273
274
275
276
    {
      zathura_page_free(document->pages[page_id]);
    }

    free(document->pages);
  }

Moritz Lipp's avatar
Moritz Lipp committed
277
278
  free(document);

Moritz Lipp's avatar
Moritz Lipp committed
279
280
error_out:

Moritz Lipp's avatar
Moritz Lipp committed
281
282
283
  return NULL;
}

Moritz Lipp's avatar
Moritz Lipp committed
284
285
bool
zathura_document_free(zathura_document_t* document)
Moritz Lipp's avatar
Moritz Lipp committed
286
{
Moritz Lipp's avatar
Moritz Lipp committed
287
  if (!document) {
Moritz Lipp's avatar
Moritz Lipp committed
288
    return false;
Moritz Lipp's avatar
Moritz Lipp committed
289
290
  }

Moritz Lipp's avatar
Moritz Lipp committed
291
  /* free pages */
Moritz Lipp's avatar
Moritz Lipp committed
292
  for (unsigned int page_id = 0; page_id < document->number_of_pages; page_id++)
Moritz Lipp's avatar
Moritz Lipp committed
293
294
295
296
297
298
299
  {
    zathura_page_free(document->pages[page_id]);
  }

  free(document->pages);

  /* free document */
Moritz Lipp's avatar
Moritz Lipp committed
300
  if (!document->functions.document_free) {
Moritz Lipp's avatar
Moritz Lipp committed
301
    fprintf(stderr, "error: %s not implemented\n", __FUNCTION__);
Moritz Lipp's avatar
Moritz Lipp committed
302

Moritz Lipp's avatar
Moritz Lipp committed
303
    if (document->file_path) {
Moritz Lipp's avatar
Moritz Lipp committed
304
305
306
      free(document->file_path);
    }

Moritz Lipp's avatar
Moritz Lipp committed
307
308
    free(document);
    return true;
Moritz Lipp's avatar
Moritz Lipp committed
309
310
  }

Moritz Lipp's avatar
Moritz Lipp committed
311
312
  bool r = document->functions.document_free(document);

Moritz Lipp's avatar
Moritz Lipp committed
313
  if (document->file_path) {
Moritz Lipp's avatar
Moritz Lipp committed
314
315
316
    free(document->file_path);
  }

Moritz Lipp's avatar
Moritz Lipp committed
317
318
319
  free(document);

  return r;
Moritz Lipp's avatar
Moritz Lipp committed
320
321
322
323
324
}

bool
zathura_document_save_as(zathura_document_t* document, const char* path)
{
Moritz Lipp's avatar
Moritz Lipp committed
325
  if (!document || !path) {
Moritz Lipp's avatar
Moritz Lipp committed
326
327
328
    return false;
  }

Moritz Lipp's avatar
Moritz Lipp committed
329
  if (!document->functions.document_save_as) {
Moritz Lipp's avatar
Moritz Lipp committed
330
331
332
333
334
    fprintf(stderr, "error: %s not implemented\n", __FUNCTION__);
    return false;
  }

  return document->functions.document_save_as(document, path);
Moritz Lipp's avatar
Moritz Lipp committed
335
336
}

337
girara_tree_node_t*
Moritz Lipp's avatar
Moritz Lipp committed
338
zathura_document_index_generate(zathura_document_t* document)
Moritz Lipp's avatar
Moritz Lipp committed
339
{
Moritz Lipp's avatar
Moritz Lipp committed
340
  if (!document) {
Moritz Lipp's avatar
Moritz Lipp committed
341
342
343
    return NULL;
  }

Moritz Lipp's avatar
Moritz Lipp committed
344
  if (!document->functions.document_index_generate) {
Moritz Lipp's avatar
Moritz Lipp committed
345
346
347
348
349
    fprintf(stderr, "error: %s not implemented\n", __FUNCTION__);
    return NULL;
  }

  return document->functions.document_index_generate(document);
Moritz Lipp's avatar
Moritz Lipp committed
350
351
352
}

zathura_list_t*
Moritz Lipp's avatar
Moritz Lipp committed
353
zathura_document_attachments_get(zathura_document_t* document)
Moritz Lipp's avatar
Moritz Lipp committed
354
{
Moritz Lipp's avatar
Moritz Lipp committed
355
  if (!document) {
Moritz Lipp's avatar
Moritz Lipp committed
356
357
358
    return NULL;
  }

Moritz Lipp's avatar
Moritz Lipp committed
359
  if (!document->functions.document_attachments_get) {
Moritz Lipp's avatar
Moritz Lipp committed
360
361
362
363
364
    fprintf(stderr, "error: %s not implemented\n", __FUNCTION__);
    return NULL;
  }

  return document->functions.document_attachments_get(document);
Moritz Lipp's avatar
Moritz Lipp committed
365
366
367
}

bool
Moritz Lipp's avatar
Moritz Lipp committed
368
zathura_document_attachments_free(zathura_list_t* list)
Moritz Lipp's avatar
Moritz Lipp committed
369
370
371
372
{
  return false;
}

Moritz Lipp's avatar
Moritz Lipp committed
373
zathura_page_t*
374
zathura_page_get(zathura_document_t* document, unsigned int page_id)
Moritz Lipp's avatar
Moritz Lipp committed
375
{
Moritz Lipp's avatar
Moritz Lipp committed
376
  if (!document) {
Moritz Lipp's avatar
Moritz Lipp committed
377
378
379
    return NULL;
  }

Moritz Lipp's avatar
Moritz Lipp committed
380
  if (!document->functions.page_get) {
Moritz Lipp's avatar
Moritz Lipp committed
381
382
383
384
    fprintf(stderr, "error: %s not implemented\n", __FUNCTION__);
    return NULL;
  }

385
386
  zathura_page_t* page = document->functions.page_get(document, page_id);

Moritz Lipp's avatar
Moritz Lipp committed
387
  if (page) {
388
389
390
    page->number    = page_id;
    page->rendered  = false;
    page->event_box = gtk_event_box_new();
391
392
393
394
    g_static_mutex_init(&(page->lock));
  }

  return page;
Moritz Lipp's avatar
Moritz Lipp committed
395
396
}

Moritz Lipp's avatar
Moritz Lipp committed
397
398
399
bool
zathura_page_free(zathura_page_t* page)
{
Moritz Lipp's avatar
Moritz Lipp committed
400
  if (!page || !page->document) {
Moritz Lipp's avatar
Moritz Lipp committed
401
402
403
    return false;
  }

Moritz Lipp's avatar
Moritz Lipp committed
404
  if (!page->document->functions.page_free) {
Moritz Lipp's avatar
Moritz Lipp committed
405
406
407
408
409
410
411
    fprintf(stderr, "error: %s not implemented\n", __FUNCTION__);
    return false;
  }

  return page->document->functions.page_free(page);
}

Moritz Lipp's avatar
Moritz Lipp committed
412
zathura_list_t*
Moritz Lipp's avatar
Moritz Lipp committed
413
zathura_page_search_text(zathura_page_t* page, const char* text)
Moritz Lipp's avatar
Moritz Lipp committed
414
{
Moritz Lipp's avatar
Moritz Lipp committed
415
  if (!page || !page->document || !text) {
Moritz Lipp's avatar
Moritz Lipp committed
416
417
418
    return NULL;
  }

Moritz Lipp's avatar
Moritz Lipp committed
419
  if (!page->document->functions.page_search_text) {
Moritz Lipp's avatar
Moritz Lipp committed
420
421
422
423
424
    fprintf(stderr, "error: %s not implemented\n", __FUNCTION__);
    return NULL;
  }

  return page->document->functions.page_search_text(page, text);
Moritz Lipp's avatar
Moritz Lipp committed
425
426
}

Moritz Lipp's avatar
Moritz Lipp committed
427
428
zathura_list_t*
zathura_page_links_get(zathura_page_t* page)
Moritz Lipp's avatar
Moritz Lipp committed
429
{
Moritz Lipp's avatar
Moritz Lipp committed
430
  if (!page || !page->document) {
Moritz Lipp's avatar
Moritz Lipp committed
431
432
433
    return NULL;
  }

Moritz Lipp's avatar
Moritz Lipp committed
434
  if (!page->document->functions.page_links_get) {
Moritz Lipp's avatar
Moritz Lipp committed
435
436
437
438
439
    fprintf(stderr, "error: %s not implemented\n", __FUNCTION__);
    return NULL;
  }

  return page->document->functions.page_links_get(page);
Moritz Lipp's avatar
Moritz Lipp committed
440
441
442
}

bool
Moritz Lipp's avatar
Moritz Lipp committed
443
zathura_page_links_free(zathura_list_t* list)
Moritz Lipp's avatar
Moritz Lipp committed
444
445
446
447
448
{
  return false;
}

zathura_list_t*
Moritz Lipp's avatar
Moritz Lipp committed
449
zathura_page_form_fields_get(zathura_page_t* page)
Moritz Lipp's avatar
Moritz Lipp committed
450
{
Moritz Lipp's avatar
Moritz Lipp committed
451
  if (!page || !page->document) {
Moritz Lipp's avatar
Moritz Lipp committed
452
453
454
    return NULL;
  }

Moritz Lipp's avatar
Moritz Lipp committed
455
  if (!page->document->functions.page_form_fields_get) {
Moritz Lipp's avatar
Moritz Lipp committed
456
457
458
459
460
    fprintf(stderr, "error: %s not implemented\n", __FUNCTION__);
    return NULL;
  }

  return page->document->functions.page_form_fields_get(page);
Moritz Lipp's avatar
Moritz Lipp committed
461
462
463
}

bool
Moritz Lipp's avatar
Moritz Lipp committed
464
zathura_page_form_fields_free(zathura_list_t* list)
Moritz Lipp's avatar
Moritz Lipp committed
465
466
467
{
  return false;
}
Moritz Lipp's avatar
Moritz Lipp committed
468

469
zathura_image_buffer_t*
Moritz Lipp's avatar
Moritz Lipp committed
470
471
zathura_page_render(zathura_page_t* page)
{
Moritz Lipp's avatar
Moritz Lipp committed
472
  if (!page || !page->document) {
Moritz Lipp's avatar
Moritz Lipp committed
473
474
475
    return NULL;
  }

Moritz Lipp's avatar
Moritz Lipp committed
476
  if (!page->document->functions.page_render) {
Moritz Lipp's avatar
Moritz Lipp committed
477
478
479
480
    fprintf(stderr, "error: %s not implemented\n", __FUNCTION__);
    return NULL;
  }

481
  zathura_image_buffer_t* buffer = page->document->functions.page_render(page);
Moritz Lipp's avatar
Moritz Lipp committed
482

483
  if (buffer) {
Moritz Lipp's avatar
Moritz Lipp committed
484
485
486
    page->rendered = true;
  }

487
  return buffer;
Moritz Lipp's avatar
Moritz Lipp committed
488
}
489
490
491
492

zathura_index_element_t*
zathura_index_element_new(const char* title)
{
Moritz Lipp's avatar
Moritz Lipp committed
493
  if (!title) {
494
    return NULL;
Moritz Lipp's avatar
Moritz Lipp committed
495
  }
496
497

  zathura_index_element_t* res = g_malloc0(sizeof(zathura_index_element_t));
Moritz Lipp's avatar
Moritz Lipp committed
498

Moritz Lipp's avatar
Moritz Lipp committed
499
  if (!res) {
500
    return NULL;
Moritz Lipp's avatar
Moritz Lipp committed
501
502
  }

503
  res->title = g_strdup(title);
Moritz Lipp's avatar
Moritz Lipp committed
504

505
506
507
508
509
510
  return res;
}

void
zathura_index_element_free(zathura_index_element_t* index)
{
Moritz Lipp's avatar
Moritz Lipp committed
511
  if (!index) {
512
    return;
Moritz Lipp's avatar
Moritz Lipp committed
513
  }
514
515

  g_free(index->title);
Moritz Lipp's avatar
Moritz Lipp committed
516

Moritz Lipp's avatar
Moritz Lipp committed
517
  if (index->type == ZATHURA_LINK_EXTERNAL) {
518
    g_free(index->target.uri);
Moritz Lipp's avatar
Moritz Lipp committed
519
520
  }

521
522
  g_free(index);
}
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554

zathura_image_buffer_t*
zathura_image_buffer_create(unsigned int width, unsigned int height)
{
  zathura_image_buffer_t* image_buffer = malloc(sizeof(zathura_image_buffer_t));

  if (image_buffer == NULL) {
    return NULL;
  }

  image_buffer->data = calloc(width * height * 3, sizeof(unsigned char));

  if (image_buffer->data == NULL) {
    free(image_buffer);
    return NULL;
  }

  image_buffer->width  = width;
  image_buffer->height = height;

  return image_buffer;
}

void
zathura_image_buffer_free(zathura_image_buffer_t* image_buffer)
{
  if (image_buffer == NULL) {
    return;
  }

  free(image_buffer->data);
}