1 /* GepubWidget
2  *
3  * Copyright (C) 2016 Daniel Garcia <danigm@wadobo.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include <config.h>
21 #include <gtk/gtk.h>
22 #include <JavaScriptCore/JSValueRef.h>
23 #include <locale.h>
24 
25 #include "gepub-widget.h"
26 
27 struct _GepubWidget {
28     WebKitWebView parent;
29 
30     GepubDoc *doc;
31     gboolean paginate;
32     gint chapter_length; // real chapter length
33     gint chapter_pos; // position in the chapter, a percentage based on chapter_length
34     gint length;
35     gint init_chapter_pos;
36     gint margin; // lateral margin in px
37     gint font_size; // font size in pt
38     gchar *font_family;
39     gfloat line_height;
40 };
41 
42 struct _GepubWidgetClass {
43     WebKitWebViewClass parent_class;
44 };
45 
46 enum {
47     PROP_0,
48     PROP_DOC,
49     PROP_PAGINATE,
50     PROP_CHAPTER,
51     PROP_N_CHAPTERS,
52     PROP_CHAPTER_POS,
53     NUM_PROPS
54 };
55 
56 static GParamSpec *properties[NUM_PROPS] = { NULL, };
57 
G_DEFINE_TYPE(GepubWidget,gepub_widget,WEBKIT_TYPE_WEB_VIEW)58 G_DEFINE_TYPE (GepubWidget, gepub_widget, WEBKIT_TYPE_WEB_VIEW)
59 
60 #define HUNDRED_PERCENT 100.0
61 
62 static void
63 scroll_to_chapter_pos (GepubWidget *widget) {
64     gchar *script = g_strdup_printf("document.querySelector('body').scrollTo(%d, 0)", widget->chapter_pos);
65     webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (widget), script, NULL, NULL, NULL);
66     g_free(script);
67 }
68 
69 static void
adjust_chapter_pos(GepubWidget * widget)70 adjust_chapter_pos (GepubWidget *widget)
71 {
72     // integer division to make a page start
73     gint page = widget->chapter_pos / widget->length;
74     gint next = page + 1;
75     gint d1 = widget->chapter_pos - (widget->length * page);
76     gint d2 = (widget->length * next) - widget->chapter_pos;
77 
78     if (d1 < d2) {
79         widget->chapter_pos = widget->length * page;
80     } else {
81         widget->chapter_pos = widget->length * next;
82     }
83     scroll_to_chapter_pos (widget);
84 }
85 
86 static void
pagination_initialize_finished(GObject * object,GAsyncResult * result,gpointer user_data)87 pagination_initialize_finished (GObject      *object,
88                                 GAsyncResult *result,
89                                 gpointer     user_data)
90 {
91     WebKitJavascriptResult *js_result;
92     JSValueRef              value;
93     JSGlobalContextRef      context;
94     GError                 *error = NULL;
95     GepubWidget            *widget = GEPUB_WIDGET (user_data);
96 
97     js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error);
98     if (!js_result) {
99         g_warning ("Error running javascript: %s", error->message);
100         g_error_free (error);
101         return;
102     }
103 
104     context = webkit_javascript_result_get_global_context (js_result);
105     value = webkit_javascript_result_get_value (js_result);
106     if (JSValueIsNumber (context, value)) {
107         double n;
108 
109         n = JSValueToNumber (context, value, NULL);
110         widget->chapter_length = (int)n;
111 
112         if (widget->init_chapter_pos) {
113             widget->chapter_pos = widget->init_chapter_pos * widget->chapter_length / HUNDRED_PERCENT;
114             if (widget->chapter_pos > (widget->chapter_length - widget->length)) {
115                 widget->chapter_pos = (widget->chapter_length - widget->length);
116             }
117             widget->init_chapter_pos = 0;
118         }
119 
120         if (widget->chapter_pos) {
121             adjust_chapter_pos (widget);
122         }
123     } else {
124         g_warning ("Error running javascript: unexpected return value");
125     }
126     webkit_javascript_result_unref (js_result);
127 }
128 
129 static void
get_length_finished(GObject * object,GAsyncResult * result,gpointer user_data)130 get_length_finished (GObject      *object,
131                      GAsyncResult *result,
132                      gpointer     user_data)
133 {
134     WebKitJavascriptResult *js_result;
135     JSValueRef              value;
136     JSGlobalContextRef      context;
137     GError                 *error = NULL;
138     GepubWidget            *widget = GEPUB_WIDGET (user_data);
139 
140     js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error);
141     if (!js_result) {
142         g_warning ("Error running javascript: %s", error->message);
143         g_error_free (error);
144         return;
145     }
146 
147     context = webkit_javascript_result_get_global_context (js_result);
148     value = webkit_javascript_result_get_value (js_result);
149     if (JSValueIsNumber (context, value)) {
150         double n;
151 
152         n = JSValueToNumber (context, value, NULL);
153         widget->length = (int)n;
154     } else {
155         g_warning ("Error running javascript: unexpected return value");
156     }
157     webkit_javascript_result_unref (js_result);
158 }
159 
160 static void
reload_length_cb(GtkWidget * widget,GdkRectangle * allocation,gpointer user_data)161 reload_length_cb (GtkWidget *widget,
162                   GdkRectangle *allocation,
163                   gpointer      user_data)
164 {
165     GepubWidget *gwidget = GEPUB_WIDGET (widget);
166     WebKitWebView *web_view = WEBKIT_WEB_VIEW (widget);
167     int margin, font_size;
168     float line_height;
169     gchar *script, *font_family;
170 
171     webkit_web_view_run_javascript (web_view,
172         "window.innerWidth",
173         NULL, get_length_finished, (gpointer)widget);
174 
175     margin = gwidget->margin;
176     font_size = gwidget->font_size;
177     font_family = gwidget->font_family;
178     line_height = gwidget->line_height;
179 
180     script = g_strdup_printf (
181         "if (!document.querySelector('#gepubwrap'))"
182         "document.body.innerHTML = '<div id=\"gepubwrap\">' + document.body.innerHTML + '</div>';"
183 
184         "document.querySelector('#gepubwrap').style.marginLeft = '%dpx';"
185         "document.querySelector('#gepubwrap').style.marginRight = '%dpx';"
186         , margin, margin);
187     webkit_web_view_run_javascript (web_view, script, NULL, NULL, NULL);
188     g_free (script);
189 
190     if (font_size) {
191         script = g_strdup_printf (
192             "document.querySelector('#gepubwrap').style.fontSize = '%dpt';"
193             , font_size);
194         webkit_web_view_run_javascript (web_view, script, NULL, NULL, NULL);
195         g_free (script);
196     }
197 
198     if (font_family) {
199         script = g_strdup_printf (
200             "document.querySelector('#gepubwrap').style.fontFamily = '%s';"
201             , font_family);
202         webkit_web_view_run_javascript (web_view, script, NULL, NULL, NULL);
203         g_free (script);
204     }
205 
206     if (line_height) {
207         gchar line_height_buffer[G_ASCII_DTOSTR_BUF_SIZE];
208 
209         g_ascii_formatd (line_height_buffer,
210                          G_ASCII_DTOSTR_BUF_SIZE,
211                          "%f",
212                          line_height);
213         script = g_strdup_printf (
214             "document.querySelector('#gepubwrap').style.lineHeight = %s;"
215             , line_height_buffer);
216         webkit_web_view_run_javascript (web_view, script, NULL, NULL, NULL);
217         g_free (script);
218     }
219 
220     if (gwidget->paginate) {
221         webkit_web_view_run_javascript (web_view,
222                 "document.body.style.overflow = 'hidden';"
223                 "document.body.style.margin = '20px 0px 20px 0px';"
224                 "document.body.style.padding = '0px';"
225                 "document.body.style.columnWidth = window.innerWidth+'px';"
226                 "document.body.style.height = (window.innerHeight - 40) +'px';"
227                 "document.body.style.columnGap = '0px';"
228                 "document.body.scrollWidth",
229                 NULL, pagination_initialize_finished, (gpointer)widget);
230     }
231 }
232 
233 static void
docready_cb(WebKitWebView * web_view,WebKitLoadEvent load_event,gpointer user_data)234 docready_cb (WebKitWebView  *web_view,
235              WebKitLoadEvent load_event,
236              gpointer        user_data)
237 {
238     GepubWidget *widget = GEPUB_WIDGET (web_view);
239 
240     if (load_event == WEBKIT_LOAD_FINISHED) {
241         reload_length_cb (GTK_WIDGET (widget), NULL, NULL);
242     }
243 }
244 
245 static void
resource_callback(WebKitURISchemeRequest * request,gpointer user_data)246 resource_callback (WebKitURISchemeRequest *request, gpointer user_data)
247 {
248     GInputStream *stream;
249     gchar *path;
250     gchar *uri;
251     gchar *mime;
252     GepubWidget *widget = user_data;
253     GBytes *contents;
254 
255     if (!widget->doc)
256       return;
257 
258     uri = g_strdup (webkit_uri_scheme_request_get_uri (request));
259     // removing "epub:///"
260     path = uri + 8;
261     contents = gepub_doc_get_resource (widget->doc, path);
262     mime = gepub_doc_get_resource_mime (widget->doc, path);
263 
264     // if the resource requested doesn't exist, we should serve an
265     // empty document instead of nothing at all (otherwise some
266     // poorly-structured ebooks will fail to render).
267     if (!contents) {
268         contents = g_byte_array_free_to_bytes(g_byte_array_sized_new(0));
269         mime = g_strdup("application/octet-stream");
270     }
271 
272     if (!mime) {
273         mime = g_strdup("application/octet-stream");
274     }
275 
276     stream = g_memory_input_stream_new_from_bytes (contents);
277     webkit_uri_scheme_request_finish (request, stream, g_bytes_get_size (contents), mime);
278 
279     g_object_unref (stream);
280     g_bytes_unref (contents);
281     g_free (mime);
282     g_free (uri);
283 }
284 
285 static void
gepub_widget_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)286 gepub_widget_set_property (GObject      *object,
287                            guint         prop_id,
288                            const GValue *value,
289                            GParamSpec   *pspec)
290 {
291     GepubWidget *widget = GEPUB_WIDGET (object);
292 
293     switch (prop_id) {
294     case PROP_DOC:
295         gepub_widget_set_doc (widget, g_value_get_object (value));
296         break;
297     case PROP_PAGINATE:
298         gepub_widget_set_paginate (widget, g_value_get_boolean (value));
299         break;
300     case PROP_CHAPTER:
301         gepub_doc_set_chapter (widget->doc, g_value_get_int (value));
302         break;
303     case PROP_CHAPTER_POS:
304         gepub_widget_set_pos (widget, g_value_get_float (value));
305         break;
306     default:
307         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
308         break;
309     }
310 }
311 
312 static void
gepub_widget_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)313 gepub_widget_get_property (GObject    *object,
314                            guint       prop_id,
315                            GValue     *value,
316                            GParamSpec *pspec)
317 {
318     GepubWidget *widget = GEPUB_WIDGET (object);
319 
320     switch (prop_id) {
321     case PROP_DOC:
322         g_value_set_object (value, gepub_widget_get_doc (widget));
323         break;
324     case PROP_PAGINATE:
325         g_value_set_boolean (value, widget->paginate);
326         break;
327     case PROP_CHAPTER:
328         g_value_set_int (value, gepub_doc_get_chapter (widget->doc));
329         break;
330     case PROP_N_CHAPTERS:
331         g_value_set_int (value, gepub_doc_get_n_chapters (widget->doc));
332         break;
333     case PROP_CHAPTER_POS:
334         g_value_set_float (value, gepub_widget_get_pos (widget));
335         break;
336     default:
337         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
338         break;
339     }
340 }
341 
342 static void
gepub_widget_finalize(GObject * object)343 gepub_widget_finalize (GObject *object)
344 {
345     GepubWidget *widget = GEPUB_WIDGET (object);
346 
347     g_clear_pointer (&widget->font_family, g_free);
348     g_clear_object (&widget->doc);
349 
350     G_OBJECT_CLASS (gepub_widget_parent_class)->finalize (object);
351 }
352 
353 static void
gepub_widget_init(GepubWidget * widget)354 gepub_widget_init (GepubWidget *widget)
355 {
356     widget->chapter_length = 0;
357     widget->paginate = FALSE;
358     widget->chapter_pos = 0;
359     widget->length = 0;
360     widget->init_chapter_pos = 0;
361     widget->margin = 20;
362     widget->font_size = 0;
363     widget->font_family = NULL;
364     widget->line_height = 0;
365 }
366 
367 static void
gepub_widget_constructed(GObject * object)368 gepub_widget_constructed (GObject *object)
369 {
370     WebKitWebContext *ctx;
371     GepubWidget *widget = GEPUB_WIDGET (object);
372 
373     G_OBJECT_CLASS (gepub_widget_parent_class)->constructed (object);
374 
375     ctx = webkit_web_view_get_context (WEBKIT_WEB_VIEW (widget));
376     webkit_web_context_register_uri_scheme (ctx, "epub", resource_callback, widget, NULL);
377     g_signal_connect (widget, "load-changed", G_CALLBACK (docready_cb), NULL);
378     g_signal_connect (widget, "size-allocate", G_CALLBACK (reload_length_cb), NULL);
379 }
380 
381 static void
gepub_widget_class_init(GepubWidgetClass * klass)382 gepub_widget_class_init (GepubWidgetClass *klass)
383 {
384     GObjectClass *object_class = G_OBJECT_CLASS (klass);
385 
386     object_class->constructed = gepub_widget_constructed;
387     object_class->finalize = gepub_widget_finalize;
388     object_class->set_property = gepub_widget_set_property;
389     object_class->get_property = gepub_widget_get_property;
390 
391     properties[PROP_DOC] =
392         g_param_spec_object ("doc",
393                              "The GepubDoc",
394                              "The GepubDoc for this widget",
395                              GEPUB_TYPE_DOC,
396                              G_PARAM_READWRITE |
397                              G_PARAM_STATIC_STRINGS);
398 
399     properties[PROP_PAGINATE] =
400         g_param_spec_boolean ("paginate",
401                               "paginate",
402                               "If the widget should paginate",
403                               FALSE,
404                               G_PARAM_READWRITE);
405 
406     properties[PROP_CHAPTER] =
407         g_param_spec_int ("chapter",
408                           "Current chapter",
409                           "Current chapter in the doc",
410                           -1, G_MAXINT, 0,
411                           G_PARAM_READWRITE);
412 
413     properties[PROP_N_CHAPTERS] =
414         g_param_spec_int ("nchapters",
415                           "Number of chapters in the doc",
416                           "Number of chapters in the doc",
417                           -1, G_MAXINT, 0,
418                           G_PARAM_READABLE);
419 
420     properties[PROP_CHAPTER_POS] =
421         g_param_spec_float ("chapter_pos",
422                             "Current position in chapter",
423                             "Current position in chapter as a percentage",
424                             0.0, HUNDRED_PERCENT, 0.0,
425                             G_PARAM_READWRITE);
426 
427     g_object_class_install_properties (object_class, NUM_PROPS, properties);
428 }
429 
430 /**
431  * gepub_widget_new:
432  *
433  * Returns: (transfer full): the new GepubWidget created
434  */
435 GtkWidget *
gepub_widget_new(void)436 gepub_widget_new (void)
437 {
438   return g_object_new (GEPUB_TYPE_WIDGET,
439                        NULL);
440 }
441 
442 /**
443  * gepub_widget_get_doc:
444  * @widget: a #GepubWidget
445  *
446  * Returns: (transfer none): the #GepubDoc
447  */
448 GepubDoc *
gepub_widget_get_doc(GepubWidget * widget)449 gepub_widget_get_doc (GepubWidget *widget)
450 {
451     g_return_val_if_fail (GEPUB_IS_WIDGET (widget), NULL);
452 
453     return widget->doc;
454 }
455 
456 static void
reload_current_chapter(GepubWidget * widget)457 reload_current_chapter (GepubWidget *widget)
458 {
459     GBytes *current;
460 
461     widget->chapter_length = 0;
462     widget->chapter_pos = 0;
463     widget->length = 0;
464 
465     if (widget->doc == NULL)
466         return;
467 
468     current = gepub_doc_get_current_with_epub_uris (widget->doc);
469     webkit_web_view_load_bytes (WEBKIT_WEB_VIEW (widget),
470                                 current,
471                                 gepub_doc_get_current_mime (widget->doc),
472                                 "UTF-8", NULL);
473     g_bytes_unref (current);
474 }
475 
476 /**
477  * gepub_widget_set_doc:
478  * @widget: a #GepubWidget
479  * @doc: (nullable): a #GepubDoc
480  *
481  * Sets @doc as the document displayed by the widget.
482  */
483 void
gepub_widget_set_doc(GepubWidget * widget,GepubDoc * doc)484 gepub_widget_set_doc (GepubWidget *widget,
485                       GepubDoc    *doc)
486 {
487     g_return_if_fail (GEPUB_IS_WIDGET (widget));
488 
489     if (widget->doc == doc)
490         return;
491 
492     if (widget->doc != NULL) {
493         g_signal_handlers_disconnect_by_func (widget->doc,
494                                               reload_current_chapter, widget);
495         g_object_unref (widget->doc);
496     }
497 
498     widget->doc = doc;
499 
500     if (widget->doc != NULL) {
501         g_object_ref (widget->doc);
502         reload_current_chapter (widget);
503         g_signal_connect_swapped (widget->doc, "notify::chapter",
504                                   G_CALLBACK (reload_current_chapter), widget);
505     }
506 
507     g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_DOC]);
508 }
509 
510 /**
511  * gepub_widget_get_paginate:
512  * @widget: a #GepubWidget
513  *
514  * Returns whether pagination is enabled or disabled
515  */
516 gboolean
gepub_widget_get_paginate(GepubWidget * widget)517 gepub_widget_get_paginate (GepubWidget *widget)
518 {
519     g_return_val_if_fail (GEPUB_IS_WIDGET (widget), FALSE);
520 
521     return widget->paginate;
522 }
523 
524 /**
525  * gepub_widget_set_paginate:
526  * @widget: a #GepubWidget
527  * @p: true if the widget should paginate
528  *
529  * Enable or disable pagination
530  */
531 void
gepub_widget_set_paginate(GepubWidget * widget,gboolean p)532 gepub_widget_set_paginate (GepubWidget *widget,
533                            gboolean p)
534 {
535     g_return_if_fail (GEPUB_IS_WIDGET (widget));
536 
537     widget->paginate = p;
538     reload_current_chapter (widget);
539 }
540 
541 /**
542  * gepub_widget_get_n_chapters:
543  * @widget: a #GepubWidget
544  *
545  * Returns: the number of chapters in the document
546  */
547 gint
gepub_widget_get_n_chapters(GepubWidget * widget)548 gepub_widget_get_n_chapters (GepubWidget *widget)
549 {
550     g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0);
551     return gepub_doc_get_n_chapters (widget->doc);
552 }
553 
554 /**
555  * gepub_widget_get_chapter:
556  * @widget: a #GepubWidget
557  *
558  * Returns: the current chapter in the document
559  */
560 gint
gepub_widget_get_chapter(GepubWidget * widget)561 gepub_widget_get_chapter (GepubWidget *widget)
562 {
563     g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0);
564     return gepub_doc_get_chapter (widget->doc);
565 }
566 
567 /**
568  * gepub_widget_get_chapter_length:
569  * @widget: a #GepubWidget
570  *
571  * Returns: the current chapter length
572  */
573 gint
gepub_widget_get_chapter_length(GepubWidget * widget)574 gepub_widget_get_chapter_length (GepubWidget *widget)
575 {
576     g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0);
577     return widget->chapter_length;
578 }
579 
580 /**
581  * gepub_widget_set_chapter:
582  * @widget: a #GepubWidget
583  * @index: the new chapter
584  *
585  * Sets the current chapter in the doc
586  */
587 void
gepub_widget_set_chapter(GepubWidget * widget,gint index)588 gepub_widget_set_chapter (GepubWidget *widget,
589                           gint         index)
590 {
591     g_return_if_fail (GEPUB_IS_DOC (widget->doc));
592     return gepub_doc_set_chapter (widget->doc, index);
593 }
594 
595 /**
596  * gepub_widget_chapter_next:
597  * @widget: a #GepubWidget
598  *
599  * Returns: TRUE on success, FALSE if there's no next chapter
600  */
601 gboolean
gepub_widget_chapter_next(GepubWidget * widget)602 gepub_widget_chapter_next (GepubWidget *widget)
603 {
604     g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE);
605     return gepub_doc_go_next (widget->doc);
606 }
607 
608 /**
609  * gepub_widget_chapter_prev:
610  * @widget: a #GepubWidget
611  *
612  * Returns: TRUE on success, FALSE if there's no prev chapter
613  */
614 gboolean
gepub_widget_chapter_prev(GepubWidget * widget)615 gepub_widget_chapter_prev (GepubWidget *widget)
616 {
617     g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE);
618     return gepub_doc_go_prev (widget->doc);
619 }
620 
621 /**
622  * gepub_widget_page_next:
623  * @widget: a #GepubWidget
624  *
625  * Returns: TRUE on success, FALSE if there's no next page
626  */
627 gboolean
gepub_widget_page_next(GepubWidget * widget)628 gepub_widget_page_next (GepubWidget *widget)
629 {
630     g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE);
631     widget->chapter_pos = widget->chapter_pos + widget->length;
632 
633     if (widget->chapter_pos > (widget->chapter_length - widget->length)) {
634         widget->chapter_pos = (widget->chapter_length - widget->length);
635         return gepub_doc_go_next (widget->doc);
636     }
637 
638     scroll_to_chapter_pos (widget);
639 
640     g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_CHAPTER_POS]);
641     return TRUE;
642 }
643 
644 /**
645  * gepub_widget_page_prev:
646  * @widget: a #GepubWidget
647  *
648  * Returns: TRUE on success, FALSE if there's no next page
649  */
650 gboolean
gepub_widget_page_prev(GepubWidget * widget)651 gepub_widget_page_prev (GepubWidget *widget)
652 {
653     g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), FALSE);
654     widget->chapter_pos = widget->chapter_pos - widget->length;
655 
656     if (widget->chapter_pos < 0) {
657         widget->init_chapter_pos = HUNDRED_PERCENT;
658         return gepub_doc_go_prev (widget->doc);
659     }
660 
661     scroll_to_chapter_pos (widget);
662 
663     g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_CHAPTER_POS]);
664     return TRUE;
665 }
666 
667 /**
668  * gepub_widget_get_pos:
669  * @widget: a #GepubWidget
670  *
671  * Returns: the current position in the chapter
672  */
673 gfloat
gepub_widget_get_pos(GepubWidget * widget)674 gepub_widget_get_pos (GepubWidget *widget)
675 {
676     g_return_val_if_fail (GEPUB_IS_DOC (widget->doc), 0);
677 
678     if (!widget->chapter_length) {
679         return 0;
680     }
681 
682     return widget->chapter_pos * HUNDRED_PERCENT / (float)(widget->chapter_length);
683 }
684 
685 /**
686  * gepub_widget_set_pos:
687  * @widget: a #GepubWidget
688  * @index: the new pos
689  *
690  * Sets the current position in the chapter
691  */
692 void
gepub_widget_set_pos(GepubWidget * widget,gfloat index)693 gepub_widget_set_pos (GepubWidget *widget,
694                       gfloat       index)
695 {
696     g_return_if_fail (GEPUB_IS_DOC (widget->doc));
697     widget->chapter_pos = index * widget->chapter_length / HUNDRED_PERCENT;
698     adjust_chapter_pos (widget);
699 
700     g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_CHAPTER_POS]);
701 }
702 
703 
704 /**
705  * gepub_widget_set_margin:
706  * @widget: a #GepubWidget
707  * @margin: the margin in pixels
708  *
709  * Sets the widget left and right margin
710  */
711 void
gepub_widget_set_margin(GepubWidget * widget,gint margin)712 gepub_widget_set_margin (GepubWidget *widget,
713                          gint         margin)
714 {
715     widget->margin = margin;
716     reload_length_cb (GTK_WIDGET (widget), NULL, NULL);
717 }
718 
719 /**
720  * gepub_widget_get_margin:
721  * @widget: a #GepubWidget
722  *
723  * Gets the widget left and right margin
724  */
725 gint
gepub_widget_get_margin(GepubWidget * widget)726 gepub_widget_get_margin (GepubWidget *widget)
727 {
728     return widget->margin;
729 }
730 
731 /**
732  * gepub_widget_get_fontsize:
733  * @widget: a #GepubWidget
734  *
735  * Gets the widget custom font size in pt, if 0, it's not set
736  */
737 gint
gepub_widget_get_fontsize(GepubWidget * widget)738 gepub_widget_get_fontsize (GepubWidget *widget)
739 {
740     return widget->font_size;
741 }
742 
743 /**
744  * gepub_widget_set_fontsize:
745  * @widget: a #GepubWidget
746  * @size: the custom font size in pt
747  *
748  * Sets the widget custom font size, use 0 to show book's styles
749  */
750 void
gepub_widget_set_fontsize(GepubWidget * widget,gint size)751 gepub_widget_set_fontsize (GepubWidget *widget,
752                            gint         size)
753 {
754     widget->font_size = size;
755     reload_length_cb (GTK_WIDGET (widget), NULL, NULL);
756 }
757 
758 /**
759  * gepub_widget_get_fontfamily:
760  * @widget: a #GepubWidget
761  *
762  * Gets the widget custom font family
763  */
764 gchar *
gepub_widget_get_fontfamily(GepubWidget * widget)765 gepub_widget_get_fontfamily (GepubWidget *widget)
766 {
767     return widget->font_family;
768 }
769 
770 /**
771  * gepub_widget_set_fontfamily:
772  * @widget: a #GepubWidget
773  * @family: the custom font family name
774  *
775  * Sets the widget custom font family
776  */
777 void
gepub_widget_set_fontfamily(GepubWidget * widget,gchar * family)778 gepub_widget_set_fontfamily (GepubWidget *widget,
779                              gchar       *family)
780 {
781     g_clear_pointer (&widget->font_family, g_free);
782 
783     widget->font_family = g_strdup (family);
784     reload_length_cb (GTK_WIDGET (widget), NULL, NULL);
785 }
786 
787 /**
788  * gepub_widget_get_lineheight:
789  * @widget: a #GepubWidget
790  *
791  * Gets the widget custom line height, if 0, it's not set
792  */
793 gfloat
gepub_widget_get_lineheight(GepubWidget * widget)794 gepub_widget_get_lineheight (GepubWidget *widget)
795 {
796     return widget->line_height;
797 }
798 
799 /**
800  * gepub_widget_set_lineheight:
801  * @widget: a #GepubWidget
802  * @size: the custom line height
803  *
804  * Sets the widget custom line height, the real size will be this
805  * number multiplied by the font size.
806  * Use 0 to show book's styles
807  */
808 void
gepub_widget_set_lineheight(GepubWidget * widget,gfloat size)809 gepub_widget_set_lineheight (GepubWidget *widget,
810                              gfloat       size)
811 {
812     widget->line_height = size;
813     reload_length_cb (GTK_WIDGET (widget), NULL, NULL);
814 }
815