added support for gravity settings in BD engine to level editor
[rocksndiamonds.git] / src / libgame / text.c
1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // text.c
10 // ============================================================================
11
12 #include <stdio.h>
13 #include <stdarg.h>
14
15 #include "text.h"
16 #include "misc.h"
17
18
19 // ============================================================================
20 // static font variables
21 // ============================================================================
22
23 boolean text_drawing_enabled = TRUE;
24
25
26 // ============================================================================
27 // font functions
28 // ============================================================================
29
30 void EnableDrawingText(void)
31 {
32   text_drawing_enabled = TRUE;
33 }
34
35 void DisableDrawingText(void)
36 {
37   text_drawing_enabled = FALSE;
38 }
39
40 void InitFontInfo(struct FontBitmapInfo *font_bitmap_info, int num_fonts,
41                   int (*select_font_function)(int),
42                   int (*get_font_from_token_function)(char *),
43                   char * (*get_token_from_font_function)(int))
44 {
45   gfx.num_fonts = num_fonts;
46   gfx.font_bitmap_info = font_bitmap_info;
47   gfx.select_font_function = select_font_function;
48   gfx.get_font_from_token_function = get_font_from_token_function;
49   gfx.get_token_from_font_function = get_token_from_font_function;
50 }
51
52 void FreeFontInfo(struct FontBitmapInfo *font_bitmap_info)
53 {
54   if (font_bitmap_info == NULL)
55     return;
56
57   free(font_bitmap_info);
58 }
59
60 struct FontBitmapInfo *getFontBitmapInfo(int font_nr)
61 {
62   int font_bitmap_id = gfx.select_font_function(font_nr);
63
64   return &gfx.font_bitmap_info[font_bitmap_id];
65 }
66
67 int getFontWidth(int font_nr)
68 {
69   int font_bitmap_id = gfx.select_font_function(font_nr);
70
71   return gfx.font_bitmap_info[font_bitmap_id].width;
72 }
73
74 int getFontHeight(int font_nr)
75 {
76   int font_bitmap_id = gfx.select_font_function(font_nr);
77
78   return gfx.font_bitmap_info[font_bitmap_id].height;
79 }
80
81 int getFontDrawOffsetX(int font_nr)
82 {
83   int font_bitmap_id = gfx.select_font_function(font_nr);
84
85   return gfx.font_bitmap_info[font_bitmap_id].draw_xoffset;
86 }
87
88 int getFontDrawOffsetY(int font_nr)
89 {
90   int font_bitmap_id = gfx.select_font_function(font_nr);
91
92   return gfx.font_bitmap_info[font_bitmap_id].draw_yoffset;
93 }
94
95 int getTextWidth(char *text, int font_nr)
96 {
97   return (text != NULL ? strlen(text) * getFontWidth(font_nr) : 0);
98 }
99
100 static int getFontCharPosition(int font_nr, char c)
101 {
102   int font_bitmap_id = gfx.select_font_function(font_nr);
103   struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
104   int font_pos = (unsigned char)c - 32;
105
106   // map some special characters to their ascii values in default font
107   if (font->num_chars == DEFAULT_NUM_CHARS_PER_FONT)
108     font_pos = MAP_FONT_ASCII(c) - 32;
109   else if (font->num_chars == NUM_CHARS_PER_FONT_EXT)
110     font_pos = MAP_FONT_ASCII_EXT(c) - 32;
111
112   // this allows dynamic special characters together with special font
113   if (font_pos < 0 || font_pos >= font->num_chars)
114     font_pos = 0;
115
116   return font_pos;
117 }
118
119 void getFontCharSource(int font_nr, char c, Bitmap **bitmap, int *x, int *y)
120 {
121   int font_bitmap_id = gfx.select_font_function(font_nr);
122   struct FontBitmapInfo *font = &gfx.font_bitmap_info[font_bitmap_id];
123   int font_pos = getFontCharPosition(font_nr, c);
124   int offset_x = (font->offset_x != 0 ? font->offset_x : font->width);
125   int offset_y = (font->offset_y != 0 ? font->offset_y : font->height);
126
127   *bitmap = font->bitmap;
128   *x = font->src_x + (font_pos % font->num_chars_per_line) * offset_x;
129   *y = font->src_y + (font_pos / font->num_chars_per_line) * offset_y;
130 }
131
132
133 // ============================================================================
134 // text string helper functions
135 // ============================================================================
136
137 int maxWordLengthInRequestString(char *text)
138 {
139   char *text_ptr;
140   int word_len = 0, max_word_len = 0;
141
142   for (text_ptr = text; *text_ptr; text_ptr++)
143   {
144     word_len = (*text_ptr != ' ' &&
145                 *text_ptr != '?' &&
146                 *text_ptr != '!' ? word_len + 1 : 0);
147
148     max_word_len = MAX(word_len, max_word_len);
149   }
150
151   return max_word_len;
152 }
153
154
155 // ============================================================================
156 // simple text drawing functions
157 // ============================================================================
158
159 static void DrawInitTextExt(char *text, int ypos, int font_nr, boolean update)
160 {
161   LimitScreenUpdates(TRUE);
162
163   UPDATE_BUSY_STATE();
164
165   if (!text_drawing_enabled)
166     return;
167
168   if (window != NULL &&
169       gfx.draw_init_text &&
170       gfx.num_fonts > 0 &&
171       gfx.font_bitmap_info[font_nr].bitmap != NULL)
172   {
173     int x = (video.width - getTextWidth(text, font_nr)) / 2;
174     int y = ypos;
175     int width = video.width;
176     int height = getFontHeight(font_nr);
177
178     ClearRectangleOnBackground(drawto, 0, y, width, height);
179     DrawTextExt(drawto, x, y, text, font_nr, BLIT_MASKED);
180
181     if (update)
182       BlitBitmap(drawto, window, 0, 0, video.width, video.height, 0, 0);
183   }
184 }
185
186 void DrawInitText(char *text, int ypos, int font_nr)
187 {
188   DrawInitTextExt(text, ypos, font_nr, FALSE);
189 }
190
191 void DrawInitTextHead(char *text)
192 {
193   DrawInitTextExt(text, 120, FC_GREEN, FALSE);
194 }
195
196 void DrawInitTextItem(char *text)
197 {
198   DrawInitTextExt(text, 150, FC_YELLOW, TRUE);
199 }
200
201 void DrawTextF(int x, int y, int font_nr, char *format, ...)
202 {
203   char buffer[MAX_OUTPUT_LINESIZE + 1];
204   va_list ap;
205
206   va_start(ap, format);
207   vsprintf(buffer, format, ap);
208   va_end(ap);
209
210   if (strlen(buffer) > MAX_OUTPUT_LINESIZE)
211     Fail("string too long in DrawTextF() -- aborting");
212
213   DrawText(gfx.sx + x, gfx.sy + y, buffer, font_nr);
214 }
215
216 void DrawTextFCentered(int y, int font_nr, char *format, ...)
217 {
218   char buffer[MAX_OUTPUT_LINESIZE + 1];
219   va_list ap;
220
221   va_start(ap, format);
222   vsprintf(buffer, format, ap);
223   va_end(ap);
224
225   if (strlen(buffer) > MAX_OUTPUT_LINESIZE)
226     Fail("string too long in DrawTextFCentered() -- aborting");
227
228   DrawText(gfx.sx + (gfx.sxsize - getTextWidth(buffer, font_nr)) / 2,
229            gfx.sy + y, buffer, font_nr);
230 }
231
232 void DrawTextS(int x, int y, int font_nr, char *text)
233 {
234   DrawText(gfx.sx + x, gfx.sy + y, text, font_nr);
235 }
236
237 void DrawTextSCentered(int y, int font_nr, char *text)
238 {
239   DrawText(gfx.sx + (gfx.sxsize - getTextWidth(text, font_nr)) / 2,
240            gfx.sy + y, text, font_nr);
241 }
242
243 void DrawTextSAligned(int x, int y, char *text, int font_nr, int align)
244 {
245   DrawText(gfx.sx + ALIGNED_XPOS(x, getTextWidth(text, font_nr), align),
246            gfx.sy + y, text, font_nr);
247 }
248
249 void DrawText(int x, int y, char *text, int font_nr)
250 {
251   int mask_mode = BLIT_OPAQUE;
252
253   if (DrawingOnBackground(x, y))
254     mask_mode = BLIT_ON_BACKGROUND;
255
256   DrawTextExt(drawto, x, y, text, font_nr, mask_mode);
257
258   if (IN_GFX_FIELD_FULL(x, y))
259     redraw_mask |= REDRAW_FIELD;
260   else if (IN_GFX_DOOR_1(x, y))
261     redraw_mask |= REDRAW_DOOR_1;
262   else if (IN_GFX_DOOR_2(x, y))
263     redraw_mask |= REDRAW_DOOR_2;
264   else if (IN_GFX_DOOR_3(x, y))
265     redraw_mask |= REDRAW_DOOR_3;
266   else
267     redraw_mask |= REDRAW_ALL;
268 }
269
270 void DrawTextExt(DrawBuffer *dst_bitmap, int dst_x, int dst_y, char *text,
271                  int font_nr, int mask_mode)
272 {
273   struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
274   int font_width = getFontWidth(font_nr);
275   int font_height = getFontHeight(font_nr);
276   Bitmap *src_bitmap;
277   int src_x, src_y;
278   char *text_ptr = text;
279
280   if (!text_drawing_enabled)
281     return;
282
283 #if DEBUG
284   Debug("font:token", "'%s' / '%s'",
285         gfx.get_token_from_font_function(font_nr), text);
286 #endif
287
288   if (font->bitmap == NULL)
289     return;
290
291   // skip text to be printed outside the window (left/right will be clipped)
292   if (dst_y < 0 || dst_y + font_height > video.height)
293     return;
294
295   // add offset for drawing font characters
296   dst_x += font->draw_xoffset;
297   dst_y += font->draw_yoffset;
298
299   while (*text_ptr)
300   {
301     char c = *text_ptr++;
302
303     if (c == '\n')
304       c = ' ';          // print space instead of newline
305
306     getFontCharSource(font_nr, c, &src_bitmap, &src_x, &src_y);
307
308     // clip text at the left side of the window
309     if (dst_x < 0)
310     {
311       dst_x += font_width;
312
313       continue;
314     }
315
316     // clip text at the right side of the window
317     if (dst_x + font_width > video.width)
318       break;
319
320     if (mask_mode == BLIT_INVERSE)      // special mode for text gadgets
321     {
322       // first step: draw solid colored rectangle (use "cursor" character)
323       if (strlen(text) == 1)    // only one char inverted => draw cursor
324       {
325         Bitmap *cursor_bitmap;
326         int cursor_x, cursor_y;
327
328         getFontCharSource(font_nr, FONT_ASCII_CURSOR, &cursor_bitmap,
329                           &cursor_x, &cursor_y);
330
331         BlitBitmap(cursor_bitmap, dst_bitmap, cursor_x, cursor_y,
332                    font_width, font_height, dst_x, dst_y);
333       }
334
335       // second step: draw masked inverted character
336       SDLCopyInverseMasked(src_bitmap, dst_bitmap, src_x, src_y,
337                            font_width, font_height, dst_x, dst_y);
338     }
339     else if (mask_mode == BLIT_MASKED || mask_mode == BLIT_ON_BACKGROUND)
340     {
341       if (mask_mode == BLIT_ON_BACKGROUND)
342       {
343         // clear font character background
344         ClearRectangleOnBackground(dst_bitmap, dst_x, dst_y,
345                                    font_width, font_height);
346       }
347
348       BlitBitmapMasked(src_bitmap, dst_bitmap, src_x, src_y,
349                        font_width, font_height, dst_x, dst_y);
350     }
351     else        // normal, non-masked font blitting
352     {
353       BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y,
354                  font_width, font_height, dst_x, dst_y);
355     }
356
357     dst_x += font_width;
358   }
359 }
360
361
362 // ============================================================================
363 // text buffer drawing functions
364 // ============================================================================
365
366 #define MAX_LINES_FROM_FILE             1024
367
368 char *GetTextBufferFromFile(char *filename, int max_lines)
369 {
370   File *file;
371   char *buffer;
372   int num_lines = 0;
373
374   if (filename == NULL)
375     return NULL;
376
377   if (!(file = openFile(filename, MODE_READ)))
378     return NULL;
379
380   buffer = checked_calloc(1);   // start with valid, but empty text buffer
381
382   while (!checkEndOfFile(file) && num_lines < max_lines)
383   {
384     char line[MAX_LINE_LEN];
385     char *line_ptr;
386     int line_len;
387
388     // read next line of input file
389     if (!getStringFromFile(file, line, MAX_LINE_LEN))
390       break;
391
392     line_len = strlen(line);
393
394     // cut trailing line break (this can be newline and/or carriage return)
395     for (line_ptr = &line[line_len]; line_ptr >= line; line_ptr--)
396       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
397         *line_ptr = '\0';
398
399     // re-add newline (so the result is terminated by newline, but not CR/LF)
400     if (strlen(line) != line_len)
401       strcat(line, "\n");
402
403     buffer = checked_realloc(buffer, strlen(buffer) + strlen(line) + 1);
404
405     strcat(buffer, line);
406
407     num_lines++;
408   }
409
410   closeFile(file);
411
412   if (getTextEncoding(buffer) == TEXT_ENCODING_UTF_8)
413   {
414     char *body_latin1 = getLatin1FromUTF8(buffer);
415
416     checked_free(buffer);
417
418     buffer = body_latin1;
419   }
420
421   return buffer;
422 }
423
424 static boolean RenderLineToBuffer(char **src_buffer_ptr, char *dst_buffer,
425                                   int *dst_buffer_len, int line_length,
426                                   boolean last_line_was_empty)
427 {
428   char *text_ptr = *src_buffer_ptr;
429   char *buffer = dst_buffer;
430   int buffer_len = *dst_buffer_len;
431   boolean buffer_filled = FALSE;
432
433   while (*text_ptr)
434   {
435     char *word_ptr;
436     int word_len;
437
438     if (strEqual(text_ptr, "  "))       // special case: force line break
439       buffer_filled = TRUE;
440
441     // skip leading whitespaces
442     while (*text_ptr == ' ' || *text_ptr == '\t')
443       text_ptr++;
444
445     word_ptr = text_ptr;
446     word_len = 0;
447
448     // look for end of next word
449     while (*word_ptr != ' ' && *word_ptr != '\t' && *word_ptr != '\0')
450     {
451       word_ptr++;
452       word_len++;
453     }
454
455     if (word_len == 0)
456     {
457       continue;
458     }
459     else if (*text_ptr == '\n')         // special case: force empty line
460     {
461       if (buffer_len == 0)
462         text_ptr++;
463
464       // prevent printing of multiple empty lines
465       if (buffer_len > 0 || !last_line_was_empty)
466         buffer_filled = TRUE;
467     }
468     else if (word_len < line_length - buffer_len)
469     {
470       // word fits into text buffer -- add word
471
472       if (buffer_len > 0)
473         buffer[buffer_len++] = ' ';
474
475       strncpy(&buffer[buffer_len], text_ptr, word_len);
476       buffer_len += word_len;
477       buffer[buffer_len] = '\0';
478       text_ptr += word_len;
479     }
480     else if (buffer_len > 0)
481     {
482       // not enough space left for word in text buffer -- print buffer
483
484       buffer_filled = TRUE;
485     }
486     else
487     {
488       // word does not fit at all into empty text buffer -- cut word
489
490       strncpy(buffer, text_ptr, line_length);
491       buffer[line_length] = '\0';
492       text_ptr += line_length;
493       buffer_filled = TRUE;
494     }
495
496     if (buffer_filled)
497       break;
498   }
499
500   *src_buffer_ptr = text_ptr;
501   *dst_buffer_len = buffer_len;
502
503   return buffer_filled;
504 }
505
506 static boolean getCheckedTokenValueFromString(char *string, char **token,
507                                               char **value)
508 {
509   char *ptr;
510
511   if (!getTokenValueFromString(string, token, value))
512     return FALSE;
513
514   if (**token != '.')                   // token should begin with dot
515     return FALSE;
516
517   for (ptr = *token; *ptr; ptr++)       // token should contain no whitespace
518     if (*ptr == ' ' || *ptr == '\t')
519       return FALSE;
520
521   for (ptr = *value; *ptr; ptr++)       // value should contain no whitespace
522     if (*ptr == ' ' || *ptr == '\t')
523       return FALSE;
524
525   return TRUE;
526 }
527
528 static void DrawTextBuffer_Flush(int x, int y, char *buffer, int base_font_nr,
529                                  int font_nr, int line_length, int cut_length,
530                                  int mask_mode, boolean centered,
531                                  int current_ypos)
532 {
533   int buffer_len = strlen(buffer);
534   int base_font_width = getFontWidth(base_font_nr);
535   int font_width = getFontWidth(font_nr);
536   int offset_chars = (centered ? (line_length - buffer_len) / 2 : 0);
537   int line_width = base_font_width * line_length;
538   int buffer_width = font_width * buffer_len;
539   int offset_xsize = (centered ? (line_width - buffer_width) / 2 : 0);
540   int final_cut_length = MAX(0, cut_length - offset_chars);
541   int xx = x + offset_xsize;
542   int yy = y + current_ypos;
543
544   buffer[final_cut_length] = '\0';
545
546   if (mask_mode != -1)
547     DrawTextExt(drawto, xx, yy, buffer, font_nr, mask_mode);
548   else
549     DrawText(xx, yy, buffer, font_nr);
550 }
551
552 static int DrawTextBufferExt(int x, int y, char *text_buffer, int base_font_nr,
553                              int line_length, int cut_length, int max_lines,
554                              int line_spacing, int mask_mode, boolean autowrap,
555                              boolean centered, boolean parse_comments,
556                              boolean is_text_area)
557 {
558   char buffer[line_length + 1];
559   int buffer_len;
560   int font_nr = base_font_nr;
561   int font_height = getFontHeight(font_nr);
562   int line_height = font_height + line_spacing;
563   int current_line = 0;
564   int current_ypos = 0;
565   int max_ysize = max_lines * line_height;
566
567   if (text_buffer == NULL || *text_buffer == '\0')
568     return 0;
569
570   if (current_line >= max_lines)
571     return 0;
572
573   if (cut_length == -1)
574     cut_length = line_length;
575
576   buffer[0] = '\0';
577   buffer_len = 0;
578
579   while (*text_buffer && current_ypos < max_ysize)
580   {
581     char line[MAX_LINE_LEN + 1];
582     char *line_ptr;
583     boolean last_line_was_empty = TRUE;
584     int num_line_chars = MAX_LINE_LEN;
585     int i;
586
587     // copy next line from text buffer to line buffer (nearly fgets() style)
588     for (i = 0; i < num_line_chars && *text_buffer; i++)
589     {
590       if ((line[i] = *text_buffer++) == '\n')
591       {
592         // in text areas, 'line_length' sized lines cause additional empty line
593         if (i == line_length && is_text_area)
594           text_buffer--;
595
596         break;
597       }
598     }
599     line[i] = '\0';
600
601     // prevent 'num_line_chars' sized lines to cause additional empty line
602     if (i == num_line_chars && *text_buffer == '\n')
603       text_buffer++;
604
605     // skip comments (lines directly beginning with '#')
606     if (line[0] == '#' && parse_comments)
607     {
608       char *token, *value;
609
610       // try to read generic token/value pair definition after comment sign
611       if (getCheckedTokenValueFromString(line + 1, &token, &value))
612       {
613         // if found, flush the current buffer, if non-empty
614         if (buffer_len > 0 && current_ypos < max_ysize)
615         {
616           DrawTextBuffer_Flush(x, y, buffer, base_font_nr, font_nr, line_length,
617                                cut_length, mask_mode, centered, current_ypos);
618           current_ypos += line_height;
619           current_line++;
620
621           buffer[0] = '\0';
622           buffer_len = 0;
623         }
624
625         if (strEqual(token, ".font"))
626           font_nr = gfx.get_font_from_token_function(value);
627         else if (strEqual(token, ".autowrap"))
628           autowrap = get_boolean_from_string(value);
629         else if (strEqual(token, ".centered"))
630           centered = get_boolean_from_string(value);
631         else if (strEqual(token, ".parse_comments"))
632           parse_comments = get_boolean_from_string(value);
633
634         // if font has changed, depending values need to be updated as well
635         font_height = getFontHeight(font_nr);
636         line_height = font_height + line_spacing;
637       }
638
639       continue;
640     }
641
642     // cut trailing newline and carriage return from input line
643     for (line_ptr = line; *line_ptr; line_ptr++)
644     {
645       if (*line_ptr == '\n' || *line_ptr == '\r')
646       {
647         *line_ptr = '\0';
648         break;
649       }
650     }
651
652     if (strlen(line) == 0)              // special case: force empty line
653       strcpy(line, "\n");
654
655     line_ptr = line;
656
657     while (*line_ptr && current_ypos < max_ysize)
658     {
659       boolean buffer_filled;
660
661       if (autowrap)
662       {
663         buffer_filled = RenderLineToBuffer(&line_ptr, buffer, &buffer_len,
664                                            line_length, last_line_was_empty);
665       }
666       else
667       {
668         if (strlen(line_ptr) <= line_length)
669         {
670           buffer_len = strlen(line_ptr);
671           strcpy(buffer, line_ptr);
672         }
673         else
674         {
675           buffer_len = line_length;
676           strncpy(buffer, line_ptr, line_length);
677         }
678
679         buffer[buffer_len] = '\0';
680         line_ptr += buffer_len;
681
682         buffer_filled = TRUE;
683       }
684
685       if (buffer_filled)
686       {
687         DrawTextBuffer_Flush(x, y, buffer, base_font_nr, font_nr, line_length,
688                              cut_length, mask_mode, centered, current_ypos);
689         current_ypos += line_height;
690         current_line++;
691
692         last_line_was_empty = (buffer_len == 0);
693
694         buffer[0] = '\0';
695         buffer_len = 0;
696       }
697     }
698   }
699
700   if (buffer_len > 0 && current_ypos < max_ysize)
701   {
702     DrawTextBuffer_Flush(x, y, buffer, base_font_nr, font_nr, line_length,
703                          cut_length, mask_mode, centered, current_ypos);
704     current_ypos += line_height;
705     current_line++;
706   }
707
708   return current_line;
709 }
710
711 int DrawTextArea(int x, int y, char *text_buffer, int font_nr,
712                  int line_length, int cut_length, int max_lines,
713                  int line_spacing, int mask_mode, boolean autowrap,
714                  boolean centered, boolean parse_comments)
715 {
716   return DrawTextBufferExt(x, y, text_buffer, font_nr,
717                            line_length, cut_length, max_lines,
718                            line_spacing, mask_mode, autowrap,
719                            centered, parse_comments, TRUE);
720 }
721
722 int DrawTextBuffer(int x, int y, char *text_buffer, int font_nr,
723                    int line_length, int cut_length, int max_lines,
724                    int line_spacing, int mask_mode, boolean autowrap,
725                    boolean centered, boolean parse_comments)
726 {
727   return DrawTextBufferExt(x, y, text_buffer, font_nr,
728                            line_length, cut_length, max_lines,
729                            line_spacing, mask_mode, autowrap,
730                            centered, parse_comments, FALSE);
731 }
732
733 int DrawTextBufferS(int x, int y, char *text_buffer, int font_nr,
734                     int line_length, int cut_length, int max_lines,
735                     int line_spacing, int mask_mode, boolean autowrap,
736                     boolean centered, boolean parse_comments)
737 {
738   return DrawTextBuffer(gfx.sx + x, gfx.sy + y, text_buffer, font_nr,
739                         line_length, cut_length, max_lines,
740                         line_spacing, mask_mode, autowrap,
741                         centered, parse_comments);
742 }
743
744 int DrawTextBufferVA(int x, int y, char *format, va_list ap, int font_nr,
745                      int line_length, int cut_length, int max_lines,
746                      int line_spacing, int mask_mode, boolean autowrap,
747                      boolean centered, boolean parse_comments)
748 {
749   char text_buffer[MAX_OUTPUT_LINESIZE];
750   int text_length = vsnprintf(text_buffer, MAX_OUTPUT_LINESIZE, format, ap);
751
752   if (text_length >= MAX_OUTPUT_LINESIZE)
753     Warn("string too long in DrawTextBufferVA() -- truncated");
754
755   int num_lines_printed = DrawTextBuffer(x, y, text_buffer, font_nr,
756                                          line_length, cut_length, max_lines,
757                                          line_spacing, mask_mode, autowrap,
758                                          centered, parse_comments);
759   return num_lines_printed;
760 }
761
762 int DrawTextFile(int x, int y, char *filename, int font_nr,
763                  int line_length, int cut_length, int max_lines,
764                  int line_spacing, int mask_mode, boolean autowrap,
765                  boolean centered, boolean parse_comments)
766 {
767   char *text_buffer = GetTextBufferFromFile(filename, MAX_LINES_FROM_FILE);
768   int num_lines_printed = DrawTextBuffer(x, y, text_buffer, font_nr,
769                                          line_length, cut_length, max_lines,
770                                          line_spacing, mask_mode, autowrap,
771                                          centered, parse_comments);
772   checked_free(text_buffer);
773
774   return num_lines_printed;
775 }