#include #include #include #include #if !defined WIN32 //#include // for bool type when compiling with gcc #include #include #endif #include #include FT_FREETYPE_H #include FT_GLYPH_H // for some reason this file isn't included by default on Win32 or Linux #include FT_ADVANCES_H typedef enum { TL_OK=0, TL_FreetypeInitFailed, TL_FontOpenFailed, TL_FontUnknownFormat, TL_FontIndexOutOfRange } TL_Result; typedef struct { unsigned int glyph; float posX; float posY; int font; int size; } TL_GlyphPosition; typedef struct { int width; int numGlyphs; TL_GlyphPosition *glyphPositions; } TL_TextLayout; #if defined WIN32 // MSVC doesn't know about isblank(), provide an implementation here: int isblank(char c) { if(c == ' ' || c == '\t') { return 1; } return 0; } int iswblank(wchar_t c) { if(c == L' ' || c == L'\t') { return 1; } return 0; } #endif FT_Library library; typedef struct { FT_Face face; int defaultSize; /// This is the default size to be used with the given font in the absence of explicit sizing int currentSize; /// Freetype is stateful WRT font size; this is the currently active size } TL_Font; struct { TL_Font *fonts; int numFonts; } TL_Globals; TL_Result TL_Init() { TL_Globals.fonts=0; TL_Globals.numFonts=0; if(FT_Init_FreeType(&library)) { return TL_FreetypeInitFailed; } int majorVersion, minorVersion, patchVersion; FT_Library_Version(library,&majorVersion,&minorVersion,&patchVersion); printf("Freetype library version: %d.%d.%d\n",majorVersion,minorVersion,patchVersion); return TL_OK; } TL_Result TL_LoadFont(const char *filename, int *font) { TL_Font tlFont; int i; int error; tlFont.currentSize = -1; error = FT_New_Face(library, filename, 0, &(tlFont.face)); if(error == FT_Err_Unknown_File_Format) { fprintf(stderr, "font file format not supported\n"); return TL_FontUnknownFormat; } else if(error != 0) { fprintf(stderr, "unknown error while opening font - is filename correct?\n"); return TL_FontOpenFailed; } TL_Globals.fonts = (TL_Font*)realloc(TL_Globals.fonts, (TL_Globals.numFonts+ 1) * sizeof(TL_Font)); TL_Globals.fonts[TL_Globals.numFonts] = tlFont; *font = TL_Globals.numFonts; TL_Globals.numFonts++; return TL_OK; } void _TL_ActivateFontSize(int font, int size) { if(TL_Globals.fonts[font].currentSize == size) { return; } FT_Set_Pixel_Sizes(TL_Globals.fonts[font].face, 0, size); TL_Globals.fonts[font].currentSize = size; } TL_Result TL_SetFontSize(int font, int size) { if(font>=TL_Globals.numFonts) { return TL_FontIndexOutOfRange; } _TL_ActivateFontSize(font,size); TL_Globals.fonts[font].defaultSize=size; return TL_OK; } int _TL_CalculateRunWidth(TL_GlyphPosition *glyphs, int length, bool firstInLine) { int width=0; FT_Vector kerning; FT_Fixed advance; int i; for(i=0;isize->metrics.x_scale); width+=advance; // check whether this is the first character in the string/line if((i==0 && !firstInLine) || i!=0) { // respect proper kerning (if possible, i.e. both glyphs are from the same font and size) if(FT_HAS_KERNING(TL_Globals.fonts[glyphs[i].font].face) && glyphs[i-1].font==glyphs[i].font && glyphs[i-1].size==glyphs[i].size) { // Note: glyphs[i-1] may point beyond start of passed glyphs array, // but it's only a section of a larger string, so this unchecked i-1 is fine FT_Get_Kerning(TL_Globals.fonts[glyphs[i].font].face,glyphs[i-1].glyph,glyphs[i].glyph,FT_KERNING_UNFITTED,&kerning); width+=kerning.x; } } } return width; } void _TL_LayoutParagraph(const wchar_t *text, TL_GlyphPosition *glyphs, int length, int width) { int i; int currentPos=0; int currentX=0; bool firstInLine=true; int lineStart=0; if(length==0) { return; } do { // determine range of characters for next word i=currentPos; while(iswblank(text[i]) && i width) { // wrap onto new line currentX = 0; // recalculate width of word without leading whitespace wordWidth =_TL_CalculateRunWidth(glyphs + wordStart, wordEnd - wordStart, true); lineStart = wordStart; } // position glyphs of this word on the right line for(i=currentPos;i= TL_Globals.numFonts) { return NULL; } int length = wcslen(text); width = width * 64; // Freetype coordinates are in 1/64 pixels // translate characters into font glyphs TL_GlyphPosition *glyphs=(TL_GlyphPosition*)malloc(length*sizeof(TL_GlyphPosition)); for(i=0;iwidth=width/64; layout->numGlyphs=length; layout->glyphPositions=glyphs; return layout; } int main(int argc, char *argv[]) { TL_Init(); int fontId; TL_LoadFont("Cantarell-Regular.ttf", &fontId); TL_SetFontSize(fontId, 20); TL_TextLayout *layout = TL_LayoutText(fontId, L"Test", 1000); for(int i=0; i < layout->numGlyphs; i++) { printf("glyph %d: position (%f, %f)\n", i, layout->glyphPositions[i].posX, layout->glyphPositions[i].posY); } #if defined WIN32 system("pause"); // hack to keep terminal window open in Windows #endif return 0; }