Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2. SDL_bdf - renders BDF fonts
  3. Copyright (C) 2002-2003 Andre de Leiradella
  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18.  
  19. For information about SDL_bdf contact leiradella@bigfoot.com
  20.  
  21. Version 1.0: first public release.
  22. Version 1.1: removed SDL dependecies, now SDL_bdf can be used with any graphics
  23.              library.
  24. Version 1.2: fixed BDF_SizeH and BDF_SizeEntitiesH to return the correct sizes.
  25. */
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include <ctype.h>
  29. #include <stdarg.h>
  30. #include <stdlib.h>
  31. #include <limits.h>
  32. #include <SDL_bdf.h>
  33.  
  34. /*
  35. BDF fonts are encoded in ascii. Each line of the file begins with a keyword,
  36. has zero or more arguments which can be integers, numbers (floats), strings and
  37. quoted string, and ends with an eol. Some keywords are optional or
  38. user-defined, unquoted strings can begin with any character which means that it
  39. can start with a minus sign making it hard to distinguish them from negative
  40. integers or numbers (and it do happen in some places). Adobe's spec isn't clear
  41. sometimes so we have to be cautious (eg it doesn't say how eols are encoded so
  42. we have to accept MSDOS (cr lf), Unix (lf) and Mac (cr) styles. I implemented a
  43. *very* relaxed parser which won't stop on some errors. The parser reads the
  44. font line by line and for each one verifies which is the keyword (using a hash
  45. table generated by gperf) and takes the appropriate action through a switch
  46. statement.
  47. */
  48.  
  49. /* BDF keywords. */
  50. #define BBX             1
  51. #define BITMAP          2
  52. #define CHARS           3
  53. #define COMMENT         4
  54. #define CONTENTVERSION  5
  55. #define DWIDTH          6
  56. #define DWIDTH1         7
  57. #define ENCODING        8
  58. #define ENDCHAR         9
  59. #define ENDFONT         10
  60. #define ENDPROPERTIES   11
  61. #define FONT            12
  62. #define FONTBOUNDINGBOX 13
  63. #define METRICSSET      14
  64. #define SIZE            15
  65. #define STARTCHAR       16
  66. #define STARTFONT       17
  67. #define STARTPROPERTIES 18
  68. #define SWIDTH          19
  69. #define SWIDTH1         20
  70. #define VVECTOR         21
  71.  
  72. /* Include GPERF hash function generated from bdf.in. */
  73. #include "bdf.gperf"
  74.  
  75. /* Reads a line from rwops up to 65536 characters. */
  76. static int readline(BDF_ReadByte getbyte, void *info, char *data) {
  77.         int k, i = 65536;
  78.  
  79.         for (;;) {
  80.                 k = getbyte(info);
  81.                 if (k == -1)
  82.                         return 0;
  83.                 switch (k) {
  84.                         default:
  85.                                 *data++=k;
  86.                                 if (--i==0) {
  87.                         case '\n':
  88.                                         *data='\0';
  89.                                         return 1;
  90.                                 }
  91.                         case '\r':
  92.                                 ;
  93.                 }
  94.         }
  95. }
  96.  
  97. /* Parse an integer updating the pointer. */
  98. static int readinteger(char **data, int *integer) {
  99.         char *aux;
  100.         int  i, signal;
  101.  
  102.         aux = *data;
  103.         signal = 1;
  104.         /* Adobe's spec doesn't say numbers can be preceeded by '+' but we never know. */
  105.         switch (*aux) {
  106.                 case '-':
  107.                         signal = -1;
  108.                 case '+':
  109.                         aux++;
  110.                         break;
  111.         }
  112.         /* Skip spaces between signal and first digit. */
  113.         while (isspace(*aux)) aux++;
  114.         if (!isdigit(*aux)) return 0;
  115.         /* Now we start reading digits. */
  116.         i = 0;
  117.         while (isdigit(*aux)) i = i * 10 + *aux++ - '0';
  118.         /* We're done, update pointer and return value. */
  119.         *data = aux;
  120.         if (integer != NULL) *integer = signal * i;
  121.         return 1;
  122. }
  123.  
  124. /* Parse a double updating the pointer. */
  125. static int readnumber(char **data, double *number) {
  126.         register char *aux;
  127.         double        n, d;
  128.         int           signal;
  129.  
  130.         aux = *data;
  131.         signal = 1;
  132.         /* Adobe's spec doesn't say numbers can be preceeded by '+' but we never know. */
  133.         switch (*aux) {
  134.                 case '-':
  135.                         signal = -1;
  136.                 case '+':
  137.                         aux++;
  138.                         break;
  139.         }
  140.         /* Skip spaces between signal and first digit. */
  141.         while (isspace(*aux)) aux++;
  142.         if (!isdigit(*aux)) return 0;
  143.         /* Now we start reading digits */
  144.         n = 0;
  145.         while (isdigit(*aux)) n = n * 10 + *aux++ - '0';
  146.         d = 10;
  147.         /* If next character is a dot then we have a decimal part. */
  148.         if (*aux == '.') {
  149.                 aux++;
  150.                 /* Decimal point must be succeeded by one or more digits. */
  151.                 if (!isdigit(*aux)) return 0;
  152.                 while (isdigit(*aux)) n += (*aux++ - '0') / d, d /= 10;
  153.         }
  154.         /* We're done, update pointer and return value. */
  155.         *data = aux;
  156.         if (number != NULL) *number = signal*n;
  157.         return 1;
  158. }
  159.  
  160. /* Parse a string updating the pointer. */
  161. static int readstring(char **data, char **string) {
  162.         int len;
  163.  
  164.         len = strlen(*data);
  165.         if (string != NULL) {
  166.                 /* Malloc the required space. */
  167.                 *string=(char *)malloc(len + 1);
  168.                 if (*string == NULL)
  169.                         return 0;
  170.                 /* Copy everything. */
  171.                 strcpy(*string, *data);
  172.         }
  173.         /* We're done, update pointer. */
  174.         *data += len;
  175.         return 1;
  176. }
  177.  
  178. /* Scan the line (just after the keyword) for elements listed in format. */
  179. static int scan(char *data, char *format, ...) {
  180.         va_list args;
  181.         int     *i;
  182.         double  *n;
  183.         char    **s;
  184.  
  185.         /* Keyword already skipped, skip spaces. */
  186.         while (*data != '\0' && isspace(*data)) data++;
  187.         /* Scan the data for the pattern in format. */
  188.         va_start(args, format);
  189.         while (*format != '\0') {
  190.                 switch (*format++) {
  191.                         case 'i': /* integer. */
  192.                                 i = va_arg(args, int *);
  193.                                 if (!readinteger(&data, i)) {
  194.                                         va_end(args);
  195.                                         return 0;
  196.                                 }
  197.                                 break;
  198.                         case 'n': /* number. */
  199.                                 n = va_arg(args, double *);
  200.                                 if (!readnumber(&data, n)) {
  201.                                         va_end(args);
  202.                                         return 0;
  203.                                 }
  204.                                 break;
  205.                         case 's': /* string. */
  206.                                 s = va_arg(args, char **);
  207.                                 if (!readstring(&data, s)) {
  208.                                         va_end(args);
  209.                                         return 0;
  210.                                 }
  211.                                 break;
  212.                 }
  213.                 /* Skip spaces between elements. */
  214.                 while (*data != '\0' && isspace(*data)) data++;
  215.         }
  216.         va_end(args);
  217.         return *data == '\0';
  218. }
  219.  
  220. /* Compare function to sort characters by their names. */
  221. static int compare(const void *c1, const void *c2) {
  222.         return strcmp(((BDF_Char *)c1)->name, ((BDF_Char *)c2)->name);
  223. }
  224.  
  225. #define XVAL(x) (isdigit((x)) ? (x) - '0' : toupper((x)) - 'A' + 10)
  226.  
  227. BDF_Font *BDF_OpenFont(BDF_ReadByte getbyte, void *info, int *error) {
  228.         BDF_Font       *font;
  229.         BDF_Char       *chr;
  230.         char           *data;
  231.         register char  *aux;
  232.         struct bdfword *word;
  233.         unsigned char  *bits;
  234.         double         n;
  235.         int            numChars;
  236.         int            dwx0, dwy0;
  237.         int            dwx1, dwy1;
  238.         int            bbw, bbh, bbxoff0x, bbyoff0y, i;
  239.  
  240.         /* Malloc the font. */
  241.         font = (BDF_Font *)malloc(sizeof(BDF_Font));
  242.         if (font == NULL) {
  243.                 if (error != NULL) *error = BDF_MEMORYERROR;
  244.                 goto error;
  245.         }
  246.         /* Null array of characters. */
  247.         font->chars = NULL;
  248.         /* Looks ugly but I'm lazy... */
  249.         data = (char *)malloc(65537 * sizeof(char));
  250.         if (data == NULL) {
  251.                 if (error != NULL) *error = BDF_MEMORYERROR;
  252.                 goto error;
  253.         }
  254.         /* Zero the structure. */
  255.         font->metricsSet = font->numChars = 0;
  256.         dwx0 = dwy0 = 0;
  257.         dwx1 = dwy1 = 0;
  258.         bbw = bbh = 0;
  259.         bbxoff0x = bbyoff0y = 0;
  260.         /* chr holds the current character or NULL if we're not inside a character definition. */
  261.         chr = NULL;
  262.         for (;;) {
  263.                 /* Read one line at a time. */
  264.                 if (!readline(getbyte, info, data)) {
  265.                         if (*error != NULL) *error = BDF_READERROR;
  266.                         goto error;
  267.                 }
  268.                 /* Find end of keyword. */
  269.                 aux = data;
  270.                 while (*aux != '\0' && !isspace(*aux)) aux++;
  271.                 /* Find which keyword it is using gperf's hash function. */
  272.                 word = (struct bdfword *)in_word_set(data, aux - data);
  273.                 switch (word == NULL ? 0 : word->code) {
  274.                         case STARTFONT:
  275.                                 /* Issue an error on versions higher than 2.2. */
  276.                                 if (!scan(aux, "n", &n)) {
  277.                                         if (error != NULL) *error = BDF_PARSEERROR;
  278.                                         goto error;
  279.                                 }
  280.                                 if (n > 2.2) {
  281.                                         if (error != NULL) *error = BDF_WRONGVERSION;
  282.                                         goto error;
  283.                                 }
  284.                                 break;
  285.                         case FONTBOUNDINGBOX:
  286.                                 /* The FONTBOUNDINGBOX values seems to be defaults for BBX values. */
  287.                                 if (!scan(aux, "iiii", &bbw, &bbh, &bbxoff0x, &bbyoff0y)) {
  288.                                         if (error != NULL) *error = BDF_PARSEERROR;
  289.                                         goto error;
  290.                                 }
  291.                                 break;
  292.                         case METRICSSET:
  293.                                 /* We only handle horizontal writing by now. */
  294.                                 if (!scan(aux, "i", &font->metricsSet)) {
  295.                                         if (error != NULL) *error = BDF_PARSEERROR;
  296.                                         goto error;
  297.                                 }
  298.                                 if (font->metricsSet != 0) {
  299.                                         if (error != NULL) *error = BDF_CANNOTHANDLEVERTICAL;
  300.                                         goto error;
  301.                                 }
  302.                                 break;
  303.                         case DWIDTH:
  304.                                 /* This is the character's width in pixels. */
  305.                                 if (chr != NULL)
  306.                                         if (!scan(aux, "ii", &chr->dwx0, &chr->dwy0)) {
  307.                                                 if (error != NULL) *error = BDF_PARSEERROR;
  308.                                                 goto error;
  309.                                         }
  310.                                 else
  311.                                         if (!scan(aux, "ii", &dwx0, &dwy0)) {
  312.                                                 if (error != NULL) *error = BDF_PARSEERROR;
  313.                                                 goto error;
  314.                                         }
  315.                                 break;
  316.                         case DWIDTH1:
  317.                                 /* This is the character's width in pixels for vertical writing. */
  318.                                 if (chr != NULL)
  319.                                         if (!scan(aux, "ii", &chr->dwx1, &chr->dwy1)) {
  320.                                                 if (error != NULL) *error = BDF_PARSEERROR;
  321.                                                 goto error;
  322.                                         }
  323.                                 else
  324.                                         if (!scan(aux, "ii", &dwx1, &dwy1)) {
  325.                                                 if (error != NULL) *error = BDF_PARSEERROR;
  326.                                                 goto error;
  327.                                         }
  328.                                 break;
  329.                         case CHARS:
  330.                                 /* We read the number of chars in this font and malloc the required memory. */
  331.                                 if (!scan(aux, "i", &font->numChars)) {
  332.                                         if (error != NULL) *error = BDF_PARSEERROR;
  333.                                         goto error;
  334.                                 }
  335.                                 font->chars = (BDF_Char *)malloc(font->numChars * sizeof(BDF_Char));
  336.                                 if (font->chars == NULL) {
  337.                                         if (error != NULL) *error = BDF_MEMORYERROR;
  338.                                         goto error;
  339.                                 }
  340.                                 /* Zero all characters' info. */
  341.                                 for (i = font->numChars, chr = font->chars; i != 0; i--, chr++) {
  342.                                         chr->name = NULL;
  343.                                         chr->code = -1;
  344.                                         chr->dwx0 = chr->dwy0 = 0;
  345.                                         chr->dwx1 = chr->dwy1 = 0;
  346.                                         chr->bbw = chr->bbh = 0;
  347.                                         chr->bbxoff0x = chr->bbyoff0y = 0;
  348.                                         chr->bits = NULL;
  349.                                 }
  350.                                 /* chr points to the current character. */
  351.                                 chr = font->chars;
  352.                                 break;
  353.                         case STARTCHAR:
  354.                                 /* If chr is NULL there are more characters in the font then expected. */
  355.                                 if (chr == NULL) {
  356.                                         if (error != NULL) *error = BDF_TOOMANYCHARACTERS;
  357.                                         goto error;
  358.                                 }
  359.                                 if (!scan(aux, "s", &chr->name)) {
  360.                                         if (error != NULL) *error = BDF_PARSEERROR;
  361.                                         goto error;
  362.                                 }
  363.                                 /* Copy default values. */
  364.                                 chr->code = -1;
  365.                                 chr->dwx0 = dwx0;
  366.                                 chr->dwy0 = dwy0;
  367.                                 chr->dwx1 = dwx1;
  368.                                 chr->dwy1 = dwy1;
  369.                                 chr->bbw = bbw;
  370.                                 chr->bbh = bbh;
  371.                                 chr->bbxoff0x = bbxoff0x;
  372.                                 chr->bbyoff0y = bbyoff0y;
  373.                                 break;
  374.                         case ENCODING:
  375.                                 /* Read character's code, it can be -1. */
  376.                                 if (chr != NULL)
  377.                                         if (!scan(aux, "i", &chr->code)) {
  378.                                                 if (error != NULL) *error = BDF_PARSEERROR;
  379.                                                 goto error;
  380.                                         }
  381.                                 break;
  382.                         case BBX:
  383.                                 /* The bounding box around the character's black pixels. */
  384.                                 if (chr != NULL)
  385.                                         if (!scan(aux, "iiii", &chr->bbw, &chr->bbh, &chr->bbxoff0x, &chr->bbyoff0y)) {
  386.                                                 if (error != NULL) *error = BDF_PARSEERROR;
  387.                                                 goto error;
  388.                                         }
  389.                                 break;
  390.                         case BITMAP:
  391.                                 /* BITMAP signals the start of the hex data. */
  392.                                 if (chr != NULL) {
  393.                                         /* wbytes is the width of the char in bytes. */
  394.                                         chr->wbytes = (chr->bbw + 7) / 8;
  395.                                         /* Malloc the memory for the pixels. */
  396.                                         chr->bits = (unsigned char *)malloc(chr->wbytes * chr->bbh);
  397.                                         if (chr->bits == NULL) {
  398.                                                 if (error != NULL) *error = BDF_MEMORYERROR;
  399.                                                 goto error;
  400.                                         }
  401.                                         /* Read all pixels from file. */
  402.                                         for (i = chr->bbh, bits = chr->bits; i != 0; i--) {
  403.                                                 if (!readline(getbyte, info, data)) {
  404.                                                         if (error != NULL) *error = BDF_READERROR;
  405.                                                         goto error;
  406.                                                 }
  407.                                                 aux = data;
  408.                                                 while (aux[0] != '\0' && aux[1] != '\0') {
  409.                                                         *bits++ = XVAL(aux[0]) * 16 + XVAL(aux[1]);
  410.                                                         aux += 2;
  411.                                                 }
  412.                                         }
  413.                                 }
  414.                                 break;
  415.                         case ENDCHAR:
  416.                                 /* Skip to the next character, makes a bound check. */
  417.                                 chr++;
  418.                                 if ((chr-font->chars) >= font->numChars)
  419.                                         chr = NULL;
  420.                                 break;
  421.                         case ENDFONT:
  422.                                 /* Ends the font, if chr is not NULL then we are short on characters. */
  423.                                 if (chr != NULL) {
  424.                                         if (error != NULL) *error = BDF_TOOFEWCHARACTERS;
  425.                                         goto error;
  426.                                 }
  427.                                 /* Sort font by character names, should be an hash table. */
  428.                                 qsort(font->chars, font->numChars, sizeof(BDF_Char), compare);
  429.                                 /* Fast pointers to characters encoded between [0..255]. */
  430.                                 for (i = 0; i < 256; i++)
  431.                                         font->code[i] = NULL;
  432.                                 for (i = font->numChars, chr = font->chars; i != 0; i--, chr++)
  433.                                         if (chr->code >= 0 && chr->code <= 255)
  434.                                                 font->code[chr->code] = chr;
  435.                                 if (error != NULL) *error = BDF_OK;
  436.                                 free(data);
  437.                                 return font;
  438.                 }
  439.         }
  440.         error:
  441.         /* Free everything. */
  442.         free(data);
  443.         BDF_CloseFont(font);
  444.         return NULL;
  445. }
  446.  
  447. void BDF_CloseFont(BDF_Font *font) {
  448.         int      i;
  449.         BDF_Char *chr;
  450.  
  451.         /* Free everything. */
  452.         if (font != NULL) {
  453.                 if (font->chars != NULL) {
  454.                         for (i = font->numChars, chr = font->chars; i != 0; i--, chr++) {
  455.                                 free(chr->name);
  456.                                 free(chr->bits);
  457.                         }
  458.                         free(font->chars);
  459.                 }
  460.                 free(font);
  461.         }
  462. }
  463.  
  464. /* Finds a char in the font, if entities is not zero then handle entities. */
  465. static BDF_Char *findchar(BDF_Font *font, char **text, int entities) {
  466.         char     *aux;
  467.         BDF_Char key, *chr;
  468.  
  469.         /* Handle entities. */
  470.         if (entities != 0 && **text == '&') {
  471.                 if ((*text)[1] != '&') {
  472.                         key.name = *text + 1;
  473.                         aux = strchr(*text, ';');
  474.                         if (aux == NULL) {
  475.                                 *text = *text + strlen(*text);
  476.                                 return NULL;
  477.                         }
  478.                         *aux = '\0';
  479.                         *text = aux + 1;
  480.                         chr = (BDF_Char *)bsearch(&key, font->chars, font->numChars, sizeof(BDF_Char), compare);
  481.                         *aux = ';';
  482.                         return chr;
  483.                 } else
  484.                         (*text)++;
  485.         }
  486.         /* Return the character in the range [0..255]. */
  487.         return font->code[*(unsigned char *)(*text)++];
  488. }
  489.  
  490. /* Determines the size of the horizontal text. */
  491. static void sizeh(BDF_Font *font, char *text, int entities, int *x0, int *y0, int *width, int *height) {
  492.         BDF_Char *chr;
  493.         int      first, y, h, minh, maxh;
  494.  
  495.         first = 1;
  496.         minh = *y0 = INT_MAX;
  497.         maxh = INT_MIN;
  498.         y = 0;
  499.         while (*text != '\0') {
  500.                 chr = findchar(font, &text, entities);
  501.                 if (first != 0) {
  502.                         first = 0;
  503.                         *x0 = *width = -chr->bbxoff0x;
  504.                 }
  505.                 if (chr != NULL) {
  506.                         h = y - (chr->bbyoff0y + chr->bbh);
  507.                         if (h < minh)
  508.                                 minh = h;
  509.                         h += chr->bbh - 1;
  510.                         if (h > maxh)
  511.                                 maxh = h;
  512.                         *width += chr->dwx0;
  513.                         if (chr->bbyoff0y < *y0)
  514.                                 *y0 = chr->bbyoff0y;
  515.                         y += chr->dwy0;
  516.                 }
  517.         }
  518.         *height = maxh - minh + 1;
  519.         *y0 += *height;
  520. }
  521.  
  522. void BDF_SizeH(BDF_Font *font, char *text, int *x0, int *y0, int *width, int *height) {
  523.         int _x0, _y0, _width, _height;
  524.  
  525.         sizeh(font, text, 0, &_x0, &_y0, &_width, &_height);
  526.         if (x0 != NULL) *x0 = _x0;
  527.         if (y0 != NULL) *y0 = _y0;
  528.         if (width != NULL) *width = _width;
  529.         if (height != NULL) *height = _height;
  530. }
  531.  
  532. void BDF_SizeEntitiesH(BDF_Font *font, char *text, int *x0, int *y0, int *width, int *height) {
  533.         int _x0, _y0, _width, _height;
  534.  
  535.         sizeh(font, text, 1, &_x0, &_y0, &_width, &_height);
  536.         if (x0 != NULL) *x0 = _x0;
  537.         if (y0 != NULL) *y0 = _y0;
  538.         if (width != NULL) *width = _width;
  539.         if (height != NULL) *height = _height;
  540. }
  541.  
  542. /* Draws a char on the surface. */
  543. static void drawchar(void *surface, BDF_PutPixel putpixel, BDF_Char *chr, int x, int y, unsigned int color) {
  544.         int           xx;
  545.         unsigned char *bits, *endfont, *endline;
  546.  
  547.         /* Calculate the position of the first pixel. */
  548.         x += chr->bbxoff0x;
  549.         y -= (chr->bbyoff0y + chr->bbh);
  550.         bits = chr->bits;
  551.         /* Put them! */
  552.         for (endfont = bits + chr->wbytes * chr->bbh; bits < endfont; y++)
  553.                 for (endline = bits + chr->wbytes, xx = x; bits < endline; xx += 8, bits++) {
  554.                         if ((*bits) & 0x80) putpixel(surface, xx, y, color);
  555.                         if ((*bits) & 0x40) putpixel(surface, xx + 1, y, color);
  556.                         if ((*bits) & 0x20) putpixel(surface, xx + 2, y, color);
  557.                         if ((*bits) & 0x10) putpixel(surface, xx + 3, y, color);
  558.                         if ((*bits) & 0x08) putpixel(surface, xx + 4, y, color);
  559.                         if ((*bits) & 0x04) putpixel(surface, xx + 5, y, color);
  560.                         if ((*bits) & 0x02) putpixel(surface, xx + 6, y, color);
  561.                         if ((*bits) & 0x01) putpixel(surface, xx + 7, y, color);
  562.                 }
  563. }
  564.  
  565. /* Draws an entire line of text. */
  566. static int drawh(void *surface, BDF_PutPixel putpixel, BDF_Font *font, char *text, int entities, int x, int y, unsigned int color) {
  567.         BDF_Char *chr;
  568.  
  569.         /* For each character... */
  570.         while (*text != '\0') {
  571.                 chr = findchar(font, &text, entities);
  572.                 if (chr != NULL) {
  573.                         /* ... draw it. */
  574.                         drawchar(surface, putpixel, chr, x, y, color);
  575.                         x += chr->dwx0;
  576.                         y += chr->dwy0;
  577.                 }
  578.         }
  579.         return x;
  580. }
  581.  
  582. int BDF_DrawH(void *surface, BDF_PutPixel putpixel, BDF_Font *font, char *text, int x, int y, unsigned int color) {
  583.         return drawh(surface, putpixel, font, text, 0, x, y, color);
  584. }
  585.  
  586. int BDF_DrawEntitiesH(void *surface, BDF_PutPixel putpixel, BDF_Font *font, char *text, int x, int y,unsigned int color) {
  587.         return drawh(surface, putpixel, font, text, 1, x, y, color);
  588. }
  589.