Frame background stuff
[colloquium.git] / src / render.c
1 /*
2  * render.c
3  *
4  * Copyright © 2013-2018 Thomas White <taw@bitwiz.org.uk>
5  *
6  * This file is part of Colloquium.
7  *
8  * Colloquium is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <cairo.h>
29 #include <cairo-pdf.h>
30 #include <pango/pangocairo.h>
31 #include <assert.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <math.h>
35 #include <gdk-pixbuf/gdk-pixbuf.h>
36 #include <gdk/gdk.h>
37
38 #include "sc_parse.h"
39 #include "sc_interp.h"
40 #include "presentation.h"
41 #include "frame.h"
42 #include "render.h"
43 #include "imagestore.h"
44 #include "utils.h"
45
46
47 static void do_background(cairo_t *cr, struct frame *fr)
48 {
49         cairo_pattern_t *patt = NULL;
50
51         if ( fr->grad == GRAD_NOBG ) return;  /* Should not end up here */
52
53         cairo_new_path(cr);
54         cairo_rectangle(cr, 0.0, 0.0, fr->w, fr->h);
55
56         switch ( fr->grad ) {
57
58                 case GRAD_NONE:
59                 cairo_set_source_rgba(cr, fr->bgcol[0],
60                                           fr->bgcol[1],
61                                           fr->bgcol[2],
62                                           fr->bgcol[3]);
63                 break;
64
65                 case GRAD_VERT:
66                 patt = cairo_pattern_create_linear(0.0, 0.0,
67                                                    0.0, fr->h);
68                 cairo_pattern_add_color_stop_rgb(patt, 0.0, fr->bgcol[0],
69                                                             fr->bgcol[1],
70                                                             fr->bgcol[2]);
71                 cairo_pattern_add_color_stop_rgb(patt, 1.0, fr->bgcol2[0],
72                                                             fr->bgcol2[1],
73                                                             fr->bgcol2[2]);
74                 cairo_set_source(cr, patt);
75                 break;
76
77                 case GRAD_HORIZ:
78                 patt = cairo_pattern_create_linear(0.0, 0.0,
79                                                    fr->w, 0.0);
80                 cairo_pattern_add_color_stop_rgb(patt, 0.0, fr->bgcol[0],
81                                                             fr->bgcol[1],
82                                                             fr->bgcol[2]);
83                 cairo_pattern_add_color_stop_rgb(patt, 1.0, fr->bgcol2[0],
84                                                             fr->bgcol2[1],
85                                                             fr->bgcol2[2]);
86                 cairo_set_source(cr, patt);
87                 break;
88
89                 case GRAD_NOBG:
90                 break;
91
92         }
93
94         cairo_fill(cr);
95         if ( patt != NULL ) cairo_pattern_destroy(patt);
96 }
97
98
99 static int draw_frame(cairo_t *cr, struct frame *fr, ImageStore *is,
100                       double min_y, double max_y)
101 {
102         int i;
103         double hpos = 0.0;
104
105         cairo_save(cr);
106         do_background(cr, fr);
107
108         /* Actually render the contents */
109         cairo_translate(cr, fr->pad_l, fr->pad_t);
110         for ( i=0; i<fr->n_paras; i++ ) {
111
112                 double cur_h = paragraph_height(fr->paras[i]);
113
114                 cairo_save(cr);
115                 cairo_translate(cr, 0.0, hpos);
116
117                 if ( (hpos + cur_h > min_y) && (hpos < max_y) ) {
118                         render_paragraph(cr, fr->paras[i], is);
119                 } /* else paragraph is not visible */
120
121                 hpos += cur_h;
122                 cairo_restore(cr);
123
124         }
125         cairo_restore(cr);
126
127         return 0;
128 }
129
130
131 int recursive_draw(struct frame *fr, cairo_t *cr,
132                    ImageStore *is,
133                    double min_y, double max_y)
134 {
135         int i;
136
137         draw_frame(cr, fr, is, min_y, max_y);
138
139         for ( i=0; i<fr->num_children; i++ ) {
140                 cairo_save(cr);
141                 cairo_translate(cr, fr->children[i]->x, fr->children[i]->y);
142                 recursive_draw(fr->children[i], cr, is,
143                                min_y - fr->children[i]->y,
144                                max_y - fr->children[i]->y);
145                 cairo_restore(cr);
146         }
147
148         return 0;
149 }
150
151
152 void wrap_frame(struct frame *fr, PangoContext *pc)
153 {
154         int i;
155         double w;
156
157         w = fr->w - fr->pad_l - fr->pad_r;
158
159         for ( i=0; i<fr->n_paras; i++ ) {
160                 wrap_paragraph(fr->paras[i], pc, w, 0, 0);
161         }
162 }
163
164
165 int recursive_wrap(struct frame *fr, PangoContext *pc)
166 {
167         int i;
168
169         wrap_frame(fr, pc);
170
171         for ( i=0; i<fr->num_children; i++ ) {
172                 recursive_wrap(fr->children[i], pc);
173         }
174
175         return 0;
176 }
177
178
179 struct frame *interp_and_shape(SCBlock *scblocks, SCBlock *stylesheet,
180                                SCCallbackList *cbl, ImageStore *is,
181                                int slide_number,
182                                PangoContext *pc, double w, double h,
183                                PangoLanguage *lang)
184 {
185         SCInterpreter *scin;
186         char snum[64];
187         struct frame *top;
188
189         top = frame_new();
190         top->resizable = 0;
191         top->x = 0.0;
192         top->y = 0.0;
193         top->w = w;
194         top->h = h;
195         top->scblocks = scblocks;
196
197         scin = sc_interp_new(pc, lang, is, top);
198         if ( scin == NULL ) {
199                 fprintf(stderr, _("Failed to set up interpreter.\n"));
200                 frame_free(top);
201                 return NULL;
202         }
203
204         sc_interp_set_callbacks(scin, cbl);
205
206         snprintf(snum, 63, "%i", slide_number);
207         add_macro(scin, "slidenumber", snum);
208
209         if ( stylesheet != NULL ) {
210                 sc_interp_run_stylesheet(scin, stylesheet);
211         }
212
213         top->fontdesc = pango_font_description_copy(sc_interp_get_fontdesc(scin));
214         top->col[0] = sc_interp_get_fgcol(scin)[0];
215         top->col[1] = sc_interp_get_fgcol(scin)[1];
216         top->col[2] = sc_interp_get_fgcol(scin)[2];
217         top->col[3] = sc_interp_get_fgcol(scin)[3];
218
219         sc_interp_add_block(scin, scblocks);
220
221         sc_interp_destroy(scin);
222
223         return top;
224 }
225
226
227 static struct frame *render_sc_with_context(SCBlock *scblocks,
228                                  cairo_t *cr, double log_w, double log_h,
229                                  SCBlock *stylesheet, SCCallbackList *cbl,
230                                  ImageStore *is,
231                                  int slide_number, PangoLanguage *lang,
232                                  PangoContext *pc)
233 {
234         struct frame *top;
235
236         cairo_rectangle(cr, 0.0, 0.0, log_w, log_h);
237         cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
238         cairo_fill(cr);
239
240         top = interp_and_shape(scblocks, stylesheet, cbl, is,
241                                slide_number, pc, log_w, log_h, lang);
242
243         recursive_wrap(top, pc);
244
245         recursive_draw(top, cr, is, 0.0, log_h);
246
247         return top;
248 }
249
250
251 cairo_surface_t *render_sc(SCBlock *scblocks, int w, int h,
252                            double log_w, double log_h,
253                            SCBlock *stylesheet, SCCallbackList *cbl,
254                            ImageStore *is,
255                            int slide_number, struct frame **ptop,
256                            PangoLanguage *lang)
257 {
258         cairo_surface_t *surf;
259         cairo_t *cr;
260         struct frame *top;
261         PangoContext *pc;
262
263         surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
264         cr = cairo_create(surf);
265         pc = pango_cairo_create_context(cr);
266         cairo_scale(cr, w/log_w, h/log_h);
267         top = render_sc_with_context(scblocks, cr, log_w, log_h,
268                                      stylesheet, cbl, is, slide_number,
269                                      lang, pc);
270         g_object_unref(pc);
271         cairo_destroy(cr);
272
273         *ptop = top;
274
275         return surf;
276 }
277
278
279 int export_pdf(struct presentation *p, const char *filename)
280 {
281         double r;
282         double w = 2048.0;
283         double scale;
284         cairo_surface_t *surf;
285         cairo_t *cr;
286         SCBlock *bl;
287         int i;
288         PangoContext *pc;
289
290         r = p->slide_height / p->slide_width;
291
292         surf = cairo_pdf_surface_create(filename, w, w*r);
293         if ( cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS ) {
294                 fprintf(stderr, _("Couldn't create Cairo surface\n"));
295                 return 1;
296         }
297
298         cr = cairo_create(surf);
299         scale = w / p->slide_width;
300         pc = pango_cairo_create_context(cr);
301
302         i = 1;
303         bl = p->scblocks;
304         while ( bl != NULL ) {
305
306                 if ( safe_strcmp(sc_block_name(bl), "slide") != 0 ) {
307                         bl = sc_block_next(bl);
308                         continue;
309                 }
310
311                 cairo_save(cr);
312
313                 cairo_scale(cr, scale, scale);
314
315                 cairo_rectangle(cr, 0.0, 0.0, p->slide_width, p->slide_height);
316                 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
317                 cairo_fill(cr);
318
319                 render_sc_with_context(bl, cr, p->slide_width,
320                                        p->slide_height, p->stylesheet, NULL,
321                                        p->is, i, p->lang, pc);
322
323                 cairo_restore(cr);
324
325                 cairo_show_page(cr);
326
327                 bl = sc_block_next(bl);
328                 i++;
329
330         }
331
332         g_object_unref(pc);
333         cairo_surface_finish(surf);
334         cairo_destroy(cr);
335
336         return 0;
337 }