1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      Text drawing routines.
12  *
13  *      By Shawn Hargreaves.
14  *
15  *      textout_justify() by Seymour Shlien.
16  *
17  *      Laurence Withers added the textout_ex() function family.
18  *
19  *      See readme.txt for copyright information.
20  */
21 
22 
23 #include <math.h>
24 #include <ctype.h>
25 #include "allegro5/allegro.h"
26 
27 #include "allegro5/allegro_font.h"
28 #include "allegro5/internal/aintern_dtor.h"
29 #include "allegro5/internal/aintern_font.h"
30 #include "allegro5/internal/aintern_system.h"
31 
32 /* If you call this, you're probably making a mistake. */
33 /*
34 #define strlen(s)   __are_you_sure__
35 */
36 /* Removed the above define since some compilers seem to use some
37  * preprocessor magic when calling strcmp() that inserts a call to strlen.
38  * There might be a better way to do this.
39  */
40 
41 
42 
43 /* Text usually looks best when aligned to pixels -
44  * but if x is 0.5 it may very well end up at an integer
45  * position if the current transformation scales by 2 or
46  * translated x by 0.5. So we simply apply the transformation,
47  * round to nearest integer, and backtransform that.
48  */
align_to_integer_pixel_inner(ALLEGRO_TRANSFORM const * fwd,ALLEGRO_TRANSFORM const * inv,float * x,float * y)49 static void align_to_integer_pixel_inner(
50    ALLEGRO_TRANSFORM const *fwd,
51    ALLEGRO_TRANSFORM const *inv,
52    float *x, float *y)
53 {
54    al_transform_coordinates(fwd, x, y);
55    *x = floorf(*x + 0.5f);
56    *y = floorf(*y + 0.5f);
57    al_transform_coordinates(inv, x, y);
58 }
59 
align_to_integer_pixel(float * x,float * y)60 static void align_to_integer_pixel(float *x, float *y)
61 {
62    ALLEGRO_TRANSFORM const *fwd;
63    ALLEGRO_TRANSFORM inv;
64 
65    fwd = al_get_current_transform();
66    al_copy_transform(&inv, fwd);
67    al_invert_transform(&inv);
68    align_to_integer_pixel_inner(fwd, &inv, x, y);
69 }
70 
71 
72 
73 /* Function: al_draw_ustr
74  */
al_draw_ustr(const ALLEGRO_FONT * font,ALLEGRO_COLOR color,float x,float y,int flags,const ALLEGRO_USTR * ustr)75 void al_draw_ustr(const ALLEGRO_FONT *font,
76    ALLEGRO_COLOR color, float x, float y, int flags,
77    const ALLEGRO_USTR *ustr)
78 {
79    ASSERT(font);
80    ASSERT(ustr);
81 
82    if (flags & ALLEGRO_ALIGN_CENTRE) {
83       /* Use integer division to avoid introducing a fractional
84        * component to an integer x value.
85        */
86       x -= font->vtable->text_length(font, ustr) / 2;
87    }
88    else if (flags & ALLEGRO_ALIGN_RIGHT) {
89       x -= font->vtable->text_length(font, ustr);
90    }
91 
92    if (flags & ALLEGRO_ALIGN_INTEGER)
93       align_to_integer_pixel(&x, &y);
94 
95    font->vtable->render(font, color, ustr, x, y);
96 }
97 
98 
99 
100 /* Function: al_draw_text
101  */
al_draw_text(const ALLEGRO_FONT * font,ALLEGRO_COLOR color,float x,float y,int flags,char const * text)102 void al_draw_text(const ALLEGRO_FONT *font,
103    ALLEGRO_COLOR color, float x, float y, int flags,
104    char const *text)
105 {
106    ALLEGRO_USTR_INFO info;
107    ASSERT(text);
108    al_draw_ustr(font, color, x, y, flags, al_ref_cstr(&info, text));
109 }
110 
111 
112 
113 /* Function: al_draw_justified_ustr
114  */
al_draw_justified_ustr(const ALLEGRO_FONT * font,ALLEGRO_COLOR color,float x1,float x2,float y,float diff,int flags,const ALLEGRO_USTR * ustr)115 void al_draw_justified_ustr(const ALLEGRO_FONT *font,
116    ALLEGRO_COLOR color, float x1, float x2,
117    float y, float diff, int flags, const ALLEGRO_USTR *ustr)
118 {
119    const char *whitespace = " \t\n\r";
120    ALLEGRO_USTR_INFO word_info;
121    const ALLEGRO_USTR *word;
122    int pos1, pos2;
123    int minlen;
124    int num_words;
125    int space;
126    float fleft, finc;
127    int advance;
128    ALLEGRO_TRANSFORM const *fwd = NULL;
129    ALLEGRO_TRANSFORM inv;
130 
131    ASSERT(font);
132 
133    /* count words and measure min length (without spaces) */
134    num_words = 0;
135    minlen = 0;
136    pos1 = 0;
137    for (;;) {
138       pos1 = al_ustr_find_cset_cstr(ustr, pos1, whitespace);
139       if (pos1 == -1)
140          break;
141       pos2 = al_ustr_find_set_cstr(ustr, pos1, whitespace);
142       if (pos2 == -1)
143          pos2 = al_ustr_size(ustr);
144 
145       word = al_ref_ustr(&word_info, ustr, pos1, pos2);
146       minlen += font->vtable->text_length(font, word);
147       num_words++;
148 
149       pos1 = pos2;
150    }
151 
152    /* amount of room for space between words */
153    space = x2 - x1 - minlen;
154 
155    if ((space <= 0) || (space > diff) || (num_words < 2)) {
156       /* can't justify */
157       if (flags & ALLEGRO_ALIGN_INTEGER)
158          align_to_integer_pixel(&x1, &y);
159       font->vtable->render(font, color, ustr, x1, y);
160       return;
161    }
162 
163    /* distribute space left evenly between words */
164    fleft = (float)x1;
165    finc = (float)space / (float)(num_words-1);
166    pos1 = 0;
167 
168    if (flags & ALLEGRO_ALIGN_INTEGER) {
169       fwd = al_get_current_transform();
170       al_copy_transform(&inv, fwd);
171       al_invert_transform(&inv);
172    }
173 
174    for (;;) {
175       pos1 = al_ustr_find_cset_cstr(ustr, pos1, whitespace);
176       if (pos1 == -1)
177          break;
178       pos2 = al_ustr_find_set_cstr(ustr, pos1, whitespace);
179       if (pos2 == -1)
180          pos2 = al_ustr_size(ustr);
181 
182       word = al_ref_ustr(&word_info, ustr, pos1, pos2);
183       if (flags & ALLEGRO_ALIGN_INTEGER) {
184          float drawx = fleft;
185          float drawy = y;
186          align_to_integer_pixel_inner(fwd, &inv, &drawx, &drawy);
187          advance = font->vtable->render(font, color, word, drawx, drawy);
188       }
189       else {
190          advance = font->vtable->render(font, color, word, fleft, y);
191       }
192       fleft += advance + finc;
193       pos1 = pos2;
194    }
195 }
196 
197 
198 /* Function: al_draw_justified_text
199  */
al_draw_justified_text(const ALLEGRO_FONT * font,ALLEGRO_COLOR color,float x1,float x2,float y,float diff,int flags,const char * text)200 void al_draw_justified_text(const ALLEGRO_FONT *font,
201    ALLEGRO_COLOR color, float x1, float x2,
202    float y, float diff, int flags, const char *text)
203 {
204    ALLEGRO_USTR_INFO info;
205    ASSERT(text);
206    al_draw_justified_ustr(font, color, x1, x2, y, diff, flags,
207       al_ref_cstr(&info, text));
208 }
209 
210 
211 /* Function: al_draw_textf
212  */
al_draw_textf(const ALLEGRO_FONT * font,ALLEGRO_COLOR color,float x,float y,int flags,const char * format,...)213 void al_draw_textf(const ALLEGRO_FONT *font, ALLEGRO_COLOR color,
214    float x, float y, int flags,
215    const char *format, ...)
216 {
217    ALLEGRO_USTR *buf;
218    va_list ap;
219    const char *s;
220    ASSERT(font);
221    ASSERT(format);
222 
223    /* Fast path for common case. */
224    if (0 == strcmp(format, "%s")) {
225       va_start(ap, format);
226       s = va_arg(ap, const char *);
227       al_draw_text(font, color, x, y, flags, s);
228       va_end(ap);
229       return;
230    }
231 
232    va_start(ap, format);
233    buf = al_ustr_new("");
234    al_ustr_vappendf(buf, format, ap);
235    va_end(ap);
236 
237    al_draw_text(font, color, x, y, flags, al_cstr(buf));
238 
239    al_ustr_free(buf);
240 }
241 
242 
243 
244 /* Function: al_draw_justified_textf
245  */
al_draw_justified_textf(const ALLEGRO_FONT * f,ALLEGRO_COLOR color,float x1,float x2,float y,float diff,int flags,const char * format,...)246 void al_draw_justified_textf(const ALLEGRO_FONT *f,
247    ALLEGRO_COLOR color, float x1, float x2, float y,
248    float diff, int flags, const char *format, ...)
249 {
250    ALLEGRO_USTR *buf;
251    va_list ap;
252    ASSERT(f);
253    ASSERT(format);
254 
255    va_start(ap, format);
256    buf = al_ustr_new("");
257    al_ustr_vappendf(buf, format, ap);
258    va_end(ap);
259 
260    al_draw_justified_text(f, color, x1, x2, y, diff, flags,
261       al_cstr(buf));
262 
263    al_ustr_free(buf);
264 }
265 
266 
267 
268 /* Function: al_get_ustr_width
269  */
al_get_ustr_width(const ALLEGRO_FONT * f,ALLEGRO_USTR const * ustr)270 int al_get_ustr_width(const ALLEGRO_FONT *f, ALLEGRO_USTR const *ustr)
271 {
272    ASSERT(f);
273    ASSERT(ustr);
274 
275    return f->vtable->text_length(f, ustr);
276 }
277 
278 
279 
280 /* Function: al_get_text_width
281  */
al_get_text_width(const ALLEGRO_FONT * f,const char * str)282 int al_get_text_width(const ALLEGRO_FONT *f, const char *str)
283 {
284    ALLEGRO_USTR_INFO str_info;
285    const ALLEGRO_USTR *ustr;
286    ASSERT(f);
287    ASSERT(str);
288 
289    ustr = al_ref_cstr(&str_info, str);
290 
291    return f->vtable->text_length(f, ustr);
292 }
293 
294 
295 
296 /* Function: al_get_font_line_height
297  */
al_get_font_line_height(const ALLEGRO_FONT * f)298 int al_get_font_line_height(const ALLEGRO_FONT *f)
299 {
300    ASSERT(f);
301    return f->vtable->font_height(f);
302 }
303 
304 
305 
306 /* Function: al_get_font_ascent
307  */
al_get_font_ascent(const ALLEGRO_FONT * f)308 int al_get_font_ascent(const ALLEGRO_FONT *f)
309 {
310    ASSERT(f);
311    return f->vtable->font_ascent(f);
312 }
313 
314 
315 
316 /* Function: al_get_font_descent
317  */
al_get_font_descent(const ALLEGRO_FONT * f)318 int al_get_font_descent(const ALLEGRO_FONT *f)
319 {
320    ASSERT(f);
321    return f->vtable->font_descent(f);
322 }
323 
324 
325 
326 /* Function: al_get_ustr_dimensions
327  */
al_get_ustr_dimensions(const ALLEGRO_FONT * f,ALLEGRO_USTR const * ustr,int * bbx,int * bby,int * bbw,int * bbh)328 void al_get_ustr_dimensions(const ALLEGRO_FONT *f,
329    ALLEGRO_USTR const *ustr,
330    int *bbx, int *bby, int *bbw, int *bbh)
331 {
332    ASSERT(f);
333    ASSERT(ustr);
334    f->vtable->get_text_dimensions(f, ustr, bbx, bby,
335       bbw, bbh);
336 }
337 
338 
339 
340 /* Function: al_get_text_dimensions
341  */
al_get_text_dimensions(const ALLEGRO_FONT * f,char const * text,int * bbx,int * bby,int * bbw,int * bbh)342 void al_get_text_dimensions(const ALLEGRO_FONT *f,
343    char const *text,
344    int *bbx, int *bby, int *bbw, int *bbh)
345 {
346    ALLEGRO_USTR_INFO info;
347    ASSERT(f);
348    ASSERT(text);
349 
350    f->vtable->get_text_dimensions(f, al_ref_cstr(&info, text), bbx, bby,
351       bbw, bbh);
352 }
353 
354 
355 
356 /* Function: al_destroy_font
357  */
al_destroy_font(ALLEGRO_FONT * f)358 void al_destroy_font(ALLEGRO_FONT *f)
359 {
360    if (!f)
361       return;
362 
363    _al_unregister_destructor(_al_dtor_list, f->dtor_item);
364 
365    f->vtable->destroy(f);
366 }
367 
368 
369 /* Function: al_get_font_ranges
370  */
al_get_font_ranges(ALLEGRO_FONT * f,int ranges_count,int * ranges)371 int al_get_font_ranges(ALLEGRO_FONT *f, int ranges_count, int *ranges)
372 {
373    return f->vtable->get_font_ranges(f, ranges_count, ranges);
374 }
375 
376 /* Function: al_draw_glyph
377  */
al_draw_glyph(const ALLEGRO_FONT * f,ALLEGRO_COLOR color,float x,float y,int codepoint)378 void al_draw_glyph(const ALLEGRO_FONT *f, ALLEGRO_COLOR color, float x, float y,
379    int codepoint)
380 {
381    f->vtable->render_char(f, color, codepoint, x, y);
382 };
383 
384 /* Function: al_get_glyph_width
385  */
al_get_glyph_width(const ALLEGRO_FONT * f,int codepoint)386 int al_get_glyph_width(const ALLEGRO_FONT *f, int codepoint)
387 {
388    return f->vtable->char_length(f, codepoint);
389 }
390 
391 /* Function: al_get_glyph_dimensions
392  */
al_get_glyph_dimensions(const ALLEGRO_FONT * f,int codepoint,int * bbx,int * bby,int * bbw,int * bbh)393 bool al_get_glyph_dimensions(const ALLEGRO_FONT *f,
394    int codepoint, int *bbx, int *bby, int *bbw, int *bbh)
395 {
396    return f->vtable->get_glyph_dimensions(f, codepoint, bbx, bby, bbw, bbh);
397 }
398 
399 /* Function: al_get_glyph_advance
400  */
al_get_glyph_advance(const ALLEGRO_FONT * f,int codepoint1,int codepoint2)401 int al_get_glyph_advance(const ALLEGRO_FONT *f, int codepoint1, int codepoint2)
402 {
403    return f->vtable->get_glyph_advance(f, codepoint1, codepoint2);
404 }
405 
406 /* Function: al_get_glyph
407  */
al_get_glyph(const ALLEGRO_FONT * f,int prev_codepoint,int codepoint,ALLEGRO_GLYPH * glyph)408 bool al_get_glyph(const ALLEGRO_FONT *f, int prev_codepoint, int codepoint, ALLEGRO_GLYPH *glyph)
409 {
410    return f->vtable->get_glyph(f, prev_codepoint, codepoint, glyph);
411 };
412 
413 
414 /* This helper function helps splitting an ustr in several delimited parts.
415  * It returns an ustr that refers to the next part of the string that
416  * is delimited by the delimiters in delimiter.
417  * Returns NULL at the end of the string.
418  * Pos is updated to byte index of character after the delimiter or
419  * to the end of the string.
420  */
ustr_split_next(const ALLEGRO_USTR * ustr,ALLEGRO_USTR_INFO * info,int * pos,const char * delimiter)421 static const ALLEGRO_USTR *ustr_split_next(const ALLEGRO_USTR *ustr,
422    ALLEGRO_USTR_INFO *info, int *pos, const char *delimiter)
423 {
424    const ALLEGRO_USTR *result;
425    int end, size;
426 
427    size = al_ustr_size(ustr);
428    if (*pos >= size) {
429       return NULL;
430    }
431 
432    end = al_ustr_find_set_cstr(ustr, *pos, delimiter);
433    if (end == -1)
434       end = size;
435 
436    result = al_ref_ustr(info, ustr, *pos, end);
437    /* Set pos to character AFTER delimiter */
438    al_ustr_next(ustr, &end);
439    (*pos) = end;
440    return result;
441 }
442 
443 
444 
445 /* This returns the next "soft" line of text from ustr
446  * that will fit in max_with using the font font, starting at pos *pos.
447  * These are "soft" lines because they are broken up if needed at a space
448  * or tab character.
449  * This function updates pos if needed, and returns the next "soft" line,
450  * or NULL if no more soft lines.
451  * The soft line will not include the trailing space where the
452  * line was split, but pos will be set to point to after that trailing
453  * space so iteration can continue easily.
454  */
get_next_soft_line(const ALLEGRO_USTR * ustr,ALLEGRO_USTR_INFO * info,int * pos,const ALLEGRO_FONT * font,float max_width)455 static const ALLEGRO_USTR *get_next_soft_line(const ALLEGRO_USTR *ustr,
456    ALLEGRO_USTR_INFO *info, int *pos,
457    const ALLEGRO_FONT *font, float max_width)
458 {
459    const ALLEGRO_USTR *result = NULL;
460    const char *whitespace = " \t";
461    int old_end = 0;
462    int end = 0;
463    int size = al_ustr_size(ustr);
464    bool first_word = true;
465 
466    if (*pos >= size) {
467       return NULL;
468    }
469 
470    end = *pos;
471    old_end = end;
472    do {
473       /* On to the next word. */
474       end = al_ustr_find_set_cstr(ustr, end, whitespace);
475       if (end < 0)
476          end = size;
477 
478       /* Reference to the line that is being built. */
479       result = al_ref_ustr(info, ustr, *pos, end);
480 
481       /* Check if the line is too long. If it is, return a soft line. */
482       if (al_get_ustr_width(font, result) > max_width) {
483          /* Corner case: a single word may not even fit the line.
484           * In that case, return the word/line anyway as the "soft line",
485           * the user can set a clip rectangle to cut it. */
486 
487          if (first_word) {
488             /* Set pos to character AFTER end to allow easy iteration. */
489             al_ustr_next(ustr, &end);
490             *pos = end;
491             return result;
492          }
493          else {
494             /* Not first word, return old end position without the new word */
495             result = al_ref_ustr(info, ustr, *pos, old_end);
496             /* Set pos to character AFTER end to allow easy iteration. */
497             al_ustr_next(ustr, &old_end);
498             *pos = old_end;
499             return result;
500          }
501       }
502       first_word = false;
503       old_end    = end;
504       /* Skip the character at end which normally is whitespace. */
505       al_ustr_next(ustr, &end);
506    } while (end < size);
507 
508    /* If we get here the whole ustr will fit.*/
509    result = al_ref_ustr(info, ustr, *pos, size);
510    *pos = size;
511    return result;
512 }
513 
514 
515 /* Function: al_do_multiline_ustr
516  */
al_do_multiline_ustr(const ALLEGRO_FONT * font,float max_width,const ALLEGRO_USTR * ustr,bool (* cb)(int line_num,const ALLEGRO_USTR * line,void * extra),void * extra)517 void al_do_multiline_ustr(const ALLEGRO_FONT *font, float max_width,
518    const ALLEGRO_USTR *ustr,
519    bool (*cb)(int line_num, const ALLEGRO_USTR * line, void *extra),
520    void *extra)
521 {
522    const char *linebreak  = "\n";
523    const ALLEGRO_USTR *hard_line, *soft_line;
524    ALLEGRO_USTR_INFO hard_line_info, soft_line_info;
525    int hard_line_pos = 0, soft_line_pos = 0;
526    int line_num = 0;
527    bool proceed;
528 
529    /* For every "hard" line separated by a newline character... */
530    hard_line = ustr_split_next(ustr, &hard_line_info, &hard_line_pos,
531       linebreak);
532    while (hard_line) {
533       /* For every "soft" line in the "hard" line... */
534       soft_line_pos = 0;
535       soft_line =
536       get_next_soft_line(hard_line, &soft_line_info, &soft_line_pos, font,
537          max_width);
538       /* No soft line here because it's an empty hard line. */
539       if (!soft_line) {
540          /* Call the callback with empty string to indicate an empty line. */
541          proceed = cb(line_num, al_ustr_empty_string(), extra);
542          if (!proceed) return;
543          line_num ++;
544       }
545       while(soft_line) {
546          /* Call the callback on the next soft line. */
547          proceed = cb(line_num, soft_line, extra);
548          if (!proceed) return;
549          line_num++;
550 
551          soft_line = get_next_soft_line(hard_line, &soft_line_info,
552             &soft_line_pos, font, max_width);
553       }
554       hard_line = ustr_split_next(ustr, &hard_line_info, &hard_line_pos,
555          linebreak);
556    }
557 }
558 
559 
560 
561 /* Helper struct for al_do_multiline_text. */
562 typedef struct DO_MULTILINE_TEXT_EXTRA {
563    bool (*callback)(int line_num, const char *line, int size, void *extra);
564    void *extra;
565 } DO_MULTILINE_TEXT_EXTRA;
566 
567 
568 
569 /* The functions do_multiline_text_cb is the helper callback
570  * that "adapts" al_do_multiline_ustr to al_do_multiline_text.
571  */
do_multiline_text_cb(int line_num,const ALLEGRO_USTR * line,void * extra)572 static bool do_multiline_text_cb(int line_num, const ALLEGRO_USTR *line,
573    void *extra) {
574    DO_MULTILINE_TEXT_EXTRA *s = extra;
575 
576    return s->callback(line_num, al_cstr(line), al_ustr_size(line), s->extra);
577 }
578 
579 
580 
581 /* Function: al_do_multiline_text
582  */
al_do_multiline_text(const ALLEGRO_FONT * font,float max_width,const char * text,bool (* cb)(int line_num,const char * line,int size,void * extra),void * extra)583 void al_do_multiline_text(const ALLEGRO_FONT *font,
584    float max_width, const char *text,
585    bool (*cb)(int line_num, const char *line, int size, void *extra),
586    void *extra)
587 {
588    ALLEGRO_USTR_INFO info;
589    DO_MULTILINE_TEXT_EXTRA extra2;
590    ASSERT(font);
591    ASSERT(text);
592 
593    extra2.callback = cb;
594    extra2.extra = extra;
595    al_do_multiline_ustr(font, max_width, al_ref_cstr(&info, text),
596       do_multiline_text_cb, &extra2);
597 }
598 
599 
600 
601 /* Helper struct for al_draw_multiline_ustr. */
602 typedef struct DRAW_MULTILINE_USTR_EXTRA {
603    const ALLEGRO_FONT *font;
604    ALLEGRO_COLOR color;
605    float x;
606    float y;
607    float line_height;
608    int flags;
609 } DRAW_MULTILINE_USTR_EXTRA;
610 
611 
612 
613 /* The function draw_multiline_ustr_cb is the helper callback
614  * that implements the actual drawing for al_draw_multiline_ustr.
615  */
draw_multiline_ustr_cb(int line_num,const ALLEGRO_USTR * line,void * extra)616 static bool draw_multiline_ustr_cb(int line_num, const ALLEGRO_USTR *line,
617    void *extra) {
618    DRAW_MULTILINE_USTR_EXTRA *s = extra;
619    float y;
620 
621    y  = s->y + (s->line_height * line_num);
622    al_draw_ustr(s->font, s->color, s->x, y, s->flags, line);
623    return true;
624 }
625 
626 
627 
628 /* Function: al_draw_multiline_ustr
629  */
al_draw_multiline_ustr(const ALLEGRO_FONT * font,ALLEGRO_COLOR color,float x,float y,float max_width,float line_height,int flags,const ALLEGRO_USTR * ustr)630 void al_draw_multiline_ustr(const ALLEGRO_FONT *font,
631      ALLEGRO_COLOR color, float x, float y, float max_width, float line_height,
632      int flags, const ALLEGRO_USTR *ustr)
633 {
634    DRAW_MULTILINE_USTR_EXTRA extra;
635    ASSERT(font);
636    ASSERT(ustr);
637 
638    extra.font = font;
639    extra.color = color;
640    extra.x = x;
641    extra.y = y;
642    if (line_height < 1) {
643       extra.line_height =  al_get_font_line_height(font);
644    }
645    else {
646       extra.line_height = line_height;
647    }
648    extra.flags = flags;
649 
650    al_do_multiline_ustr(font, max_width, ustr, draw_multiline_ustr_cb, &extra);
651 }
652 
653 
654 
655 /* Function: al_draw_multiline_text
656  */
al_draw_multiline_text(const ALLEGRO_FONT * font,ALLEGRO_COLOR color,float x,float y,float max_width,float line_height,int flags,const char * text)657 void al_draw_multiline_text(const ALLEGRO_FONT *font,
658      ALLEGRO_COLOR color, float x, float y, float max_width, float line_height,
659      int flags, const char *text)
660 {
661    ALLEGRO_USTR_INFO info;
662    ASSERT(font);
663    ASSERT(text);
664 
665    al_draw_multiline_ustr(font, color, x, y, max_width, line_height, flags,
666       al_ref_cstr(&info, text));
667 }
668 
669 
670 
671 /* Function: al_draw_multiline_textf
672  */
al_draw_multiline_textf(const ALLEGRO_FONT * font,ALLEGRO_COLOR color,float x,float y,float max_width,float line_height,int flags,const char * format,...)673 void al_draw_multiline_textf(const ALLEGRO_FONT *font,
674      ALLEGRO_COLOR color, float x, float y, float max_width, float line_height,
675      int flags, const char *format, ...)
676 {
677    ALLEGRO_USTR *buf;
678    va_list ap;
679    ASSERT(font);
680    ASSERT(format);
681 
682    va_start(ap, format);
683    buf = al_ustr_new("");
684    al_ustr_vappendf(buf, format, ap);
685    va_end(ap);
686 
687    al_draw_multiline_ustr(font, color, x, y, max_width, line_height, flags,
688       buf);
689 
690    al_ustr_free(buf);
691 }
692 
693 
694 /* Function: al_set_fallback_font
695  */
al_set_fallback_font(ALLEGRO_FONT * font,ALLEGRO_FONT * fallback)696 void al_set_fallback_font(ALLEGRO_FONT *font, ALLEGRO_FONT *fallback)
697 {
698    font->fallback = fallback;
699 }
700 
701 /* Function: al_get_fallback_font
702  */
al_get_fallback_font(ALLEGRO_FONT * font)703 ALLEGRO_FONT *al_get_fallback_font(ALLEGRO_FONT *font)
704 {
705    return font->fallback;
706 }
707 
708 
709 /* vim: set sts=3 sw=3 et: */
710