3bbddb33b6467c9083bc407cfa25814069f69179
[colloquium.git] / libstorycode / stylesheet.c
1 /*
2  * stylesheet.c
3  *
4  * Copyright © 2019 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 <stdlib.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <stdio.h>
32
33 #include "stylesheet.h"
34 #include "storycode.h"
35
36 enum style_mask
37 {
38         SM_FRAME_GEOM = 1<<0,
39         SM_FONT       = 1<<1,
40         SM_FGCOL      = 1<<2,
41         SM_BGCOL      = 1<<3,  /* Includes bggrad, bgcol and bgcol2 */
42         SM_PARASPACE  = 1<<4,
43         SM_PADDING    = 1<<5,
44         SM_ALIGNMENT  = 1<<6,
45 };
46
47
48 struct style
49 {
50         char *name;
51         enum style_mask set;
52
53         struct frame_geom geom;
54         char *font;
55         struct colour fgcol;
56         enum gradient bggrad;
57         struct colour bgcol;
58         struct colour bgcol2;
59         struct length paraspace[4];  /* l r t b */
60         struct length padding[4];    /* l r t b */
61         enum alignment alignment;
62
63         int n_substyles;
64         struct style *substyles;
65 };
66
67
68 struct _stylesheet
69 {
70         struct style top;
71 };
72
73
74 static void copy_col(struct colour *to, struct colour from)
75 {
76         int i;
77         for ( i=0; i<4; i++ ) to->rgba[i] = from.rgba[i];
78         to->hexcode = from.hexcode;
79 }
80
81
82 static void default_style(struct style *s)
83 {
84         s->geom.x.len = 0.0;
85         s->geom.x.unit = LENGTH_FRAC;
86         s->geom.y.len = 0.0;
87         s->geom.y.unit = LENGTH_FRAC;
88         s->geom.w.len = 1.0;
89         s->geom.w.unit = LENGTH_FRAC;
90         s->geom.h.len = 1.0;
91         s->geom.h.unit = LENGTH_FRAC;
92
93         s->font = strdup("Sans 12");
94         s->alignment = ALIGN_LEFT;
95
96         s->fgcol.rgba[0] = 0.0;
97         s->fgcol.rgba[1] = 0.0;
98         s->fgcol.rgba[2] = 0.0;
99         s->fgcol.rgba[3] = 1.0;
100         s->fgcol.hexcode = 1;
101
102         s->bggrad = GRAD_NONE;
103
104         s->bgcol.rgba[0] = 1.0;
105         s->bgcol.rgba[1] = 1.0;
106         s->bgcol.rgba[2] = 1.0;
107         s->bgcol.rgba[3] = 1.0;
108         s->bgcol.hexcode = 1;
109
110         s->bgcol2.rgba[0] = 1.0;
111         s->bgcol2.rgba[1] = 1.0;
112         s->bgcol2.rgba[2] = 1.0;
113         s->bgcol2.rgba[3] = 1.0;
114         s->bgcol2.hexcode = 1;
115
116         s->paraspace[0].len = 0.0;
117         s->paraspace[1].len = 0.0;
118         s->paraspace[2].len = 0.0;
119         s->paraspace[3].len = 0.0;
120         s->paraspace[0].unit = LENGTH_UNIT;
121         s->paraspace[1].unit = LENGTH_UNIT;
122         s->paraspace[2].unit = LENGTH_UNIT;
123         s->paraspace[3].unit = LENGTH_UNIT;
124
125         s->padding[0].len = 0.0;
126         s->padding[1].len = 0.0;
127         s->padding[2].len = 0.0;
128         s->padding[3].len = 0.0;
129         s->padding[0].unit = LENGTH_UNIT;
130         s->padding[1].unit = LENGTH_UNIT;
131         s->padding[2].unit = LENGTH_UNIT;
132         s->padding[3].unit = LENGTH_UNIT;
133 }
134
135
136 static void free_style_contents(struct style *sty)
137 {
138         int i;
139         for ( i=0; i<sty->n_substyles; i++ ) {
140                 free_style_contents(&sty->substyles[i]);
141         }
142         free(sty->name);
143         free(sty->substyles);
144 }
145
146
147 void stylesheet_free(Stylesheet *s)
148 {
149         free_style_contents(&s->top);
150         free(s);
151 }
152
153
154 static int strdotcmp(const char *a, const char *b)
155 {
156         int i = 0;
157         do {
158                 if ( (b[i] != '.') && (a[i] != b[i]) ) return 1;
159                 i++;
160         } while ( (a[i] != '\0') && (b[i] != '\0') && (b[i] != '.') );
161         return 0;
162 }
163
164
165 static struct style *lookup_style(struct style *sty, const char *path)
166 {
167         int i;
168         const char *nxt = path;
169
170         assert(sty != NULL);
171         if ( path[0] == '\0' ) return sty;
172
173         for ( i=0; i<sty->n_substyles; i++ ) {
174                 if ( strdotcmp(sty->substyles[i].name, path) == 0 ) {
175                         sty = &sty->substyles[i];
176                         break;
177                 }
178         }
179
180         nxt = strchr(nxt, '.');
181         if ( nxt == NULL ) return sty;
182         return lookup_style(sty, nxt+1);
183 }
184
185
186 static struct style *create_style(Stylesheet *ss, const char *path, const char *name)
187 {
188         struct style *sty;
189         struct style *substy_new;
190         struct style *sty_new;
191
192         sty = lookup_style(&ss->top, path);
193         substy_new = realloc(sty->substyles, (sty->n_substyles+1)*sizeof(struct style));
194         if ( substy_new == NULL ) return NULL;
195
196         sty->substyles = substy_new;
197         sty->substyles[sty->n_substyles].n_substyles = 0;
198         sty->substyles[sty->n_substyles].substyles = NULL;
199
200         sty_new = &sty->substyles[sty->n_substyles++];
201
202         sty_new->set = 0;
203         default_style(sty_new);
204         sty_new->name = strdup(name);
205
206         return sty_new;
207 }
208
209
210 Stylesheet *stylesheet_new()
211 {
212         Stylesheet *ss;
213         struct style *sty;
214
215         ss = malloc(sizeof(*ss));
216         if ( ss == NULL ) return NULL;
217
218         ss->top.n_substyles = 0;
219         ss->top.substyles = NULL;
220         ss->top.name = strdup("");
221         ss->top.set = 0;
222         default_style(&ss->top);
223
224         create_style(ss, "", "NARRATIVE");
225         create_style(ss, "NARRATIVE", "BP");
226         create_style(ss, "NARRATIVE", "PRESTITLE");
227         sty = create_style(ss, "", "SLIDE");
228         sty->geom.w.unit = LENGTH_UNIT;
229         sty->geom.w.len = 1024.0;
230         sty->geom.h.unit = LENGTH_UNIT;
231         sty->geom.h.len = 768.0;
232         create_style(ss, "SLIDE", "TEXT");
233         create_style(ss, "SLIDE", "PRESTITLE");
234         create_style(ss, "SLIDE", "SLIDETITLE");
235         create_style(ss, "SLIDE", "FOOTER");
236
237         return ss;
238 }
239
240
241 int stylesheet_get_slide_default_size(Stylesheet *s, double *w, double *h)
242 {
243         struct style *sty = lookup_style(&s->top, "SLIDE");
244         if ( sty == NULL ) return 1;
245         assert(sty->geom.w.unit == LENGTH_UNIT);
246         assert(sty->geom.h.unit == LENGTH_UNIT);
247         *w = sty->geom.w.len;
248         *h = sty->geom.h.len;
249         return 0;
250 }
251
252
253 int stylesheet_set_geometry(Stylesheet *s, const char *stn, struct frame_geom geom)
254 {
255         struct style *sty = lookup_style(&s->top, stn);
256         if ( sty == NULL ) return 1;
257         sty->geom = geom;
258         sty->set |= SM_FRAME_GEOM;
259         return 0;
260 }
261
262
263 int stylesheet_set_font(Stylesheet *s, const char *stn, char *font)
264 {
265         struct style *sty = lookup_style(&s->top, stn);
266         if ( sty == NULL ) return 1;
267         if ( sty->font != NULL ) {
268                 free(sty->font);
269         }
270         sty->font = font;
271         sty->set |= SM_FONT;
272         return 0;
273 }
274
275
276 int stylesheet_set_padding(Stylesheet *s, const char *stn, struct length padding[4])
277 {
278         int i;
279         struct style *sty = lookup_style(&s->top, stn);
280         if ( sty == NULL ) return 1;
281         for ( i=0; i<4; i++ ) {
282                 sty->padding[i] = padding[i];
283         }
284         sty->set |= SM_PADDING;
285         return 0;
286 }
287
288
289 int stylesheet_set_paraspace(Stylesheet *s, const char *stn, struct length paraspace[4])
290 {
291         int i;
292         struct style *sty = lookup_style(&s->top, stn);
293         if ( sty == NULL ) return 1;
294         for ( i=0; i<4; i++ ) {
295                 sty->paraspace[i] = paraspace[i];
296         }
297         sty->set |= SM_PARASPACE;
298         return 0;
299 }
300
301
302 int stylesheet_set_fgcol(Stylesheet *s, const char *stn, struct colour fgcol)
303 {
304         int i;
305         struct style *sty = lookup_style(&s->top, stn);
306         if ( sty == NULL ) return 1;
307         for ( i=0; i<4; i++ ) {
308                 sty->fgcol.rgba[i] = fgcol.rgba[i];
309         }
310         sty->fgcol.hexcode = fgcol.hexcode;
311         sty->set |= SM_FGCOL;
312         return 0;
313 }
314
315
316 int stylesheet_set_background(Stylesheet *s, const char *stn, enum gradient grad,
317                               struct colour bgcol, struct colour bgcol2)
318 {
319         int i;
320         struct style *sty = lookup_style(&s->top, stn);
321         if ( sty == NULL ) return 1;
322         sty->bggrad = grad;
323         for ( i=0; i<4; i++ ) {
324                 sty->bgcol.rgba[i] = bgcol.rgba[i];
325                 sty->bgcol2.rgba[i] = bgcol2.rgba[i];
326         }
327         sty->bgcol.hexcode = bgcol.hexcode;
328         sty->bgcol2.hexcode = bgcol2.hexcode;
329         sty->set |= SM_BGCOL;
330         return 0;
331 }
332
333
334 int stylesheet_set_alignment(Stylesheet *s, const char *stn, enum alignment align)
335 {
336         struct style *sty = lookup_style(&s->top, stn);
337         if ( sty == NULL ) return 1;
338         assert(align != ALIGN_INHERIT);
339         sty->alignment = align;
340         sty->set |= SM_ALIGNMENT;
341         return 0;
342 }
343
344
345 int stylesheet_get_geometry(Stylesheet *s, const char *stn, struct frame_geom *geom)
346 {
347         struct style *sty = lookup_style(&s->top, stn);
348         if ( sty == NULL ) return 1;
349         *geom = sty->geom;
350         return 0;
351 }
352
353
354 const char *stylesheet_get_font(Stylesheet *s, const char *stn,
355                                 struct colour *fgcol, enum alignment *alignment)
356 {
357         struct style *sty = lookup_style(&s->top, stn);
358         if ( sty == NULL ) return NULL;
359
360         *alignment = sty->alignment;
361         if ( fgcol != NULL ) {
362                 copy_col(fgcol, sty->fgcol);
363         }
364         return sty->font;
365 }
366
367
368 int stylesheet_get_background(Stylesheet *s, const char *stn,
369                               enum gradient *grad, struct colour *bgcol,
370                               struct colour *bgcol2)
371 {
372         struct style *sty = lookup_style(&s->top, stn);
373         if ( sty == NULL ) return 1;
374
375         copy_col(bgcol, sty->bgcol);
376         copy_col(bgcol2, sty->bgcol2);
377         *grad = sty->bggrad;
378         return 0;
379 }
380
381
382 int stylesheet_get_padding(Stylesheet *s, const char *stn,
383                            struct length padding[4])
384 {
385         int i;
386         struct style *sty = lookup_style(&s->top, stn);
387         if ( sty == NULL ) return 1;
388         for ( i=0; i<4; i++ ) padding[i] = sty->padding[i];
389         return 0;
390 }
391
392
393 int stylesheet_get_paraspace(Stylesheet *s, const char *stn,
394                            struct length paraspace[4])
395 {
396         int i;
397         struct style *sty = lookup_style(&s->top, stn);
398         if ( sty == NULL ) return 1;
399         for ( i=0; i<4; i++ ) paraspace[i] = sty->paraspace[i];
400         return 0;
401 }
402
403
404 static void add_text(char **text, size_t *len, size_t *lenmax, const char *prefix,
405                      const char *tadd)
406 {
407         size_t taddlen, prefixlen;
408
409         if ( *text == NULL ) return;
410         taddlen = strlen(tadd);
411         prefixlen = strlen(prefix);
412
413         if ( *len + taddlen + prefixlen + 1 > *lenmax ) {
414                 *lenmax += taddlen + prefixlen + 32;
415                 char *text_new = realloc(*text, *lenmax);
416                 if ( text_new == NULL ) {
417                         *text = NULL;
418                         return;
419                 }
420                 *text = text_new;
421         }
422
423         strcat(*text, prefix);
424         strcat(*text, tadd);
425         *len += taddlen + prefixlen;
426 }
427
428
429 static void format_col(char *a, size_t max_len, struct colour col)
430 {
431         if ( !col.hexcode ) {
432                 snprintf(a, max_len, "%.2f,%.2f,%.2f,%.2f",
433                          col.rgba[0], col.rgba[1], col.rgba[2], col.rgba[3]);
434         } else {
435                 snprintf(a, max_len, "#%.2X%.2X%.2X",
436                          (unsigned int)(col.rgba[0]*255),
437                          (unsigned int)(col.rgba[1]*255),
438                          (unsigned int)(col.rgba[2]*255));
439         }
440 }
441
442
443 static void add_style(char **text, size_t *len, size_t *lenmax, const char *prefix,
444                       struct style *sty)
445 {
446         char *prefix2;
447         int i;
448
449         if ( sty->set & SM_FRAME_GEOM ) {
450                 char tmp[256];
451                 snprintf(tmp, 255, "GEOMETRY %.4g%c x %.4g%c +%.4g%c +%.4g%c\n",
452                          sty->geom.w.len, unitc(sty->geom.w.unit),
453                          sty->geom.h.len, unitc(sty->geom.h.unit),
454                          sty->geom.x.len, unitc(sty->geom.x.unit),
455                          sty->geom.y.len, unitc(sty->geom.y.unit));
456                 add_text(text, len, lenmax, prefix, tmp);
457         }
458
459         if ( sty->set & SM_FONT ) {
460                 char tmp[256];
461                 snprintf(tmp, 255, "FONT %s\n", sty->font);
462                 add_text(text, len, lenmax, prefix, tmp);
463         }
464
465         if ( sty->set & SM_FGCOL ) {
466                 char tmp[256];
467                 format_col(tmp, 255, sty->fgcol);
468                 add_text(text, len, lenmax, prefix, "FGCOL ");
469                 add_text(text, len, lenmax, "", tmp);
470                 add_text(text, len, lenmax, "", "\n");
471         }
472
473         if ( sty->set & SM_BGCOL ) {
474                 char tmp[256];
475                 add_text(text, len, lenmax, prefix, "BGCOL ");
476                 add_text(text, len, lenmax, "", bgcolc(sty->bggrad));
477                 format_col(tmp, 255, sty->bgcol);
478                 add_text(text, len, lenmax, "", tmp);
479                 if ( sty->bggrad != GRAD_NONE ) {
480                         format_col(tmp, 255, sty->bgcol2);
481                         add_text(text, len, lenmax, " ", tmp);
482                 }
483                 add_text(text, len, lenmax, "", "\n");
484         }
485
486         if ( sty->set & SM_PARASPACE ) {
487                 char tmp[256];
488                 snprintf(tmp, 255, "PARASPACE %.4g%c,%.4g%c,%.4g%c,%.4g%c\n",
489                          sty->paraspace[0].len, unitc(sty->paraspace[0].unit),
490                          sty->paraspace[1].len, unitc(sty->paraspace[1].unit),
491                          sty->paraspace[2].len, unitc(sty->paraspace[2].unit),
492                          sty->paraspace[3].len, unitc(sty->paraspace[3].unit));
493                 add_text(text, len, lenmax, prefix, tmp);
494         }
495
496         if ( sty->set & SM_PADDING ) {
497                 char tmp[256];
498                 snprintf(tmp, 255, "PAD %.4g%c,%.4g%c,%.4g%c,%.4g%c\n",
499                          sty->padding[0].len, unitc(sty->padding[0].unit),
500                          sty->padding[1].len, unitc(sty->padding[1].unit),
501                          sty->padding[2].len, unitc(sty->padding[2].unit),
502                          sty->padding[3].len, unitc(sty->padding[3].unit));
503                 add_text(text, len, lenmax, prefix, tmp);
504         }
505
506         if ( sty->set & SM_ALIGNMENT ) {
507                 char tmp[256];
508                 snprintf(tmp, 255, "ALIGN %s\n", alignc(sty->alignment));
509                 add_text(text, len, lenmax, prefix, tmp);
510         }
511
512         prefix2 = malloc(strlen(prefix)+3);
513         strcpy(prefix2, prefix);
514         strcat(prefix2, "  ");
515         for ( i=0; i<sty->n_substyles; i++ ) {
516                 add_text(text, len, lenmax, prefix, sty->substyles[i].name);
517                 add_text(text, len, lenmax, "", " {\n");
518                 add_style(text, len, lenmax, prefix2, &sty->substyles[i]);
519                 add_text(text, len, lenmax, prefix, "}\n");
520         }
521         free(prefix2);
522 }
523
524
525 char *stylesheet_serialise(Stylesheet *s)
526 {
527         size_t len = 0;      /* Current length of "text", not including \0 */
528         size_t lenmax = 32;  /* Current amount of memory allocated to "text" */
529         char *text;
530
531         text = malloc(lenmax*sizeof(char));
532         if ( text == NULL ) return NULL;
533         text[0] = '\0';
534
535         add_text(&text, &len, &lenmax, "", "STYLES {\n");
536         add_style(&text, &len, &lenmax, "  ", &s->top);
537         add_text(&text, &len, &lenmax, "", "}\n\n");
538
539         return text;
540 }
541
542
543 int stylesheet_get_num_substyles(Stylesheet *s, const char *stn)
544 {
545         struct style *sty = lookup_style(&s->top, stn);
546         if ( sty == NULL ) return 0;
547         return sty->n_substyles;
548 }
549
550
551 const char *stylesheet_get_substyle_name(Stylesheet *s, const char *stn, int i)
552 {
553         struct style *sty = lookup_style(&s->top, stn);
554         if ( sty == NULL ) return NULL;
555         if ( i >= sty->n_substyles ) return NULL;
556         return sty->substyles[i].name;
557 }
558
559
560 const char *stylesheet_get_friendly_name(const char *in)
561 {
562         if ( strcmp(in, "SLIDE") == 0 ) return "Slide";
563         if ( strcmp(in, "NARRATIVE") == 0 ) return "Narrative";
564         if ( strcmp(in, "BP") == 0 ) return "Bullet point";
565         if ( strcmp(in, "SLIDETITLE") == 0 ) return "Slide title";
566         if ( strcmp(in, "PRESTITLE") == 0 ) return "Presentation title";
567         if ( strcmp(in, "TEXT") == 0 ) return "Text frame";
568         if ( strcmp(in, "FOOTER") == 0 ) return "Footer";
569         return in;
570 }