diff -r 514e61338063 Makefile.am --- a/Makefile.am Fri Jun 26 19:05:28 2015 -0700 +++ b/Makefile.am Tue Jan 05 14:45:46 2016 +0400 @@ -9,6 +9,11 @@ libSDL2_ttf_la_SOURCES = \ SDL_ttf.c +if HAVE_CTL +libSDL2_ttf_la_SOURCES += raqm.c +libSDL2_ttfinclude_HEADERS += raqm.h +endif + EXTRA_DIST = \ Android.mk \ debian \ diff -r 514e61338063 SDL_ttf.c --- a/SDL_ttf.c Fri Jun 26 19:05:28 2015 -0700 +++ b/SDL_ttf.c Tue Jan 05 14:45:46 2016 +0400 @@ -35,6 +35,12 @@ #include "SDL_endian.h" #include "SDL_ttf.h" +#ifdef HAVE_CTL +#include "raqm.h" +#define SHAPE_TEXT(text, textlen, font, info) \ + raqm_shape(text, textlen, font->face, RAQM_DIRECTION_DEFAULT, NULL, info); +#endif + /* FIXME: Right now we assume the gray-scale renderer Freetype is using supports 256 shades of gray, but we should instead key off of num_grays in the result FT_Bitmap after the FT_Render_Glyph() call. */ @@ -331,6 +337,67 @@ #endif /* USE_FREETYPE_ERRORS */ } +static FT_Error Find_Glyph( TTF_Font* font, Uint16 ch, int want ); +static Uint32 UTF8_getch(const char **src, size_t *srclen); + +#ifndef HAVE_CTL +#define SHAPE_TEXT(text, textlen, font, info) \ + fallback_shape(text, textlen, font, info); + +typedef struct { + int index; + int x_offset; + int x_advance; + int y_offset; +} raqm_glyph_info_t; + +int fallback_shape(const char *text, size_t textlen, TTF_Font *font, raqm_glyph_info_t **info) +{ + int xstart, count; + c_glyph *glyph; + FT_Error error; + FT_Long use_kerning; + FT_UInt prev_index = 0; + raqm_glyph_info_t *g_info; + + /* check kerning */ + use_kerning = FT_HAS_KERNING( font->face ) && font->kerning; + + g_info = (raqm_glyph_info_t*) malloc(sizeof(raqm_glyph_info_t) * (textlen)); + + xstart = 0; + for ( count = 0; textlen > 0; count++ ) { + Uint16 c = UTF8_getch(&text, &textlen); + if ( c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED ) { + continue; + } + + error = Find_Glyph(font, c, CACHED_METRICS|CACHED_BITMAP); + if ( error ) { + TTF_SetFTError("Couldn't find glyph", error); + exit(-1); + } + glyph = font->current; + + g_info[count].index = glyph->index; + g_info[count].y_offset = 0; + g_info[count].x_offset = 0; + g_info[count].x_advance = glyph->advance; + + /* do kerning, if possible AC-Patch */ + if ( use_kerning && prev_index && glyph->index ) { + FT_Vector delta; + FT_Get_Kerning( font->face, prev_index, g_info->index, ft_kerning_default, &delta ); + xstart += delta.x >> 6; + g_info[count - 1].x_advance += delta.x; + } + } + + *info = g_info; + return count; +} +#endif + int TTF_Init( void ) { int status = 0; @@ -618,7 +685,7 @@ cached->maxy = FT_FLOOR(metrics->horiBearingY); cached->miny = cached->maxy - FT_CEIL(metrics->height); cached->yoffset = font->ascent - cached->maxy; - cached->advance = FT_CEIL(metrics->horiAdvance); + cached->advance = metrics->horiAdvance; } else { /* Get the bounding box for non-scalable format. * Again, freetype2 fills in many of the font metrics @@ -631,7 +698,7 @@ cached->maxy = FT_FLOOR(metrics->horiBearingY); cached->miny = cached->maxy - FT_CEIL(face->available_sizes[font->font_size_family].height); cached->yoffset = 0; - cached->advance = FT_CEIL(metrics->horiAdvance); + cached->advance = metrics->horiAdvance; } /* Adjust for bold and italic text */ @@ -912,6 +979,24 @@ return retval; } +static FT_Error Find_GlyphByIndex( TTF_Font* font, Uint16 idx, int want ) +{ + int retval = 0; + int hsize = sizeof( font->cache ) / sizeof( font->cache[0] ); + + int h = idx % hsize; + font->current = &font->cache[h]; + + if (font->current->cached != idx) + Flush_Glyph( font->current ); + + if ( (font->current->stored & want) != want ) { + font->current->index = idx; + retval = Load_Glyph( font, idx, font->current, want ); + } + return retval; +} + void TTF_CloseFont( TTF_Font* font ) { if ( font ) { @@ -1158,7 +1243,7 @@ *maxy = font->current->maxy; } if ( advance ) { - *advance = font->current->advance; + *advance = FT_CEIL(font->current->advance); if ( TTF_HANDLE_STYLE_BOLD(font) ) { *advance += font->glyph_overhang; } @@ -1184,7 +1269,7 @@ return status; } -int TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h) +static int CalculateSize(TTF_Font *font, raqm_glyph_info_t *g_info, int glyph_count, int *w, int*h) { int status; int x, z; @@ -1192,69 +1277,31 @@ int miny, maxy; c_glyph *glyph; FT_Error error; - FT_Long use_kerning; FT_UInt prev_index = 0; int outline_delta = 0; - size_t textlen; - - TTF_CHECKPOINTER(text, -1); + int i; /* Initialize everything to 0 */ status = 0; minx = maxx = 0; miny = maxy = 0; - /* check kerning */ - use_kerning = FT_HAS_KERNING( font->face ) && font->kerning; - /* Init outline handling */ if ( font->outline > 0 ) { outline_delta = font->outline * 2; } /* Load each character and sum it's bounding box */ - textlen = SDL_strlen(text); x= 0; - while ( textlen > 0 ) { - Uint16 c = UTF8_getch(&text, &textlen); - if ( c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED ) { - continue; - } - - error = Find_Glyph(font, c, CACHED_METRICS); + for (i = 0; i < glyph_count; i++) { + error = Find_GlyphByIndex(font, g_info[i].index, CACHED_METRICS); if ( error ) { TTF_SetFTError("Couldn't find glyph", error); return -1; } glyph = font->current; - /* handle kerning */ - if ( use_kerning && prev_index && glyph->index ) { - FT_Vector delta; - FT_Get_Kerning( font->face, prev_index, glyph->index, ft_kerning_default, &delta ); - x += delta.x >> 6; - } - -#if 0 - if ( (ch == text) && (glyph->minx < 0) ) { - /* Fixes the texture wrapping bug when the first letter - * has a negative minx value or horibearing value. The entire - * bounding box must be adjusted to be bigger so the entire - * letter can fit without any texture corruption or wrapping. - * - * Effects: First enlarges bounding box. - * Second, xstart has to start ahead of its normal spot in the - * negative direction of the negative minx value. - * (pushes everything to the right). - * - * This will make the memory copy of the glyph bitmap data - * work out correctly. - * */ - z -= glyph->minx; - } -#endif - - z = x + glyph->minx; + z = x + FT_FLOOR(g_info[i].x_offset) + glyph->minx; if ( minx > z ) { minx = z; } @@ -1262,14 +1309,14 @@ x += font->glyph_overhang; } if ( glyph->advance > glyph->maxx ) { - z = x + glyph->advance; + z = x + FT_FLOOR(g_info[i].x_advance); } else { - z = x + glyph->maxx; + z = x + FT_FLOOR(g_info[i].x_offset) + glyph->maxx; } if ( maxx < z ) { maxx = z; } - x += glyph->advance; + x += FT_FLOOR(g_info[i].x_advance); if ( glyph->miny < miny ) { miny = glyph->miny; @@ -1277,7 +1324,7 @@ if ( glyph->maxy > maxy ) { maxy = glyph->maxy; } - prev_index = glyph->index; + prev_index = g_info[i].index; } /* Fill the bounds rectangle */ @@ -1303,6 +1350,23 @@ return status; } +int TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h) +{ + raqm_glyph_info_t *info; + size_t textlen; + int glyph_count; + + TTF_CHECKPOINTER(text, -1); + + textlen = SDL_strlen(text); + + /* Shape text */ + glyph_count = SHAPE_TEXT(text, textlen, font, &info); + + /* Calculate the size */ + return CalculateSize(font, info, glyph_count, w, h); +} + int TTF_SizeUNICODE(TTF_Font *font, const Uint16 *text, int *w, int *h) { int status = -1; @@ -1353,18 +1417,24 @@ Uint8* dst; Uint8 *dst_check; int row, col; + int i; c_glyph *glyph; - + raqm_glyph_info_t *g_info; + int glyph_count; FT_Bitmap *current; FT_Error error; - FT_Long use_kerning; FT_UInt prev_index = 0; size_t textlen; TTF_CHECKPOINTER(text, NULL); + textlen = SDL_strlen(text); + + /* Shape text */ + glyph_count = SHAPE_TEXT(text, textlen, font, &g_info); + /* Get the dimensions of the text surface */ - if ( ( TTF_SizeUTF8(font, text, &width, &height) < 0 ) || !width ) { + if ( ( CalculateSize(font, g_info, glyph_count, &width, &height) < 0 ) || !width ) { TTF_SetError( "Text has zero width" ); return NULL; } @@ -1389,26 +1459,19 @@ palette->colors[1].b = fg.b; SDL_SetColorKey( textbuf, SDL_TRUE, 0 ); - /* check kerning */ - use_kerning = FT_HAS_KERNING( font->face ) && font->kerning; - - /* Load and render each character */ - textlen = SDL_strlen(text); + /* Load and render each glyph */ first = SDL_TRUE; xstart = 0; - while ( textlen > 0 ) { - Uint16 c = UTF8_getch(&text, &textlen); - if ( c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED ) { - continue; - } - - error = Find_Glyph(font, c, CACHED_METRICS|CACHED_BITMAP); + for (i = 0; i < glyph_count; i++) { + int y_offset; + error = Find_GlyphByIndex(font, g_info[i].index, CACHED_METRICS|CACHED_BITMAP); if ( error ) { TTF_SetFTError("Couldn't find glyph", error); SDL_FreeSurface( textbuf ); return NULL; } glyph = font->current; + y_offset = glyph->yoffset - FT_FLOOR(g_info[i].y_offset); current = &glyph->bitmap; /* Ensure the width of the pixmap is correct. On some cases, * freetype may report a larger pixmap than possible.*/ @@ -1416,12 +1479,6 @@ if (font->outline <= 0 && width > glyph->maxx - glyph->minx) { width = glyph->maxx - glyph->minx; } - /* do kerning, if possible AC-Patch */ - if ( use_kerning && prev_index && glyph->index ) { - FT_Vector delta; - FT_Get_Kerning( font->face, prev_index, glyph->index, ft_kerning_default, &delta ); - xstart += delta.x >> 6; - } /* Compensate for wrap around bug with negative minx's */ if ( first && (glyph->minx < 0) ) { xstart -= glyph->minx; @@ -1431,15 +1488,15 @@ for ( row = 0; row < current->rows; ++row ) { /* Make sure we don't go either over, or under the * limit */ - if ( row+glyph->yoffset < 0 ) { + if ( row+y_offset < 0 ) { continue; } - if ( row+glyph->yoffset >= textbuf->h ) { + if ( row+y_offset >= textbuf->h ) { continue; } dst = (Uint8*) textbuf->pixels + - (row+glyph->yoffset) * textbuf->pitch + - xstart + glyph->minx; + (row+y_offset) * textbuf->pitch + + xstart + FT_FLOOR(g_info[i].x_offset) + glyph->minx; src = current->buffer + row * current->pitch; for ( col=width; col>0 && dst < dst_check; --col ) { @@ -1447,11 +1504,11 @@ } } - xstart += glyph->advance; + xstart += FT_FLOOR(g_info[i].x_advance); if ( TTF_HANDLE_STYLE_BOLD(font) ) { xstart += font->glyph_overhang; } - prev_index = glyph->index; + prev_index = g_info[i].index; } /* Handle the underline style */ @@ -1465,6 +1522,7 @@ row = TTF_strikethrough_top_row(font); TTF_drawLine_Solid(font, textbuf, row); } + free(g_info); return textbuf; } @@ -1524,6 +1582,7 @@ int xstart; int width; int height; + int i; SDL_Surface* textbuf; SDL_Palette* palette; int index; @@ -1536,15 +1595,20 @@ int row, col; FT_Bitmap* current; c_glyph *glyph; + raqm_glyph_info_t *g_info; + int glyph_count; + size_t textlen; FT_Error error; - FT_Long use_kerning; FT_UInt prev_index = 0; - size_t textlen; TTF_CHECKPOINTER(text, NULL); + textlen = SDL_strlen(text); + /* Shape text */ + glyph_count = SHAPE_TEXT(text, textlen, font, &g_info); + /* Get the dimensions of the text surface */ - if ( ( TTF_SizeUTF8(font, text, &width, &height) < 0 ) || !width ) { + if ( ( CalculateSize(font, g_info, glyph_count, &width, &height) < 0 ) || !width ) { TTF_SetError("Text has zero width"); return NULL; } @@ -1571,38 +1635,25 @@ palette->colors[index].b = bg.b + (index*bdiff) / (NUM_GRAYS-1); } - /* check kerning */ - use_kerning = FT_HAS_KERNING( font->face ) && font->kerning; - - /* Load and render each character */ - textlen = SDL_strlen(text); + /* Load and render each glyph */ first = SDL_TRUE; xstart = 0; - while ( textlen > 0 ) { - Uint16 c = UTF8_getch(&text, &textlen); - if ( c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED ) { - continue; - } - - error = Find_Glyph(font, c, CACHED_METRICS|CACHED_PIXMAP); + for (i = 0; i < glyph_count; i++) { + int y_offset; + error = Find_GlyphByIndex(font, g_info[i].index, CACHED_METRICS|CACHED_PIXMAP); if ( error ) { TTF_SetFTError("Couldn't find glyph", error); SDL_FreeSurface( textbuf ); return NULL; } glyph = font->current; + y_offset = glyph->yoffset - FT_FLOOR(g_info[i].y_offset); /* Ensure the width of the pixmap is correct. On some cases, * freetype may report a larger pixmap than possible.*/ width = glyph->pixmap.width; if (font->outline <= 0 && width > glyph->maxx - glyph->minx) { width = glyph->maxx - glyph->minx; } - /* do kerning, if possible AC-Patch */ - if ( use_kerning && prev_index && glyph->index ) { - FT_Vector delta; - FT_Get_Kerning( font->face, prev_index, glyph->index, ft_kerning_default, &delta ); - xstart += delta.x >> 6; - } /* Compensate for the wrap around with negative minx's */ if ( first && (glyph->minx < 0) ) { xstart -= glyph->minx; @@ -1613,26 +1664,26 @@ for ( row = 0; row < current->rows; ++row ) { /* Make sure we don't go either over, or under the * limit */ - if ( row+glyph->yoffset < 0 ) { + if ( row+ y_offset < 0 ) { continue; } - if ( row+glyph->yoffset >= textbuf->h ) { + if ( row+ y_offset >= textbuf->h ) { continue; } dst = (Uint8*) textbuf->pixels + - (row+glyph->yoffset) * textbuf->pitch + - xstart + glyph->minx; + (row+y_offset) * textbuf->pitch + + xstart + FT_FLOOR(g_info[i].x_offset) + glyph->minx; src = current->buffer + row * current->pitch; for ( col=width; col>0 && dst < dst_check; --col ) { *dst++ |= *src++; } } - xstart += glyph->advance; + xstart += FT_FLOOR(g_info[i].x_advance); if ( TTF_HANDLE_STYLE_BOLD(font) ) { xstart += font->glyph_overhang; } - prev_index = glyph->index; + prev_index = g_info[i].index; } /* Handle the underline style */ @@ -1646,6 +1697,7 @@ row = TTF_strikethrough_top_row(font); TTF_drawLine_Shaded(font, textbuf, row); } + free(g_info); return textbuf; } @@ -1715,15 +1767,21 @@ Uint32 *dst_check; int row, col; c_glyph *glyph; + raqm_glyph_info_t *g_info; + int glyph_count; + int i; FT_Error error; - FT_Long use_kerning; FT_UInt prev_index = 0; size_t textlen; TTF_CHECKPOINTER(text, NULL); + textlen = SDL_strlen(text); + /* Shape text */ + glyph_count = SHAPE_TEXT(text, textlen, font, &g_info); + /* Get the dimensions of the text surface */ - if ( ( TTF_SizeUTF8(font, text, &width, &height) < 0 ) || !width ) { + if ( ( CalculateSize(font, g_info, glyph_count, &width, &height) < 0 ) || !width ) { TTF_SetError("Text has zero width"); return(NULL); } @@ -1739,40 +1797,28 @@ that may occur. */ dst_check = (Uint32*)textbuf->pixels + textbuf->pitch/4 * textbuf->h; - /* check kerning */ - use_kerning = FT_HAS_KERNING( font->face ) && font->kerning; - - /* Load and render each character */ - textlen = SDL_strlen(text); + /* Load and render each glyph */ first = SDL_TRUE; xstart = 0; pixel = (fg.r<<16)|(fg.g<<8)|fg.b; SDL_FillRect(textbuf, NULL, pixel); /* Initialize with fg and 0 alpha */ - while ( textlen > 0 ) { - Uint16 c = UTF8_getch(&text, &textlen); - if ( c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED ) { - continue; - } - error = Find_Glyph(font, c, CACHED_METRICS|CACHED_PIXMAP); + for (i = 0; i < glyph_count; i++) { + int y_offset; + error = Find_GlyphByIndex(font, g_info[i].index, CACHED_METRICS|CACHED_PIXMAP); if ( error ) { TTF_SetFTError("Couldn't find glyph", error); SDL_FreeSurface( textbuf ); return NULL; } glyph = font->current; + y_offset = glyph->yoffset - FT_FLOOR(g_info[i].y_offset); /* Ensure the width of the pixmap is correct. On some cases, * freetype may report a larger pixmap than possible.*/ width = glyph->pixmap.width; if (font->outline <= 0 && width > glyph->maxx - glyph->minx) { width = glyph->maxx - glyph->minx; } - /* do kerning, if possible AC-Patch */ - if ( use_kerning && prev_index && glyph->index ) { - FT_Vector delta; - FT_Get_Kerning( font->face, prev_index, glyph->index, ft_kerning_default, &delta ); - xstart += delta.x >> 6; - } /* Compensate for the wrap around bug with negative minx's */ if ( first && (glyph->minx < 0) ) { @@ -1783,15 +1829,15 @@ for ( row = 0; row < glyph->pixmap.rows; ++row ) { /* Make sure we don't go either over, or under the * limit */ - if ( row+glyph->yoffset < 0 ) { + if ( row+y_offset < 0 ) { continue; } - if ( row+glyph->yoffset >= textbuf->h ) { + if ( row+y_offset >= textbuf->h ) { continue; } dst = (Uint32*) textbuf->pixels + - (row+glyph->yoffset) * textbuf->pitch/4 + - xstart + glyph->minx; + (row+y_offset) * textbuf->pitch/4 + + xstart + FT_FLOOR(g_info[i].x_offset) + glyph->minx; /* Added code to adjust src pointer for pixmaps to * account for pitch. @@ -1803,11 +1849,11 @@ } } - xstart += glyph->advance; + xstart += FT_FLOOR(g_info[i].x_advance); if ( TTF_HANDLE_STYLE_BOLD(font) ) { xstart += font->glyph_overhang; } - prev_index = glyph->index; + prev_index = g_info[i].index; } /* Handle the underline style */ @@ -1821,6 +1867,7 @@ row = TTF_strikethrough_top_row(font); TTF_drawLine_Blended(font, textbuf, row, pixel); } + free(g_info); return(textbuf); } @@ -1878,6 +1925,7 @@ { SDL_bool first; int xstart; + int i; int width, height; SDL_Surface *textbuf; Uint32 alpha; @@ -1887,8 +1935,9 @@ Uint32 *dst_check; int row, col; c_glyph *glyph; + raqm_glyph_info_t *g_info; + int glyph_count; FT_Error error; - FT_Long use_kerning; FT_UInt prev_index = 0; const int lineSpace = 2; int line, numLines, rowSize; @@ -1897,8 +1946,12 @@ TTF_CHECKPOINTER(text, NULL); - /* Get the dimensions of the text surface */ - if ( (TTF_SizeUTF8(font, text, &width, &height) < 0) || !width ) { + textlen = SDL_strlen(text); + /* Shape text */ + glyph_count = SHAPE_TEXT(text, textlen, font, &g_info); + + /* Get the dimensions of the text surface */ + if ( ( CalculateSize(font, g_info, glyph_count, &width, &height) < 0 ) || !width ) { TTF_SetError("Text has zero width"); return(NULL); } @@ -2002,10 +2055,7 @@ that may occur. */ dst_check = (Uint32*)textbuf->pixels + textbuf->pitch/4 * textbuf->h; - /* check kerning */ - use_kerning = FT_HAS_KERNING( font->face ) && font->kerning; - - /* Load and render each character */ + /* Load and render each glyph */ pixel = (fg.r<<16)|(fg.g<<8)|fg.b; SDL_FillRect(textbuf, NULL, pixel); /* Initialize with fg and 0 alpha */ @@ -2013,35 +2063,24 @@ if ( strLines ) { text = strLines[line]; } - textlen = SDL_strlen(text); first = SDL_TRUE; xstart = 0; - while ( textlen > 0 ) { - Uint16 c = UTF8_getch(&text, &textlen); - if ( c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED ) { - continue; - } - - error = Find_Glyph(font, c, CACHED_METRICS|CACHED_PIXMAP); + for (i = 0; i < glyph_count; i++) { + int y_offset; + error = Find_GlyphByIndex(font, g_info[i].index, CACHED_METRICS|CACHED_PIXMAP); if ( error ) { TTF_SetFTError("Couldn't find glyph", error); SDL_FreeSurface( textbuf ); return NULL; } glyph = font->current; + y_offset = glyph->yoffset - FT_FLOOR(g_info[i].y_offset); /* Ensure the width of the pixmap is correct. On some cases, * freetype may report a larger pixmap than possible.*/ width = glyph->pixmap.width; if ( font->outline <= 0 && width > glyph->maxx - glyph->minx ) { width = glyph->maxx - glyph->minx; } - /* do kerning, if possible AC-Patch */ - if ( use_kerning && prev_index && glyph->index ) { - FT_Vector delta; - FT_Get_Kerning( font->face, prev_index, glyph->index, ft_kerning_default, &delta ); - xstart += delta.x >> 6; - } - /* Compensate for the wrap around bug with negative minx's */ if ( first && (glyph->minx < 0) ) { xstart -= glyph->minx; @@ -2051,15 +2090,15 @@ for ( row = 0; row < glyph->pixmap.rows; ++row ) { /* Make sure we don't go either over, or under the * limit */ - if ( row+glyph->yoffset < 0 ) { + if ( row+y_offset < 0 ) { continue; } - if ( row+glyph->yoffset >= textbuf->h ) { + if ( row+y_offset >= textbuf->h ) { continue; } dst = ((Uint32*)textbuf->pixels + rowSize * line) + - (row+glyph->yoffset) * textbuf->pitch/4 + - xstart + glyph->minx; + (row+y_offset) * textbuf->pitch/4 + + xstart + FT_FLOOR(g_info[i].x_offset) + glyph->minx; /* Added code to adjust src pointer for pixmaps to * account for pitch. @@ -2071,11 +2110,11 @@ } } - xstart += glyph->advance; + xstart += FT_FLOOR(g_info[i].x_advance); if ( TTF_HANDLE_STYLE_BOLD(font) ) { xstart += font->glyph_overhang; } - prev_index = glyph->index; + prev_index = g_info[i].index; } /* Handle the underline style * @@ -2097,6 +2136,7 @@ SDL_free(strLines); SDL_stack_free(str); } + free(g_info); return(textbuf); } diff -r 514e61338063 configure.in --- a/configure.in Fri Jun 26 19:05:28 2015 -0700 +++ b/configure.in Tue Jan 05 14:45:46 2016 +0400 @@ -120,6 +120,26 @@ LIBS="$LIBS `$FREETYPE_CONFIG $freetypeconf_args --libs`" fi +dnl Check for the CTL_FLAGS +AC_ARG_ENABLE(ctl, + [AS_HELP_STRING([--disable-ctl], + [disable complex text layout support])], + [enable_ctl=$enableval], + [enable_ctl='yes']) + +have_ctl="" +if test "x$enable_ctl" != 'xno'; then + PKG_CHECK_MODULES(CTL, [fribidi harfbuzz], have_ctl="yes", []) +fi + +if test "x$have_ctl" == "xyes"; then + AC_DEFINE(HAVE_CTL) + CFLAGS="$CFLAGS $CTL_CFLAGS" + LIBS="$LIBS $CTL_LIBS" + AC_CHECK_FUNC(fribidi_reorder_runs, AC_DEFINE(HAVE_FRIBIDI_REORDER_RUNS), []) +fi +AM_CONDITIONAL(HAVE_CTL, test "x$have_ctl" == "xyes") + dnl Check for SDL SDL_VERSION=2.0.0 AC_SUBST(SDL_VERSION) diff -r 514e61338063 raqm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/raqm.c Tue Jan 05 14:45:46 2016 +0400 @@ -0,0 +1,766 @@ +/* + * Copyright © 2015 Information Technology Authority (ITA) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The most recent version of this code can be found in: + * https://github.com/HOST-Oman/libraqm + * + */ + +#include "raqm.h" + +/* For enabling debug mode */ +/*#define RAQM_DEBUG 1*/ +#ifdef RAQM_DEBUG +#define RAQM_DBG(...) fprintf (stderr, __VA_ARGS__) +#else +#define RAQM_DBG(...) +#endif + +#ifdef RAQM_TESTING +# define RAQM_TEST(...) printf (__VA_ARGS__) +# define SCRIPT_TO_STRING(script) \ + char buff[5]; \ + hb_tag_to_string (hb_script_to_iso15924_tag (script), buff); \ + buff[4] = '\0'; +#else +# define RAQM_TEST(...) +#endif + +/* for older fribidi versions */ +#ifndef HAVE_FRIBIDI_REORDER_RUNS +typedef struct _FriBidiRunStruct FriBidiRun; +struct _FriBidiRunStruct +{ + FriBidiRun *prev; + FriBidiRun *next; + + FriBidiStrIndex pos, len; + FriBidiCharType type; + FriBidiLevel level; +}; + +/* Reverse an array of runs */ +static void reverse_run (FriBidiRun *arr, const FriBidiStrIndex len) +{ + FriBidiStrIndex i; + + assert (arr); + + for (i = 0; i < len / 2; i++) + { + FriBidiRun temp = arr[i]; + arr[i] = arr[len - 1 - i]; + arr[len - 1 - i] = temp; + } +} + +/* Seperates and reorders runs via fribidi using bidi algorithm*/ +FriBidiStrIndex fribidi_reorder_runs ( + /* input */ + const FriBidiCharType *bidi_types, + const FriBidiStrIndex len, + const FriBidiParType base_dir, + /* input and output */ + FriBidiLevel *embedding_levels, + /* output */ + FriBidiRun *runs +) +{ + FriBidiStrIndex i; + FriBidiLevel level; + FriBidiLevel last_level = -1; + FriBidiLevel max_level = 0; + FriBidiStrIndex run_count = 0; + FriBidiStrIndex run_start = 0; + FriBidiStrIndex run_index = 0; + + if (len == 0) + { + goto out; + } + + assert (bidi_types); + assert (embedding_levels); + + /* L1. Reset the embedding levels of some chars: + 4. any sequence of white space characters at the end of the line. */ + for (i = len - 1; i >= 0 && + FRIBIDI_IS_EXPLICIT_OR_BN_OR_WS (bidi_types[i]); i--) + embedding_levels[i] = FRIBIDI_DIR_TO_LEVEL (base_dir); + + /* Find max_level of the line. We don't reuse the paragraph + * max_level, both for a cleaner API, and that the line max_level + * may be far less than paragraph max_level. */ + for (i = len - 1; i >= 0; i--) + if (embedding_levels[i] > max_level) + max_level = embedding_levels[i]; + + for (i = 0; i < len; i++) + { + if (embedding_levels[i] != last_level) + run_count++; + + last_level = embedding_levels[i]; + } + + if (runs == NULL) + goto out; + + while (run_start < len) + { + int runLength = 0; + while ((run_start + runLength) < len && embedding_levels[run_start] == embedding_levels[run_start + runLength]) + runLength++; + + runs[run_index].pos = run_start; + runs[run_index].level = embedding_levels[run_start]; + runs[run_index].len = runLength; + run_start += runLength; + run_index++; + } + + /* L2. Reorder. */ + for (level = max_level; level > 0; level--) + { + for (i = run_count - 1; i >= 0; i--) + { + if (runs[i].level >= level) + { + int end = i; + for (i--; (i >= 0 && runs[i].level >= level); i--) + ; + reverse_run (runs + i + 1, end - i); + } + } + } + +out: + + return run_count; +} + +#endif + +/* Stack to handle script detection */ +typedef struct +{ + size_t capacity; + size_t size; + int* pair_index; + hb_script_t* scripts; +} Stack; + +/* Special paired characters for script detection */ +static const FriBidiChar paired_chars[] = +{ + 0x0028, 0x0029, /* ascii paired punctuation */ + 0x003c, 0x003e, + 0x005b, 0x005d, + 0x007b, 0x007d, + 0x00ab, 0x00bb, /* guillemets */ + 0x2018, 0x2019, /* general punctuation */ + 0x201c, 0x201d, + 0x2039, 0x203a, + 0x3008, 0x3009, /* chinese paired punctuation */ + 0x300a, 0x300b, + 0x300c, 0x300d, + 0x300e, 0x300f, + 0x3010, 0x3011, + 0x3014, 0x3015, + 0x3016, 0x3017, + 0x3018, 0x3019, + 0x301a, 0x301b +}; + +typedef struct +{ + FriBidiStrIndex pos, len; + FriBidiCharType type; + FriBidiLevel level; + + hb_buffer_t* buffer; + hb_script_t script; +} Run; + +/* Stack handeling functions */ +static Stack* +stack_new (size_t max) +{ + Stack* stack; + stack = (Stack*) raqm_malloc (sizeof (Stack)); + stack->scripts = (hb_script_t*) raqm_malloc (sizeof (hb_script_t) * max); + stack->pair_index = (int*) raqm_malloc (sizeof (int) * max); + stack->size = 0; + stack->capacity = max; + return stack; +} + +static void +stack_free (Stack* stack) +{ + raqm_free (stack->scripts); + raqm_free (stack->pair_index); + raqm_free (stack); + +} + +static int +stack_pop (Stack* stack) +{ + if (stack->size == 0) + { + RAQM_DBG ("Stack is Empty\n"); + return 1; + } + else + { + stack->size--; + } + + return 0; +} + +static hb_script_t +stack_top (Stack* stack) +{ + if (stack->size == 0) + { + RAQM_DBG ("Stack is Empty\n"); + } + + return stack->scripts[stack->size]; +} + +static void +stack_push (Stack* stack, + hb_script_t script, + int pi) +{ + if (stack->size == stack->capacity) + { + RAQM_DBG ("Stack is Full\n"); + } + else + { + stack->size++; + stack->scripts[stack->size] = script; + stack->pair_index[stack->size] = pi; + } +} + +static int +get_pair_index (const FriBidiChar firbidi_ch) +{ + int lower = 0; + int upper = 33; /* paired characters array length */ + while (lower <= upper) + { + int mid = (lower + upper) / 2; + if (firbidi_ch < paired_chars[mid]) + { + upper = mid - 1; + } + else if (firbidi_ch > paired_chars[mid]) + { + lower = mid + 1; + } + else + { + return mid; + } + } + + return -1; +} + +#define STACK_IS_EMPTY(script) ((script)->size <= 0) +#define STACK_IS_NOT_EMPTY(script) (! STACK_IS_EMPTY(script)) +#define IS_OPEN(pair_index) (((pair_index) & 1) == 0) + +/* Detecting script for each character in the input string, if the character + * script is common or inherited it takes the script of the character before it + * except paired characters which we try to make them use the same script. We + * then split the BiDi runs, if necessary, on script boundaries. + */ +static int +itemize_by_script(int bidirun_count, + FriBidiRun *bidiruns, + const uint32_t* text, + int length, + Run *runs) +{ + int i; + int run_count = 0; + int last_script_index = -1; + int last_set_index = -1; + hb_script_t last_script_value; + hb_script_t* scripts = NULL; + Stack* script_stack = NULL; + + scripts = (hb_script_t*) raqm_malloc (sizeof (hb_script_t) * (size_t) length); + for (i = 0; i < length; ++i) + { + scripts[i] = hb_unicode_script (hb_unicode_funcs_get_default (), text[i]); + } + +#ifdef RAQM_TESTING + if (runs) + { + RAQM_TEST ("Before script detection:\n"); + for (i = 0; i < length; ++i) + { + SCRIPT_TO_STRING (scripts[i]); + RAQM_TEST ("script for ch[%d]\t%s\n", i, buff); + } + RAQM_TEST ("\n"); + } +#endif + + script_stack = stack_new ((size_t) length); + for (i = 0; i < length; ++i) + { + if (scripts[i] == HB_SCRIPT_COMMON && last_script_index != -1) + { + int pair_index = get_pair_index (text[i]); + if (pair_index >= 0) + { /* is a paired character */ + if (IS_OPEN (pair_index)) + { + scripts[i] = last_script_value; + last_set_index = i; + stack_push (script_stack, scripts[i], pair_index); + } + else + { /* is a close paired character */ + int pi = pair_index & ~1; /* find matching opening (by getting the last even index for currnt odd index)*/ + while (STACK_IS_NOT_EMPTY (script_stack) && + script_stack->pair_index[script_stack->size] != pi) + { + stack_pop (script_stack); + } + if (STACK_IS_NOT_EMPTY (script_stack)) + { + scripts[i] = stack_top (script_stack); + last_script_value = scripts[i]; + last_set_index = i; + } + else + { + scripts[i] = last_script_value; + last_set_index = i; + } + } + } + else + { + scripts[i] = last_script_value; + last_set_index = i; + } + } + else if (scripts[i] == HB_SCRIPT_INHERITED && last_script_index != -1) + { + scripts[i] = last_script_value; + last_set_index = i; + } + else + { + int j; + for (j = last_set_index + 1; j < i; ++j) + { + scripts[j] = scripts[i]; + } + last_script_value = scripts[i]; + last_script_index = i; + last_set_index = i; + } + } + +#ifdef RAQM_TESTING + if (runs) + { + RAQM_TEST ("After script detection:\n"); + for (i = 0; i < length; ++i) + { + SCRIPT_TO_STRING (scripts[i]); + RAQM_TEST ("script for ch[%d]\t%s\n", i, buff); + } + RAQM_TEST ("\n"); + + RAQM_TEST ("Number of runs before script itemization: %d\n", bidirun_count); + RAQM_TEST ("\n"); + RAQM_TEST ("Fribidi Runs:\n"); + for (i = 0; i < bidirun_count; ++i) + { + RAQM_TEST ("run[%d]:\t start: %d\tlength: %d\tlevel: %d\n", + i, bidiruns[i].pos, bidiruns[i].len, bidiruns[i].level); + } + RAQM_TEST ("\n"); + } +#endif + + + /* To get number of runs after script seperation */ + for (i = 0; i < bidirun_count; i++) + { + int j; + FriBidiRun run = bidiruns[i]; + hb_script_t last_script = scripts[run.pos]; + run_count++; + for (j = 0; j < run.len; j++) { + hb_script_t script = scripts[run.pos + j]; + if (script != last_script) + { + run_count++; + } + last_script = script; + } + } + + if (runs == NULL) + { + goto out; + } + + run_count = 0; + + /* By iterating through bidiruns, any detection of different scripts in the same run + * will split the run so that each run contains one script. Runs with odd levels but + * different scripts will need to be reordered to appear in the correct visual order */ + for (i = 0; i < bidirun_count; i++) + { + int j; + FriBidiRun bidirun = bidiruns[i]; + + runs[run_count].level = bidirun.level; + runs[run_count].len = 0; + + if (!FRIBIDI_LEVEL_IS_RTL (bidirun.level)) + { + runs[run_count].pos = bidirun.pos; + runs[run_count].script = scripts[bidirun.pos]; + for (j = 0; j < bidirun.len; j++) + { + hb_script_t script = scripts[bidirun.pos + j]; + if (script == runs[run_count].script) + { + runs[run_count].len++; + } + else + { + run_count++; + runs[run_count].pos = bidirun.pos + j; + runs[run_count].level = bidirun.level; + runs[run_count].script = script; + runs[run_count].len = 1; + } + } + } + else + { + runs[run_count].pos = bidirun.pos + bidirun.len -1; + runs[run_count].script = scripts[bidirun.pos + bidirun.len - 1]; + for (j = bidirun.len - 1; j >= 0; j--) + { + hb_script_t script = scripts[bidirun.pos + j]; + if (script == runs[run_count].script) + { + runs[run_count].len++; + runs[run_count].pos = bidirun.pos + j; + } + else + { + run_count++; + runs[run_count].pos = bidirun.pos + j; + runs[run_count].level = bidirun.level; + runs[run_count].script = script; + runs[run_count].len = 1; + } + } + } + run_count++; + } + +#ifdef RAQM_TESTING + RAQM_TEST ("Number of runs after script itemization: %d\n", run_count); + RAQM_TEST ("\n"); + RAQM_TEST ("Final Runs:\n"); + for (i = 0; i < run_count; ++i) + { + SCRIPT_TO_STRING (runs[i].script); + RAQM_TEST ("run[%d]:\t start: %d\tlength: %d\tlevel: %d\tscript: %s\n", + i, runs[i].pos, runs[i].len, runs[i].level, buff); + } + RAQM_TEST ("\n"); +#endif + +out: + + stack_free(script_stack); + raqm_free (scripts); + return run_count; +} + +/* Does the shaping for each run buffer */ +static void +harfbuzz_shape (const FriBidiChar* unicode_str, + FriBidiStrIndex length, + hb_font_t* hb_font, + const char** featurelist, + Run* run) +{ + run->buffer = hb_buffer_create (); + + /* adding text to current buffer */ + hb_buffer_add_utf32 (run->buffer, unicode_str, length, (unsigned int)(run->pos), run->len); + + /* setting script of current buffer */ + hb_buffer_set_script (run->buffer, run->script); + + /* setting language of current buffer */ + hb_buffer_set_language (run->buffer, hb_language_get_default ()); + + /* setting direction of current buffer */ + if (FRIBIDI_LEVEL_IS_RTL (run->level)) + { + hb_buffer_set_direction (run->buffer, HB_DIRECTION_RTL); + } + else + { + hb_buffer_set_direction (run->buffer, HB_DIRECTION_LTR); + } + + /* shaping current buffer */ + if (featurelist) + { + unsigned int count = 0; + const char** p; + hb_feature_t* features = NULL; + + for (p = featurelist; *p; p++) + { + count++; + } + + features = (hb_feature_t *) malloc (sizeof (hb_feature_t) * count); + + count = 0; + for (p = featurelist; *p; p++) + { + hb_bool_t success = hb_feature_from_string (*p, -1, &features[count]); + if (success) + { + count++; + } + } + hb_shape_full (hb_font, run->buffer, features, count, NULL); + } + else + { + hb_shape (hb_font, run->buffer, NULL, 0); + } +} + +/* convert index from UTF-32 to UTF-8 */ +static uint32_t +u32_index_to_u8 (FriBidiChar* unicode, + uint32_t index) +{ + FriBidiStrIndex length; + char* output = (char*) raqm_malloc ((size_t)((index * 4) + 1)); + + length = fribidi_unicode_to_charset (FRIBIDI_CHAR_SET_UTF8, unicode, (FriBidiStrIndex)(index), output); + + raqm_free (output); + return (uint32_t)(length); +} + +/* Takes the input text and does the reordering and shaping */ +unsigned int +raqm_shape (const char* u8_str, + int u8_size, + const FT_Face face, + raqm_direction_t direction, + const char **features, + raqm_glyph_info_t** glyph_info) +{ + FriBidiChar* u32_str; + FriBidiStrIndex u32_size; + raqm_glyph_info_t* info; + unsigned int glyph_count; + unsigned int i; + + RAQM_TEST ("Text is: %s\n", u8_str); + + u32_str = (FriBidiChar*) raqm_calloc (sizeof (FriBidiChar), (size_t)(u8_size)); + u32_size = fribidi_charset_to_unicode (FRIBIDI_CHAR_SET_UTF8, u8_str, u8_size, u32_str); + + glyph_count = raqm_shape_u32 (u32_str, u32_size, face, direction, features, &info); + +#ifdef RAQM_TESTING + RAQM_TEST ("\nUTF-32 clusters:"); + for (i = 0; i < glyph_count; i++) + { + RAQM_TEST (" %02d", info[i].cluster); + } + RAQM_TEST ("\n"); +#endif + + for (i = 0; i < glyph_count; i++) + { + info[i].cluster = u32_index_to_u8 (u32_str, info[i].cluster); + } + +#ifdef RAQM_TESTING + RAQM_TEST ("UTF-8 clusters: "); + for (i = 0; i < glyph_count; i++) + { + RAQM_TEST (" %02d", info[i].cluster); + } + RAQM_TEST ("\n"); +#endif + + raqm_free (u32_str); + *glyph_info = info; + return glyph_count; +} + +/* Takes a utf-32 input text and does the reordering and shaping */ +unsigned int +raqm_shape_u32 (const uint32_t* text, + int length, + const FT_Face face, + raqm_direction_t direction, + const char **features, + raqm_glyph_info_t** glyph_info) +{ + int i = 0; + unsigned int index = 0; + int run_count; + int bidirun_count; + unsigned int total_glyph_count = 0; + unsigned int glyph_count; + unsigned int postion_length; + int max_level; + + hb_font_t* hb_font = NULL; + hb_glyph_info_t* hb_glyph_info = NULL; + hb_glyph_position_t* hb_glyph_position = NULL; + FriBidiParType par_type; + FriBidiRun* bidiruns = NULL; + FriBidiCharType* types = NULL; + FriBidiLevel* levels = NULL; + Run* runs = NULL; + raqm_glyph_info_t* info = NULL; + + types = (FriBidiCharType*) raqm_malloc (sizeof (FriBidiCharType) * (size_t)(length)); + levels = (FriBidiLevel*) raqm_malloc (sizeof (FriBidiLevel) * (size_t)(length)); + fribidi_get_bidi_types (text, length, types); + + par_type = FRIBIDI_PAR_ON; + if (direction == RAQM_DIRECTION_RTL) + { + par_type = FRIBIDI_PAR_RTL; + } + else if (direction == RAQM_DIRECTION_LTR) + { + par_type = FRIBIDI_PAR_LTR; + } + +#ifdef RAQM_TESTING + switch (direction) + { + case RAQM_DIRECTION_RTL: + RAQM_TEST ("Direction is: RTL\n\n"); + break; + case RAQM_DIRECTION_LTR: + RAQM_TEST ("Direction is: LTR\n\n"); + break; + case RAQM_DIRECTION_DEFAULT: + RAQM_TEST ("Direction is: DEFAULT\n\n"); + break; + } +#endif + + max_level = fribidi_get_par_embedding_levels (types, length, &par_type, levels); + if (max_level <= 0) + goto out; + + /* to get number of bidi runs */ + bidirun_count = fribidi_reorder_runs (types, length, par_type, levels, NULL); + bidiruns = (FriBidiRun*) raqm_malloc (sizeof (FriBidiRun) * (size_t)(bidirun_count)); + + /* to populate bidi run array */ + bidirun_count = fribidi_reorder_runs (types, length, par_type, levels, bidiruns); + + /* to get number of runs after script seperation */ + run_count = itemize_by_script (bidirun_count, bidiruns, text, length, NULL); + runs = (Run*) raqm_malloc (sizeof (Run) * (size_t)(run_count)); + + /* to populate runs_scripts array */ + itemize_by_script (bidirun_count, bidiruns, text, length, runs); + + /* harfbuzz shaping */ + hb_font = hb_ft_font_create (face, NULL); + + for (i = 0; i < run_count; i++) + { + harfbuzz_shape (text, length, hb_font, features, &runs[i]); + hb_glyph_info = hb_buffer_get_glyph_infos (runs[i].buffer, &glyph_count); + total_glyph_count += glyph_count; + } + + info = (raqm_glyph_info_t*) raqm_malloc (sizeof (raqm_glyph_info_t) * (total_glyph_count)); + + RAQM_TEST ("Glyph information:\n"); + + for (i = 0; i < run_count; i++) + { + unsigned int j; + hb_glyph_info = hb_buffer_get_glyph_infos (runs[i].buffer, &glyph_count); + hb_glyph_position = hb_buffer_get_glyph_positions (runs[i].buffer, &postion_length); + + for (j = 0; j < glyph_count; j++) + { + info[index].index = hb_glyph_info[j].codepoint; + info[index].x_offset = hb_glyph_position[j].x_offset; + info[index].y_offset = hb_glyph_position[j].y_offset; + info[index].x_advance = hb_glyph_position[j].x_advance; + info[index].cluster = hb_glyph_info[j].cluster; + RAQM_TEST ("glyph [%d]\tx_offset: %d\ty_offset: %d\tx_advance: %d\n", + info[index].index, info[index].x_offset, + info[index].y_offset, info[index].x_advance); + index++; + } + hb_buffer_destroy (runs[i].buffer); + } + +out: + + *glyph_info = info; + + hb_font_destroy (hb_font); + raqm_free (levels); + raqm_free (types); + raqm_free (bidiruns); + raqm_free (runs); + + return total_glyph_count; +} diff -r 514e61338063 raqm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/raqm.h Tue Jan 05 14:45:46 2016 +0400 @@ -0,0 +1,91 @@ +/* + * Copyright © 2015 Information Technology Authority (ITA) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * The most recent version of this code can be found in: + * https://github.com/HOST-Oman/libraqm + * + */ + +#ifndef _RAQM_H_ +#define _RAQM_H_ + +#include +#include +#include +#include +#include + +#ifndef raqm_free +# define raqm_free(X) free(X) +#endif +#ifndef raqm_malloc +# define raqm_malloc(X) malloc(X) +#endif +#ifndef raqm_calloc +# define raqm_calloc(X, Y) calloc(X, Y) +#endif + +/* Output glyph */ +typedef struct +{ + unsigned int index; /* Glyph index */ + int x_offset; /* Horizontal glyph offset */ + int x_advance; /* Horizontal glyph advance width */ + int y_offset; /* Vertical glyph offset */ + uint32_t cluster; /* Index of original character in input text */ +} raqm_glyph_info_t; + +/* Base paragraph direction */ +typedef enum +{ + RAQM_DIRECTION_DEFAULT, /* Automatic detection */ + RAQM_DIRECTION_RTL, /* Right-to-left paragraph */ + RAQM_DIRECTION_LTR /* Left-to-right paragraph */ +} raqm_direction_t; + +/* raqm_shape - apply bidi algorithm and shape text. + * + * This function reorders and shapes the text using FriBiDi and HarfBuzz. + * It supports proper script detection for each character of the input string. + * If the character script is common or inherited it takes the script of the + * character before it except some special paired characters. + * + * Returns: number of glyphs. + */ +unsigned int +raqm_shape (const char* text, /* input text, UTF-8 encoded */ + int length, /* length of text array */ + const FT_Face face, /* font to use for shaping */ + raqm_direction_t direction, /* base paragraph direction */ + const char **features, /* a NULL-terminated array of font feature strings */ + raqm_glyph_info_t** glyph_info /* output glyph info, should be freed by the client */ + ); + +unsigned int +raqm_shape_u32 (const uint32_t* text, /* input text, UTF-32 encoded */ + int length, /* length of text array */ + const FT_Face face, /* font to use for shaping */ + raqm_direction_t direction, /* base paragraph direction */ + const char **features, /* a NULL-terminated array of font feature strings */ + raqm_glyph_info_t** glyph_info /* output glyph info, should be freed by the client */ + ); + +#endif /* _RAQM_H_ */