bool Font::LoadFile(const char *file, unsigned ptsize, bool bold, bool italic) { assert(IsScreenInitialized()); Destroy(); FT_Face new_face = FreeType::Load(file); if (new_face == nullptr) return false; FT_Error error = ::FT_Set_Pixel_Sizes(new_face, 0, ptsize); if (error) { ::FT_Done_Face(new_face); return false; } const FT_Fixed y_scale = new_face->size->metrics.y_scale; height = FT_CEIL(FT_MulFix(new_face->height, y_scale)); ascent_height = FT_CEIL(FT_MulFix(new_face->ascender, y_scale)); capital_height = ::GetCapitalHeight(new_face); if (capital_height == 0) capital_height = height; // TODO: handle bold/italic face = new_face; return true; }
static c_glyph* LoadGlyph(TTF_Font* font, Uint8 ch) { c_glyph* cached = &font->cache[ch]; if (cached->pixmap.buffer) // If already cached return cached; FT_Face face = font->face; if (FT_Load_Glyph(face, FT_Get_Char_Index(face, cpMap[ch]), FT_LOAD_NO_AUTOHINT)) return 0; FT_GlyphSlot glyph = face->glyph; FT_Glyph_Metrics* metrics = &glyph->metrics; cached->minx = FT_FLOOR(metrics->horiBearingX); cached->maxx = FT_CEIL(metrics->horiBearingX+metrics->width); cached->maxy = FT_FLOOR(metrics->horiBearingY); cached->miny = cached->maxy-FT_CEIL(metrics->height); cached->yoffset = font->ascent-cached->maxy; if (FT_Render_Glyph(glyph, FT_RENDER_MODE_NORMAL)) // Render the glyph return 0; FT_Bitmap* src = &glyph->bitmap; // Copy information to cache FT_Bitmap* dst = &cached->pixmap; memcpy(dst, src, sizeof(*dst)); if (dst->rows != 0) { int len = dst->pitch*dst->rows; dst->buffer = (unsigned char *)malloc(len); if (!dst->buffer) return 0; memcpy(dst->buffer, src->buffer, len); } return cached; }
void TTF_SetCharSize(TTF_Font* font, int ptsize) { TTF_Flush_Cache(font); FT_Face face = font->face; if (FT_Set_Char_Size(face, 0, ptsize*64, 0, 0)) // Set the character size and use default DPI (72) E_Exit("TTF: Couldn't set font size"); // Get the scalable font metrics for this font FT_Fixed scale = face->size->metrics.y_scale; font->ascent = FT_CEIL(FT_MulFix(face->ascender, scale)); font->descent = FT_CEIL(FT_MulFix(face->descender, scale)); font->height = font->ascent-font->descent; font->underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale)); font->underline_height = FT_FLOOR(FT_MulFix(face->underline_thickness, scale)); if (font->underline_height < 1) font->underline_height = 1; font->width = FT_FLOOR(FT_MulFix(face->max_advance_width, face->size->metrics.x_scale)); }
static void FixGlyph( FT_Glyph glyph, FT_BBox *p_bbox, FT_Pos i_x_advance, FT_Pos i_y_advance, const FT_Vector *p_pen ) { FT_BitmapGlyph glyph_bmp = (FT_BitmapGlyph)glyph; if( p_bbox->xMin >= p_bbox->xMax ) { p_bbox->xMin = FT_CEIL(p_pen->x); p_bbox->xMax = FT_CEIL(p_pen->x + i_x_advance); glyph_bmp->left = p_bbox->xMin; } if( p_bbox->yMin >= p_bbox->yMax ) { p_bbox->yMax = FT_CEIL(p_pen->y); p_bbox->yMin = FT_CEIL(p_pen->y + i_y_advance); glyph_bmp->top = p_bbox->yMax; } }
bool Font::LoadFile(const char *file, UPixelScalar ptsize, bool bold, bool italic) { assert(IsScreenInitialized()); Destroy(); FT_Face new_face = FreeType::Load(file); if (new_face == nullptr) return false; if (ptsize>1000) { ptsize-=1000; demibold=true; } else demibold=false; // Paolo: in order to get back desired ppts we must ask for 62, not 64 FT_Error error = ::FT_Set_Char_Size(new_face, 0, ptsize<<6, 0, 62); if (error) { ::FT_Done_Face(new_face); return false; } const FT_Fixed y_scale = new_face->size->metrics.y_scale; height = FT_CEIL(FT_MulFix(new_face->height, y_scale)); ascent_height = FT_CEIL(FT_MulFix(new_face->ascender, y_scale)); capital_height = ::GetCapitalHeight(new_face); if (capital_height == 0) capital_height = height; face = new_face; return true; }
gcc_pure static unsigned GetCapitalHeight(FT_Face face) { #ifndef ENABLE_OPENGL const ScopeLock protect(freetype_mutex); #endif FT_UInt i = FT_Get_Char_Index(face, 'M'); if (i == 0) return 0; FT_Error error = FT_Load_Glyph(face, i, load_flags); if (error) return 0; return FT_CEIL(face->glyph->metrics.height); }
static FT_Error Load_Glyph(TTF_Font * font, Uint16 ch, c_glyph * cached, int want) { FT_Face face; FT_Error error; FT_GlyphSlot glyph; FT_Glyph_Metrics *metrics; FT_Outline *outline; if(!font || !font->face) { return FT_Err_Invalid_Handle; } face = font->face; /* Load the glyph */ if(!cached->index) { cached->index = FT_Get_Char_Index(face, ch); } error = FT_Load_Glyph(face, cached->index, FT_LOAD_DEFAULT); if(error) { return error; } /* Get our glyph shortcuts */ glyph = face->glyph; metrics = &glyph->metrics; outline = &glyph->outline; /* Get the glyph metrics if desired */ if((want & CACHED_METRICS) && !(cached->stored & CACHED_METRICS)) { if(FT_IS_SCALABLE(face)) { /* Get the bounding box */ cached->minx = FT_FLOOR(metrics->horiBearingX); cached->maxx = cached->minx + FT_CEIL(metrics->width); 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); } else { /* Get the bounding box for non-scalable format. * Again, freetype2 fills in many of the font metrics * with the value of 0, so some of the values we * need must be calculated differently with certain * assumptions about non-scalable formats. * */ cached->minx = FT_FLOOR(metrics->horiBearingX); cached->maxx = cached->minx + FT_CEIL(metrics->horiAdvance); 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); } /* Adjust for bold and italic text */ if(font->style & TTF_STYLE_BOLD) { cached->maxx += font->glyph_overhang; } if(font->style & TTF_STYLE_ITALIC) { cached->maxx += (int) ceil(font->glyph_italics); } cached->stored |= CACHED_METRICS; } if(((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) || ((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP))) { int mono = (want & CACHED_BITMAP); int i; FT_Bitmap *src; FT_Bitmap *dst; /* Handle the italic style */ if(font->style & TTF_STYLE_ITALIC) { FT_Matrix shear; shear.xx = 1 << 16; shear.xy = (int) (font->glyph_italics * (1 << 16)) / font->height; shear.yx = 0; shear.yy = 1 << 16; FT_Outline_Transform(outline, &shear); } /* Render the glyph */ if(mono) { error = FT_Render_Glyph(glyph, ft_render_mode_mono); } else { error = FT_Render_Glyph(glyph, ft_render_mode_normal); } if(error) { return error; } /* Copy over information to cache */ src = &glyph->bitmap; if(mono) { dst = &cached->bitmap; } else { dst = &cached->pixmap; } memcpy(dst, src, sizeof(*dst)); /* FT_Render_Glyph() and .fon fonts always generate a * two-color (black and white) glyphslot surface, even * when rendered in ft_render_mode_normal. This is probably * a freetype2 bug because it is inconsistent with the * freetype2 documentation under FT_Render_Mode section. * */ if(mono || !FT_IS_SCALABLE(face)) { dst->pitch *= 8; } /* Adjust for bold and italic text */ if(font->style & TTF_STYLE_BOLD) { int bump = font->glyph_overhang; dst->pitch += bump; dst->width += bump; } if(font->style & TTF_STYLE_ITALIC) { int bump = (int) ceil(font->glyph_italics); dst->pitch += bump; dst->width += bump; } if(dst->rows != 0) { dst->buffer = (unsigned char *) malloc(dst->pitch * dst->rows); if(!dst->buffer) { return FT_Err_Out_Of_Memory; } memset(dst->buffer, 0, dst->pitch * dst->rows); for(i = 0; i < src->rows; i++) { int soffset = i * src->pitch; int doffset = i * dst->pitch; if(mono) { unsigned char *srcp = src->buffer + soffset; unsigned char *dstp = dst->buffer + doffset; int j; for(j = 0; j < src->width; j += 8) { unsigned char ch = *srcp++; *dstp++ = (ch & 0x80) >> 7; ch <<= 1; *dstp++ = (ch & 0x80) >> 7; ch <<= 1; *dstp++ = (ch & 0x80) >> 7; ch <<= 1; *dstp++ = (ch & 0x80) >> 7; ch <<= 1; *dstp++ = (ch & 0x80) >> 7; ch <<= 1; *dstp++ = (ch & 0x80) >> 7; ch <<= 1; *dstp++ = (ch & 0x80) >> 7; ch <<= 1; *dstp++ = (ch & 0x80) >> 7; } } else if(!FT_IS_SCALABLE(face)) { /* This special case wouldn't * be here if the FT_Render_Glyph() * function wasn't buggy when it tried * to render a .fon font with 256 * shades of gray. Instead, it * returns a black and white surface * and we have to translate it back * to a 256 gray shaded surface. * */ unsigned char *srcp = src->buffer + soffset; unsigned char *dstp = dst->buffer + doffset; unsigned char ch; int j, k; for(j = 0; j < src->width; j += 8) { ch = *srcp++; for(k = 0; k < 8; ++k) { if((ch & 0x80) >> 7) { *dstp++ = NUM_GRAYS - 1; } else { *dstp++ = 0x00; } ch <<= 1; } }
TTF_Font * TTF_OpenFontIndexRW(SDL_RWops * src, int freesrc, int ptsize, long index) { TTF_Font *font; FT_Error error; FT_Face face; FT_Fixed scale; FT_Stream stream; int position; if(!TTF_initialized) { TTF_SetError("Library not initialized"); return NULL; } /* Check to make sure we can seek in this stream */ position = SDL_RWtell(src); if(position < 0) { TTF_SetError("Can't seek in stream"); return NULL; } font = (TTF_Font *) malloc(sizeof *font); if(font == NULL) { TTF_SetError("Out of memory"); return NULL; } memset(font, 0, sizeof(*font)); font->src = src; font->freesrc = freesrc; stream = (FT_Stream) malloc(sizeof(*stream)); if(stream == NULL) { TTF_SetError("Out of memory"); TTF_CloseFont(font); return NULL; } memset(stream, 0, sizeof(*stream)); /* 090303 Chase - Newer FT2 version sets this internally so we don't have to. (Can't anyway, Don't have a definition for FT_Library) */ // stream->memory = library->memory; stream->read = RWread; stream->descriptor.pointer = src; stream->pos = (unsigned long) position; SDL_RWseek(src, 0, SEEK_END); stream->size = (unsigned long) (SDL_RWtell(src) - position); SDL_RWseek(src, position, SEEK_SET); font->args.flags = FT_OPEN_STREAM; font->args.stream = stream; error = FT_Open_Face(library, &font->args, index, &font->face); if(error) { TTF_SetFTError("Couldn't load font file", error); TTF_CloseFont(font); return NULL; } face = font->face; /* Make sure that our font face is scalable (global metrics) */ if(FT_IS_SCALABLE(face)) { /* Set the character size and use default DPI (72) */ error = FT_Set_Char_Size(font->face, 0, ptsize * 64, 0, 0); if(error) { TTF_SetFTError("Couldn't set font size", error); TTF_CloseFont(font); return NULL; } /* Get the scalable font metrics for this font */ scale = face->size->metrics.y_scale; font->ascent = FT_CEIL(FT_MulFix(face->bbox.yMax, scale)); font->descent = FT_CEIL(FT_MulFix(face->bbox.yMin, scale)); font->height = font->ascent - font->descent + /* baseline */ 1; font->lineskip = FT_CEIL(FT_MulFix(face->height, scale)); font->underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale)); font->underline_height = FT_FLOOR(FT_MulFix(face->underline_thickness, scale)); } else { /* Non-scalable font case. ptsize determines which family * or series of fonts to grab from the non-scalable format. * It is not the point size of the font. * */ if(ptsize >= font->face->num_fixed_sizes) ptsize = font->face->num_fixed_sizes - 1; font->font_size_family = ptsize; error = FT_Set_Pixel_Sizes(face, face->available_sizes[ptsize].height, face->available_sizes[ptsize].width); /* With non-scalale fonts, Freetype2 likes to fill many of the * font metrics with the value of 0. The size of the * non-scalable fonts must be determined differently * or sometimes cannot be determined. * */ font->ascent = face->available_sizes[ptsize].height; font->descent = 0; font->height = face->available_sizes[ptsize].height; font->lineskip = FT_CEIL(font->ascent); font->underline_offset = FT_FLOOR(face->underline_position); font->underline_height = FT_FLOOR(face->underline_thickness); } if(font->underline_height < 1) { font->underline_height = 1; } #ifdef DEBUG_FONTS printf("Font metrics:\n"); printf("\tascent = %d, descent = %d\n", font->ascent, font->descent); printf("\theight = %d, lineskip = %d\n", font->height, font->lineskip); printf("\tunderline_offset = %d, underline_height = %d\n", font->underline_offset, font->underline_height); #endif /* Set the default font style */ font->style = TTF_STYLE_NORMAL; font->glyph_overhang = face->size->metrics.y_ppem / 10; /* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */ font->glyph_italics = 0.207f; font->glyph_italics *= font->height; return font; }
static FT_Error Load_Glyph( TTF_Font* font, Uint16 ch, c_glyph* cached, int want ) { FT_Face face; FT_Error error; FT_GlyphSlot glyph; FT_Glyph_Metrics* metrics; FT_Outline* outline; if ( !font || !font->face ) { return FT_Err_Invalid_Handle; } face = font->face; /* Load the glyph */ if ( ! cached->index ) { cached->index = FT_Get_Char_Index( face, ch ); } error = FT_Load_Glyph( face, cached->index, FT_LOAD_DEFAULT ); if( error ) { return error; } /* Get our glyph shortcuts */ glyph = face->glyph; metrics = &glyph->metrics; outline = &glyph->outline; /* Get the glyph metrics if desired */ if ( (want & CACHED_METRICS) && !(cached->stored & CACHED_METRICS) ) { if ( FT_IS_SCALABLE( face ) ) { /* Get the bounding box */ cached->minx = FT_FLOOR(metrics->horiBearingX); cached->maxx = cached->minx + FT_CEIL(metrics->width); 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); } else { /* Get the bounding box for non-scalable format. * Again, freetype2 fills in many of the font metrics * with the value of 0, so some of the values we * need must be calculated differently with certain * assumptions about non-scalable formats. * */ cached->minx = FT_FLOOR(metrics->horiBearingX); cached->maxx = cached->minx + FT_CEIL(metrics->horiAdvance); 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); } /* Adjust for bold and italic text */ if( font->style & TTF_STYLE_BOLD ) { cached->maxx += font->glyph_overhang; } if( font->style & TTF_STYLE_ITALIC ) { cached->maxx += (int)ceil(font->glyph_italics); } cached->stored |= CACHED_METRICS; } if ( ((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) || ((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP)) ) { int mono = (want & CACHED_BITMAP); int i; FT_Bitmap* src; FT_Bitmap* dst; /* Handle the italic style */ if( font->style & TTF_STYLE_ITALIC ) { FT_Matrix shear; shear.xx = 1 << 16; shear.xy = (int) ( font->glyph_italics * ( 1 << 16 ) ) / font->height; shear.yx = 0; shear.yy = 1 << 16; FT_Outline_Transform( outline, &shear ); } /* Render the glyph */ if ( mono ) { error = FT_Render_Glyph( glyph, ft_render_mode_mono ); } else { error = FT_Render_Glyph( glyph, ft_render_mode_normal ); } if( error ) { return error; } /* Copy over information to cache */ src = &glyph->bitmap; if ( mono ) { dst = &cached->bitmap; } else { dst = &cached->pixmap; } memcpy( dst, src, sizeof( *dst ) ); /* FT_Render_Glyph() and .fon fonts always generate a * two-color (black and white) glyphslot surface, even * when rendered in ft_render_mode_normal. */ /* FT_IS_SCALABLE() means that the font is in outline format, * but does not imply that outline is rendered as 8-bit * grayscale, because embedded bitmap/graymap is preferred * (see FT_LOAD_DEFAULT section of FreeType2 API Reference). * FT_Render_Glyph() canreturn two-color bitmap or 4/16/256- * color graymap according to the format of embedded bitmap/ * graymap. */ if ( glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ) { dst->pitch *= 8; } else if ( glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY2 ) { dst->pitch *= 4; } else if ( glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY4 ) { dst->pitch *= 2; } /* Adjust for bold and italic text */ if( font->style & TTF_STYLE_BOLD ) { int bump = font->glyph_overhang; dst->pitch += bump; dst->width += bump; } if( font->style & TTF_STYLE_ITALIC ) { int bump = (int)ceil(font->glyph_italics); dst->pitch += bump; dst->width += bump; } if (dst->rows != 0) { dst->buffer = (unsigned char *)malloc( dst->pitch * dst->rows ); if( !dst->buffer ) { return FT_Err_Out_Of_Memory; } memset( dst->buffer, 0, dst->pitch * dst->rows ); for( i = 0; i < src->rows; i++ ) { int soffset = i * src->pitch; int doffset = i * dst->pitch; if ( mono ) { unsigned char *srcp = src->buffer + soffset; unsigned char *dstp = dst->buffer + doffset; int j; if ( glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO ) { for ( j = 0; j < src->width; j += 8 ) { unsigned char ch = *srcp++; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; } } else if ( glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY2 ) { for ( j = 0; j < src->width; j += 4 ) { unsigned char ch = *srcp++; *dstp++ = (((ch&0xA0) >> 6) >= 0x2) ? 1 : 0; ch <<= 2; *dstp++ = (((ch&0xA0) >> 6) >= 0x2) ? 1 : 0; ch <<= 2; *dstp++ = (((ch&0xA0) >> 6) >= 0x2) ? 1 : 0; ch <<= 2; *dstp++ = (((ch&0xA0) >> 6) >= 0x2) ? 1 : 0; } } else if ( glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY4 ) { for ( j = 0; j < src->width; j += 2 ) { unsigned char ch = *srcp++; *dstp++ = (((ch&0xF0) >> 4) >= 0x8) ? 1 : 0; ch <<= 4; *dstp++ = (((ch&0xF0) >> 4) >= 0x8) ? 1 : 0; } } else { for ( j = 0; j < src->width; j++ ) {
int PG_Font::GetFontHeight() { return my_internaldata->FaceCache ? FT_CEIL(FT_MulFix(my_internaldata->FaceCache->Face->height, my_internaldata->FaceCache->Face->size->metrics.y_scale)) : 0; }
int PG_Font::GetFontDescender() { return my_internaldata->FaceCache ? FT_CEIL(FT_MulFix(my_internaldata->FaceCache->Face->descender, my_internaldata->FaceCache->Face->size->metrics.y_scale)) : 0; }
static int LayoutLine( filter_t *p_filter, paragraph_t *p_paragraph, int i_start_offset, int i_end_offset, line_desc_t **pp_line ) { if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0 || i_start_offset >= i_end_offset || i_start_offset < 0 || i_start_offset >= p_paragraph->i_size || i_end_offset <= 0 || i_end_offset > p_paragraph->i_size ) { msg_Err( p_filter, "LayoutLine() invalid parameters. " "Paragraph size: %d. Runs count: %d. " "Start offset: %d. End offset: %d", p_paragraph->i_size, p_paragraph->i_runs_count, i_start_offset, i_end_offset ); return VLC_EGENERIC; } line_desc_t *p_line = NewLine( i_end_offset - i_start_offset ); if( !p_line ) return VLC_ENOMEM; filter_sys_t *p_sys = p_filter->p_sys; int i_last_run = -1; run_desc_t *p_run = 0; text_style_t *p_style = 0; FT_Face p_face = 0; FT_Vector pen = { .x = 0, .y = 0 }; int i_line_index = 0; int i_font_width = 0; int i_ul_offset = 0; int i_ul_thickness = 0; #ifdef HAVE_FRIBIDI fribidi_reorder_line( 0, p_paragraph->p_types + i_start_offset, i_end_offset - i_start_offset, 0, p_paragraph->paragraph_type, p_paragraph->p_levels + i_start_offset, 0, p_paragraph->pi_reordered_indices + i_start_offset ); #endif for( int i = i_start_offset; i < i_end_offset; ++i, ++i_line_index ) { int i_paragraph_index; #ifdef HAVE_FRIBIDI i_paragraph_index = p_paragraph->pi_reordered_indices[ i ]; #else i_paragraph_index = i; #endif line_character_t *p_ch = p_line->p_character + i_line_index; glyph_bitmaps_t *p_bitmaps = p_paragraph->p_glyph_bitmaps + i_paragraph_index; if( !p_bitmaps->p_glyph ) { --i_line_index; continue; } if( i_last_run != p_paragraph->pi_run_ids[ i_paragraph_index ] ) { i_last_run = p_paragraph->pi_run_ids[ i_paragraph_index ]; p_run = p_paragraph->p_runs + i_last_run; p_style = p_run->p_style; p_face = p_run->p_face; i_font_width = p_style->i_style_flags & STYLE_HALFWIDTH ? p_style->i_font_size / 2 : p_style->i_font_size; } FT_Vector pen_new = { .x = pen.x + p_paragraph->p_glyph_bitmaps[ i_paragraph_index ].i_x_offset, .y = pen.y + p_paragraph->p_glyph_bitmaps[ i_paragraph_index ].i_y_offset }; FT_Vector pen_shadow = { .x = pen_new.x + p_sys->f_shadow_vector_x * ( i_font_width << 6 ), .y = pen_new.y + p_sys->f_shadow_vector_y * ( p_style->i_font_size << 6 ) }; if( p_bitmaps->p_shadow ) { if( FT_Glyph_To_Bitmap( &p_bitmaps->p_shadow, FT_RENDER_MODE_NORMAL, &pen_shadow, 0 ) ) p_bitmaps->p_shadow = 0; else FT_Glyph_Get_CBox( p_bitmaps->p_shadow, ft_glyph_bbox_pixels, &p_bitmaps->shadow_bbox ); } if( p_bitmaps->p_glyph ) { if( FT_Glyph_To_Bitmap( &p_bitmaps->p_glyph, FT_RENDER_MODE_NORMAL, &pen_new, 1 ) ) { FT_Done_Glyph( p_bitmaps->p_glyph ); if( p_bitmaps->p_outline ) FT_Done_Glyph( p_bitmaps->p_outline ); if( p_bitmaps->p_shadow ) FT_Done_Glyph( p_bitmaps->p_shadow ); --i_line_index; continue; } else FT_Glyph_Get_CBox( p_bitmaps->p_glyph, ft_glyph_bbox_pixels, &p_bitmaps->glyph_bbox ); } if( p_bitmaps->p_outline ) { if( FT_Glyph_To_Bitmap( &p_bitmaps->p_outline, FT_RENDER_MODE_NORMAL, &pen_new, 1 ) ) { FT_Done_Glyph( p_bitmaps->p_outline ); p_bitmaps->p_outline = 0; } else FT_Glyph_Get_CBox( p_bitmaps->p_outline, ft_glyph_bbox_pixels, &p_bitmaps->outline_bbox ); } FixGlyph( p_bitmaps->p_glyph, &p_bitmaps->glyph_bbox, p_bitmaps->i_x_advance, p_bitmaps->i_y_advance, &pen_new ); if( p_bitmaps->p_outline ) FixGlyph( p_bitmaps->p_outline, &p_bitmaps->outline_bbox, p_bitmaps->i_x_advance, p_bitmaps->i_y_advance, &pen_new ); if( p_bitmaps->p_shadow ) FixGlyph( p_bitmaps->p_shadow, &p_bitmaps->shadow_bbox, p_bitmaps->i_x_advance, p_bitmaps->i_y_advance, &pen_shadow ); int i_line_offset = 0; int i_line_thickness = 0; text_style_t *p_glyph_style = p_paragraph->pp_styles[ i_paragraph_index ]; if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) ) { i_line_offset = abs( FT_FLOOR( FT_MulFix( p_face->underline_position, p_face->size->metrics.y_scale ) ) ); i_line_thickness = abs( FT_CEIL( FT_MulFix( p_face->underline_thickness, p_face->size->metrics.y_scale ) ) ); if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT ) { /* Move the baseline to make it strikethrough instead of * underline. That means that strikethrough takes precedence */ i_line_offset -= abs( FT_FLOOR( FT_MulFix( p_face->descender * 2, p_face->size->metrics.y_scale ) ) ); } else if( i_line_thickness > 0 ) { p_bitmaps->glyph_bbox.yMin = __MIN( p_bitmaps->glyph_bbox.yMin, - i_line_offset - i_line_thickness ); /* The real underline thickness and position are * updated once the whole line has been parsed */ i_ul_offset = __MAX( i_ul_offset, i_line_offset ); i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness ); i_line_thickness = -1; } } p_ch->p_glyph = ( FT_BitmapGlyph ) p_bitmaps->p_glyph; p_ch->p_outline = ( FT_BitmapGlyph ) p_bitmaps->p_outline; p_ch->p_shadow = ( FT_BitmapGlyph ) p_bitmaps->p_shadow; bool b_karaoke = p_paragraph->pi_karaoke_bar[ i_paragraph_index ] != 0; p_ch->i_color = b_karaoke ? ( uint32_t ) p_glyph_style->i_karaoke_background_color | p_glyph_style->i_karaoke_background_alpha << 24 : ( uint32_t ) p_glyph_style->i_font_color | p_glyph_style->i_font_alpha << 24; p_ch->i_line_thickness = i_line_thickness; p_ch->i_line_offset = i_line_offset; BBoxEnlarge( &p_line->bbox, &p_bitmaps->glyph_bbox ); if( p_bitmaps->p_outline ) BBoxEnlarge( &p_line->bbox, &p_bitmaps->outline_bbox ); if( p_bitmaps->p_shadow ) BBoxEnlarge( &p_line->bbox, &p_bitmaps->shadow_bbox ); pen.x += p_bitmaps->i_x_advance; pen.y += p_bitmaps->i_y_advance; } p_line->i_width = __MAX( 0, p_line->bbox.xMax - p_line->bbox.xMin ); p_line->i_height = __MAX( 0, p_line->bbox.yMax - p_line->bbox.yMin ); p_line->i_character_count = i_line_index; if( i_ul_thickness > 0 ) { for( int i = 0; i < p_line->i_character_count; i++ ) { line_character_t *ch = &p_line->p_character[i]; if( ch->i_line_thickness < 0 ) { ch->i_line_offset = i_ul_offset; ch->i_line_thickness = i_ul_thickness; } } } *pp_line = p_line; return VLC_SUCCESS; } static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph, int i_max_pixel_width, line_desc_t **pp_lines ) { if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0 ) { msg_Err( p_filter, "LayoutParagraph() invalid parameters. " "Paragraph size: %d. Runs count %d", p_paragraph->i_size, p_paragraph->i_runs_count ); return VLC_EGENERIC; } int i_line_start = 0; FT_Pos i_width = 0; FT_Pos i_max_width = i_max_pixel_width << 6; FT_Pos i_preferred_width = 0; FT_Pos i_total_width = 0; FT_Pos i_last_space_width = 0; int i_last_space = -1; line_desc_t *p_first_line = 0; line_desc_t **pp_line = &p_first_line; for( int i = 0; i < p_paragraph->i_size; ++i ) { #ifdef HAVE_FRIBIDI p_paragraph->pi_reordered_indices[ i ] = i; #endif i_total_width += p_paragraph->p_glyph_bitmaps[ i ].i_x_advance; } int i_line_count = i_total_width / i_max_width + 1; i_preferred_width = i_total_width / i_line_count; for( int i = 0; i <= p_paragraph->i_size; ++i ) { if( i == p_paragraph->i_size ) { if( i_line_start < i ) if( LayoutLine( p_filter, p_paragraph, i_line_start, i, pp_line ) ) goto error; break; } if( p_paragraph->p_code_points[ i ] == ' ' #ifdef HAVE_FRIBIDI || p_paragraph->p_types[ i ] == FRIBIDI_TYPE_WS #endif ) { if( i_line_start == i ) { /* * Free orphaned white space glyphs not belonging to any lines. * At this point p_shadow points to either p_glyph or p_outline, * so we should not free it explicitly. */ if( p_paragraph->p_glyph_bitmaps[ i ].p_glyph ) FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_glyph ); if( p_paragraph->p_glyph_bitmaps[ i ].p_outline ) FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_outline ); i_line_start = i + 1; continue; } if( i_last_space == i - 1 ) { p_paragraph->p_glyph_bitmaps[ i - 1 ].i_x_advance = 0; i_last_space = i; continue; } i_last_space = i; i_last_space_width = i_width; } i_width += p_paragraph->p_glyph_bitmaps[ i ].i_x_advance; if( i_last_space_width >= i_preferred_width || i_width >= i_max_width ) { if( i_line_start == i ) { msg_Err( p_filter, "LayoutParagraph(): Width of single glyph exceeds maximum" ); goto error; } int i_end_offset; if( i_last_space > i_line_start ) i_end_offset = i_last_space; else i_end_offset = i; if( LayoutLine( p_filter, p_paragraph, i_line_start, i_end_offset, pp_line ) ) goto error; pp_line = &( *pp_line )->p_next; i_line_start = i_end_offset; i = i_line_start - 1; i_width = 0; i_last_space_width = 0; } } *pp_lines = p_first_line; return VLC_SUCCESS; error: for( int i = i_line_start; i < p_paragraph->i_size; ++i ) { if( p_paragraph->p_glyph_bitmaps[ i ].p_glyph ) FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_glyph ); if( p_paragraph->p_glyph_bitmaps[ i ].p_outline ) FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_outline ); } if( p_first_line ) FreeLines( p_first_line ); return VLC_EGENERIC; } int LayoutText( filter_t *p_filter, line_desc_t **pp_lines, FT_BBox *p_bbox, int *pi_max_face_height, uni_char_t *psz_text, text_style_t **pp_styles, uint32_t *pi_k_dates, int i_len ) { line_desc_t *p_first_line = 0; line_desc_t **pp_line = &p_first_line; paragraph_t *p_paragraph = 0; int i_paragraph_start = 0; int i_max_height = 0; for( int i = 0; i <= i_len; ++i ) { if( i == i_len || psz_text[ i ] == '\n' ) { if( i_paragraph_start == i ) { i_paragraph_start = i + 1; continue; } p_paragraph = NewParagraph( p_filter, i - i_paragraph_start, psz_text + i_paragraph_start, pp_styles + i_paragraph_start, pi_k_dates ? pi_k_dates + i_paragraph_start : 0, 20 ); if( !p_paragraph ) { if( p_first_line ) FreeLines( p_first_line ); return VLC_ENOMEM; } #ifdef HAVE_FRIBIDI if( AnalyzeParagraph( p_paragraph ) ) goto error; #endif if( ItemizeParagraph( p_filter, p_paragraph ) ) goto error; #if defined HAVE_HARFBUZZ if( ShapeParagraphHarfBuzz( p_filter, &p_paragraph ) ) goto error; if( LoadGlyphs( p_filter, p_paragraph, true, false ) ) goto error; #elif defined HAVE_FRIBIDI if( ShapeParagraphFriBidi( p_filter, p_paragraph ) ) goto error; if( LoadGlyphs( p_filter, p_paragraph, false, true ) ) goto error; if( RemoveZeroWidthCharacters( p_paragraph ) ) goto error; if( ZeroNsmAdvance( p_paragraph ) ) goto error; #else if( LoadGlyphs( p_filter, p_paragraph, false, true ) ) goto error; #endif /* * Set max line width to allow for outline and shadow glyphs, * and any extra width caused by visual reordering */ int i_max_width = ( int ) p_filter->fmt_out.video.i_visible_width - 2 * p_filter->p_sys->style.i_font_size; if( LayoutParagraph( p_filter, p_paragraph, i_max_width, pp_line ) ) goto error; FreeParagraph( p_paragraph ); p_paragraph = 0; for( ; *pp_line; pp_line = &( *pp_line )->p_next ) i_max_height = __MAX( i_max_height, ( *pp_line )->i_height ); i_paragraph_start = i + 1; } } int i_base_line = 0; FT_BBox bbox = { .xMin = INT_MAX, .yMin = INT_MAX, .xMax = INT_MIN, .yMax = INT_MIN }; for( line_desc_t *p_line = p_first_line; p_line; p_line = p_line->p_next ) { p_line->i_base_line = i_base_line; p_line->bbox.yMin -= i_base_line; p_line->bbox.yMax -= i_base_line; BBoxEnlarge( &bbox, &p_line->bbox ); i_base_line += i_max_height; } *pp_lines = p_first_line; *p_bbox = bbox; *pi_max_face_height = i_max_height; return VLC_SUCCESS; error: if( p_first_line ) FreeLines( p_first_line ); if( p_paragraph ) FreeParagraph( p_paragraph ); return VLC_EGENERIC; }
static FT_Error Load_Glyph( TTF_Font* font, unsigned short ch, c_glyph* cached, int want) { FT_Face face; FT_Error error; FT_GlyphSlot glyph; FT_Glyph_Metrics* metrics; FT_Outline* outline; assert( font ); assert( font->face ); face = font->face; /* Load the glyph */ if ( ! cached->index ) { cached->index = FT_Get_Char_Index( face, ch ); } error = FT_Load_Glyph( face, cached->index, FT_LOAD_DEFAULT ); if( error ) { return error; } /* Get our glyph shortcuts */ glyph = face->glyph; metrics = &glyph->metrics; outline = &glyph->outline; /* Get the glyph metrics if desired */ if ( (want & CACHED_METRICS) && !(cached->stored & CACHED_METRICS) ) { /* Get the bounding box */ cached->minx = FT_FLOOR(metrics->horiBearingX); cached->maxx = cached->minx + FT_CEIL(metrics->width); 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); /* Adjust for bold and italic text */ if( font->style & TTF_STYLE_BOLD ) { cached->maxx += font->glyph_overhang; } if( font->style & TTF_STYLE_ITALIC ) { cached->maxx += (int)ceil(font->glyph_italics); } cached->stored |= CACHED_METRICS; } if ( ((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) || ((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP)) ) { int mono = (want & CACHED_BITMAP); int i; FT_Bitmap* src; FT_Bitmap* dst; /* Handle the italic style */ if( font->style & TTF_STYLE_ITALIC ) { FT_Matrix shear; shear.xx = 1 << 16; shear.xy = (int) ( font->glyph_italics * ( 1 << 16 ) )/ font->height; shear.yx = 0; shear.yy = 1 << 16; FT_Outline_Transform( outline, &shear ); } /* Render the glyph */ if ( mono ) { error = FT_Render_Glyph( glyph, ft_render_mode_mono ); } else { error = FT_Render_Glyph( glyph, ft_render_mode_normal ); } if( error ) { return error; } /* Copy over information to cache */ src = &glyph->bitmap; if ( mono ) { dst = &cached->bitmap; } else { dst = &cached->pixmap; } memcpy( dst, src, sizeof( *dst ) ); if ( mono ) { dst->pitch *= 8; } /* Adjust for bold and italic text */ if( font->style & TTF_STYLE_BOLD ) { int bump = font->glyph_overhang; dst->pitch += bump; dst->width += bump; } if( font->style & TTF_STYLE_ITALIC ) { int bump = (int)ceil(font->glyph_italics); dst->pitch += bump; dst->width += bump; } if (dst->rows != 0) { dst->buffer = malloc( dst->pitch * dst->rows ); if( !dst->buffer ) { return FT_Err_Out_Of_Memory; } memset( dst->buffer, 0, dst->pitch * dst->rows ); for( i = 0; i < src->rows; i++ ) { int soffset = i * src->pitch; int doffset = i * dst->pitch; if ( mono ) { unsigned char *srcp = src->buffer + soffset; unsigned char *dstp = dst->buffer + doffset; int j; for ( j = 0; j < src->width; j += 8 ) { unsigned char ch = *srcp++; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; ch <<= 1; *dstp++ = (ch&0x80) >> 7; } } else { memcpy(dst->buffer+doffset, src->buffer+soffset,src->pitch); } }
TTF_Font* TTF_OpenFontIndex( const char *file, int ptsize, long index ) { TTF_Font* font; FT_Error error; FT_Face face; FT_Fixed scale; extern int strict_font; font = (TTF_Font*) malloc(sizeof *font); if ( font == NULL ) { fprintf(stderr, "Out of memory\n" ); return NULL; } memset( font, 0, sizeof( *font ) ); /* Open the font and create ancillary data */ error = FT_New_Face( library, file, 0, &font->face ); if( error && !strict_font ) error=FT_New_Memory_Face(library, (const FT_Byte*)DroidSans_ttf, DROIDSANS_SIZE, 0, &font->face ); if( error ) { printf( "Couldn't load font file\n"); free( font ); return NULL; } if ( index != 0 ) { if ( font->face->num_faces > index ) { FT_Done_Face( font->face ); error = FT_New_Face( library, file, index, &font->face ); if( error ) { printf( "Couldn't get font face\n"); free( font ); return NULL; } } else { fprintf(stderr, "No such font face\n"); free( font ); return NULL; } } face = font->face; /* Make sure that our font face is scalable (global metrics) */ if ( ! FT_IS_SCALABLE(face) ) { fprintf(stderr,"Font face is not scalable\n"); TTF_CloseFont( font ); return NULL; } /* Set the character size and use default DPI (72) */ error = FT_Set_Char_Size( font->face, 0, ptsize * 64, 0, 0 ); if( error ) { fprintf(stderr, "Couldn't set font size\n"); TTF_CloseFont( font ); return NULL; } /* Get the scalable font metrics for this font */ scale = face->size->metrics.y_scale; font->ascent = FT_CEIL(FT_MulFix(face->bbox.yMax, scale)); font->descent = FT_CEIL(FT_MulFix(face->bbox.yMin, scale)); font->height = font->ascent - font->descent + /* baseline */ 1; font->lineskip = FT_CEIL(FT_MulFix(face->height, scale)); font->underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale)); font->underline_height = FT_FLOOR(FT_MulFix(face->underline_thickness, scale)); if ( font->underline_height < 1 ) { font->underline_height = 1; } #ifdef DEBUG_FONTS printf("Font metrics:\n"); printf("\tascent = %d, descent = %d\n", font->ascent, font->descent); printf("\theight = %d, lineskip = %d\n", font->height, font->lineskip); printf("\tunderline_offset = %d, underline_height = %d\n", font->underline_offset, font->underline_height); #endif /* Set the default font style */ font->style = TTF_STYLE_NORMAL; font->glyph_overhang = face->size->metrics.y_ppem / 10; /* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */ /*font->glyph_italics = 0.207f; font->glyph_italics *= font->height;*/ return font; }
PixelSize Font::TextSize(const TCHAR *text) const { assert(text != nullptr); #ifndef _UNICODE assert(ValidateUTF8(text)); #endif const FT_Face face = this->face; const bool use_kerning = FT_HAS_KERNING(face); int x = 0, minx = 0, maxx = 0; unsigned prev_index = 0; #ifndef ENABLE_OPENGL const ScopeLock protect(freetype_mutex); #endif while (true) { const auto n = NextChar(text); if (n.first == 0) break; const unsigned ch = n.first; text = n.second; FT_UInt i = FT_Get_Char_Index(face, ch); if (i == 0) continue; FT_Error error = FT_Load_Glyph(face, i, load_flags); if (error) continue; const FT_GlyphSlot glyph = face->glyph; const FT_Glyph_Metrics &metrics = glyph->metrics; if (use_kerning) { if (prev_index != 0 && i != 0) { FT_Vector delta; FT_Get_Kerning(face, prev_index, i, ft_kerning_default, &delta); x += delta.x >> 6; } prev_index = i; } const int glyph_minx = FT_FLOOR(metrics.horiBearingX); const int glyph_maxx = minx + FT_CEIL(metrics.width); const int glyph_advance = FT_CEIL(metrics.horiAdvance); int z = x + glyph_minx; if (z < minx) minx = z; z = x + std::max(glyph_maxx, glyph_advance); if (z > maxx) maxx = z; x += glyph_advance; }
TTF_Font* TTF_OpenFontIndexRW( FILE* src, int freesrc, int ptsize, long index ) { TTF_Font* font; FT_Error error; FT_Face face; FT_Fixed scale; FT_Stream stream; FT_CharMap found; int position, i; if ( ! TTF_initialized ) { TTF_SetError( "Library not initialized" ); return NULL; } /* Check to make sure we can seek in this stream */ position = ftell(src); if ( position < 0 ) { TTF_SetError( "Can't seek in stream" ); return NULL; } font = (TTF_Font*) malloc(sizeof *font); if ( font == NULL ) { TTF_SetError( "Out of memory" ); return NULL; } memset(font, 0, sizeof(*font)); font->src = src; font->freesrc = freesrc; stream = (FT_Stream)malloc(sizeof(*stream)); if ( stream == NULL ) { TTF_SetError( "Out of memory" ); TTF_CloseFont( font ); return NULL; } memset(stream, 0, sizeof(*stream)); stream->read = ft_read; stream->descriptor.pointer = src; stream->pos = (unsigned long)position; fseek(src, 0, SEEK_END); stream->size = (unsigned long)(ftell(src) - position); fseek(src, position, SEEK_SET); font->args.flags = FT_OPEN_STREAM; font->args.stream = stream; error = FT_Open_Face( library, &font->args, index, &font->face ); if( error ) { TTF_SetFTError( "Couldn't load font file", error ); TTF_CloseFont( font ); return NULL; } face = font->face; /* Set charmap for loaded font */ found = 0; for (i = 0; i < face->num_charmaps; i++) { FT_CharMap charmap = face->charmaps[i]; /* Windows Unicode */ if ((charmap->platform_id == 3 && charmap->encoding_id == 1) /* Windows Symbol */ || (charmap->platform_id == 3 && charmap->encoding_id == 0) /* ISO Unicode */ || (charmap->platform_id == 2 && charmap->encoding_id == 1) /* Apple Unicode */ || (charmap->platform_id == 0)) { found = charmap; break; } } if ( found ) { /* If this fails, continue using the default charmap */ FT_Set_Charmap(face, found); } /* Make sure that our font face is scalable (global metrics) */ if ( FT_IS_SCALABLE(face) ) { /* Set the character size and use default DPI (72) */ error = FT_Set_Char_Size( font->face, 0, ptsize * 64, 0, 0 ); if( error ) { TTF_SetFTError( "Couldn't set font size", error ); TTF_CloseFont( font ); return NULL; } /* Get the scalable font metrics for this font */ scale = face->size->metrics.y_scale; font->ascent = FT_CEIL(FT_MulFix(face->ascender, scale)); font->descent = FT_CEIL(FT_MulFix(face->descender, scale)); font->height = font->ascent - font->descent + /* baseline */ 1; font->lineskip = FT_CEIL(FT_MulFix(face->height, scale)); font->underline_offset = FT_FLOOR( FT_MulFix(face->underline_position, scale)); font->underline_height = FT_FLOOR( FT_MulFix(face->underline_thickness, scale)); } else { /* Non-scalable font case. ptsize determines which family * or series of fonts to grab from the non-scalable format. * It is not the point size of the font. * */ if ( ptsize >= font->face->num_fixed_sizes ) ptsize = font->face->num_fixed_sizes - 1; font->font_size_family = ptsize; error = FT_Set_Pixel_Sizes( face, face->available_sizes[ptsize].height, face->available_sizes[ptsize].width ); /* With non-scalale fonts, Freetype2 likes to fill many of the * font metrics with the value of 0. The size of the * non-scalable fonts must be determined differently * or sometimes cannot be determined. * */ font->ascent = face->available_sizes[ptsize].height; font->descent = 0; font->height = face->available_sizes[ptsize].height; font->lineskip = FT_CEIL(font->ascent); font->underline_offset = FT_FLOOR(face->underline_position); font->underline_height = FT_FLOOR(face->underline_thickness); } if ( font->underline_height < 1 ) { font->underline_height = 1; } #ifdef DEBUG_FONTS printf("Font metrics:\n"); printf("\tascent = %d, descent = %d\n", font->ascent, font->descent); printf("\theight = %d, lineskip = %d\n", font->height, font->lineskip); printf("\tunderline_offset = %d, underline_height = %d\n", font->underline_offset, font->underline_height); printf("\tunderline_top_row = %d, strikethrough_top_row = %d\n", TTF_underline_top_row(font), TTF_strikethrough_top_row(font)); #endif /* Initialize the font face style */ font->face_style = TTF_STYLE_NORMAL; if ( font->face->style_flags & FT_STYLE_FLAG_BOLD ) { font->face_style |= TTF_STYLE_BOLD; } if ( font->face->style_flags & FT_STYLE_FLAG_ITALIC ) { font->face_style |= TTF_STYLE_ITALIC; } /* Set the default font style */ font->style = font->face_style; font->outline = 0; font->kerning = 1; font->glyph_overhang = face->size->metrics.y_ppem / 10; /* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */ font->glyph_italics = 0.207f; font->glyph_italics *= font->height; return font; }