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