void Print_Type( FT_Face face ) { FT_Module module; printf( "font type entries\n" ); module = &face->driver->root; printf( " FreeType driver: %s\n", module->clazz->module_name ); /* Is it better to dump all sfnt tag names? */ printf( " sfnt wrapped: %s\n", FT_IS_SFNT( face ) ? (char *)"yes" : (char *)"no" ); /* isScalable? */ comma_flag = 0; printf( " type: " ); if ( FT_IS_SCALABLE( face ) ) { Print_Comma( "scalable" ); if ( FT_HAS_MULTIPLE_MASTERS( face ) ) Print_Comma( "multiple masters" ); } if ( FT_HAS_FIXED_SIZES( face ) ) Print_Comma( "fixed size" ); printf( "\n" ); /* Direction */ comma_flag = 0; printf( " direction: " ); if ( FT_HAS_HORIZONTAL( face ) ) Print_Comma( "horizontal" ); if ( FT_HAS_VERTICAL( face ) ) Print_Comma( "vertical" ); printf( "\n" ); printf( " fixed width: %s\n", FT_IS_FIXED_WIDTH( face ) ? (char *)"yes" : (char *)"no" ); printf( " glyph names: %s\n", FT_HAS_GLYPH_NAMES( face ) ? (char *)"yes" : (char *)"no" ); if ( FT_IS_SCALABLE( face ) ) { printf( " EM size: %d\n", face->units_per_EM ); printf( " global BBox: (%ld,%ld):(%ld,%ld)\n", face->bbox.xMin, face->bbox.yMin, face->bbox.xMax, face->bbox.yMax ); printf( " ascent: %d\n", face->ascender ); printf( " descent: %d\n", face->descender ); printf( " text height: %d\n", face->height ); } }
bool FreeTypeFont::hasVertical() const { OpenThreads::ScopedLock<OpenThreads::Mutex> lock(FreeTypeLibrary::instance()->getMutex()); return FT_HAS_VERTICAL(_face)!=0; }
// // Constructor: // FontFace::FontFace( FontLibrary &library, const std::string &fileName ){ _fileName=fileName; // // By default report on fragmentary and partial orthographic // support in addition to fully supported orthographies: // _reportMissing = true; _reportFragmentary = true; _reportPartial = true; _reportFull = true; _reportConscript = false; FT_Error err; err = FT_New_Face(library.get(),_fileName.c_str(),0, &_face ); if(err==FT_Err_Unknown_File_Format) throw Exception("FontFace()","Unknown file format."); else if(err) throw Exception("FontFace()","Unable to open or process font file."); // // // _commonName = _face->family_name; _subFamily = _face->style_name; _glyphCount = _face->num_glyphs; if(FT_IS_SFNT(_face)){ FT_UInt count=FT_Get_Sfnt_Name_Count(_face); FT_SfntName fontName; for(unsigned j=0;j<count;j++){ FT_Get_Sfnt_Name(_face,j,&fontName); if(fontName.platform_id==3 && fontName.encoding_id==1){ // // Microsoft 3:1 Platform: // if( fontName.language_id==0x0409 ){ // // This is the de-facto // default AMERICAN ENGLISH // entry: // switch(fontName.name_id){ case NID_FONT_FAMILY: if(_commonName.empty()) _commonName = _getPlatform3Encoding1String(fontName.string_len,fontName.string); break; case NID_FONT_SUBFAM: if(_subFamily.empty()) _subFamily = _getPlatform3Encoding1String(fontName.string_len,fontName.string); break; case NID_VERSION: if(_version.empty()) _version = _getPlatform3Encoding1String(fontName.string_len,fontName.string); break; case NID_VENDOR: if(_vendor.empty()) _vendor = _getPlatform3Encoding1String(fontName.string_len,fontName.string); break; case NID_DESIGNER: if(_designer.empty()) _designer = _getPlatform3Encoding1String(fontName.string_len,fontName.string); break; case NID_URL_VENDOR: if(_vendorURL.empty()) _vendorURL = _getPlatform3Encoding1String(fontName.string_len,fontName.string); break; case NID_URL_DESIGNER: if(_designerURL.empty()) _designerURL = _getPlatform3Encoding1String(fontName.string_len,fontName.string); break; default: break; } }else{ // // Some other language: // We take whatever the very first // other language is to be // our "nativeName" string: // switch(fontName.name_id){ case NID_FONT_FAMILY: if(_nativeName.empty()) _nativeName = _getPlatform3Encoding1String(fontName.string_len,fontName.string); break; default: break; } } // // DEBUG // //if(fontName.name_id==NID_COPYRIGHT){ // // DEBUG: // std::cout << ">>> COPYRIGHT: " << _getPlatform3Encoding1String(fontName.string_len,fontName.string) << std::endl; //} //if(fontName.name_id==NID_VERSION){ // // DEBUG: // std::cout << ">>> VERSION : " << _getPlatform3Encoding1String(fontName.string_len,fontName.string) << std::endl; //} }else if(fontName.platform_id==1 && fontName.encoding_id==0){ // // Macintosh 1:0 platform: This is going to // be English in the MacRoman encoding: // // NOTA BENE: We currently do not deal with // non-English Mac strings. They seem to be // very rare in real fonts out there in the world. // switch(fontName.name_id){ case NID_FONT_FAMILY: if(_commonName.empty()) _commonName = _getPlatform1Encoding0String(fontName.string_len,fontName.string); break; case NID_FONT_SUBFAM: if(_subFamily.empty()) _subFamily = _getPlatform1Encoding0String(fontName.string_len,fontName.string); break; case NID_VERSION: if(_version.empty()) _version = _getPlatform1Encoding0String(fontName.string_len,fontName.string); break; case NID_VENDOR: if(_vendor.empty()) _vendor = _getPlatform1Encoding0String(fontName.string_len,fontName.string); break; case NID_DESIGNER: if(_designer.empty()) _designer = _getPlatform1Encoding0String(fontName.string_len,fontName.string); break; case NID_URL_VENDOR: if(_vendorURL.empty()) _vendorURL = _getPlatform1Encoding0String(fontName.string_len,fontName.string); break; case NID_URL_DESIGNER: if(_designerURL.empty()) _designerURL = _getPlatform1Encoding0String(fontName.string_len,fontName.string); break; default: break; } } } } // // If native name is missing, fill it in: // //if(_nativeName.empty()) _nativeName = _commonName; _style=NORMAL; if( _face->style_flags & FT_STYLE_FLAG_ITALIC ) _style = ITALIC; _weight=NORMAL_WEIGHT; if( _face->style_flags & FT_STYLE_FLAG_BOLD ) _weight = BOLD; _isFixedWidth=FT_IS_FIXED_WIDTH(_face); // // Vertical metrics for Japanese, Chinese, Mongolian: // _hasVerticalMetrics = FT_HAS_VERTICAL(_face); // // Check for embedded bitmaps: // _hasFixedSizes = FT_HAS_FIXED_SIZES(_face); // // Get the set of unicode values this font covers // _getUnicodeValues(); // // Check orthographic coverage: // //_checkOrthographies(); // // Check license: // _checkLicenses(); }
bool Font::CreateFromFile( const char* path, const uint32_t font_size, const bool smooth, const uint32_t start_char, const size_t num_chars ) { if( path == NULL || font_size == 0 || num_chars == 0 || _num_glyphs > 0 ) return false; Mojo::Services::Filesystem* filesystem = MOJO_GET_SERVICE(Filesystem); Mojo::Filesystem::File* file = filesystem->Open(path, Mojo::Filesystem::FILE_READ); if( !file ) return false; size_t file_len = filesystem->Length(file); if( file_len == 0 ) { filesystem->Close(file); return false; } // todo: Use FT_Open_Face to read the file instead? uint8_t* buffer = new uint8_t[file_len]; { uint32_t num_bytes_read = 0; while( num_bytes_read < file_len ) { size_t read = filesystem->Read(file, file_len - num_bytes_read, (void*)&buffer[num_bytes_read]); mojo_assertf(read > 0, "Font::CreateFromFile\n -> Services/Filesystem error?\n"); num_bytes_read += read; } } filesystem->Close(file); FT_Face ft_face; if( FT_New_Memory_Face(Mojo::GetFreeTypeLibrary(), (const FT_Byte*)buffer, file_len, 0, &ft_face) ) { delete[] buffer; return false; } FT_Set_Char_Size(ft_face, font_size * 64, font_size * 64, 72, 72); const float line_height = ft_face->size->metrics.height >> 6; // Load the glyphs Font::Glyph* glyphs = new Font::Glyph[num_chars]; FT_Glyph* ft_glyphs = new FT_Glyph[num_chars]; FT_BitmapGlyph* bm_glyphs = (FT_BitmapGlyph*)ft_glyphs; float blo = 0.0f; uint32_t bm_max_width = 0, bm_max_height = 0; for( uint32_t i = 0; i < num_chars; ++i ) { FT_Load_Char(ft_face, start_char + i, FT_LOAD_DEFAULT); FT_Get_Glyph(ft_face->glyph, &ft_glyphs[i]); glyphs[i].x_advance = ft_face->glyph->advance.x >> 6; glyphs[i].y_advance = ft_face->glyph->advance.y >> 6; glyphs[i].x_bearing = (FT_HAS_VERTICAL(ft_face) ? ft_face->glyph->metrics.vertBearingX : ft_face->glyph->metrics.horiBearingX) >> 6; glyphs[i].y_bearing = (FT_HAS_VERTICAL(ft_face) ? ft_face->glyph->metrics.vertBearingY : ft_face->glyph->metrics.horiBearingY) >> 6; FT_Glyph_To_Bitmap(&ft_glyphs[i], smooth ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1); const FT_Bitmap bitmap = bm_glyphs[i]->bitmap; glyphs[i].width = bitmap.width; glyphs[i].height = bitmap.rows; bm_max_width = bm_max_width < bitmap.width ? bitmap.width : bm_max_width; bm_max_height = bm_max_height < bitmap.rows ? bitmap.rows : bm_max_height; const float gbl = -glyphs[i].y_bearing + glyphs[i].height; blo = (blo < gbl) ? gbl : blo; } // Determine the texture atlas size uint32_t atlas_width = 0, atlas_height = 0; { static const struct { uint32_t width, height, area; } atlas_sizes[] = { { 64, 64, 64 * 64 }, { 128, 128, 128 * 128 }, { 256, 256, 256 * 256 }, { 512, 512, 512 * 512 } }; uint32_t min_area = 0; for( uint32_t i = 0; i < num_chars; ++i ) min_area += uint32_t((glyphs[i].width + 2) * (bm_max_height + 2)); for( uint32_t i = 0; i < 4; ++i ) { if( atlas_sizes[i].area < min_area ) continue; const uint32_t index = (i > 3) ? 3 : i; atlas_width = atlas_sizes[index].width; atlas_height = atlas_sizes[index].height; break; } if( atlas_width == 0 || atlas_height == 0 ) { for( uint32_t i = 0; i < num_chars; ++i ) FT_Done_Glyph(ft_glyphs[i]); delete[] ft_glyphs; delete[] glyphs; FT_Done_Face(ft_face); delete[] buffer; return false; } } Mojo::TextureAtlas tex_atlas = Mojo::TextureAtlas(atlas_width, atlas_height, 32); Mojo::BookshelfTexturePacker tex_packer = Mojo::BookshelfTexturePacker(&tex_atlas); uint8_t* bm_buffer = new uint8_t[bm_max_width * bm_max_height * 4]; for( uint32_t i = 0; i < num_chars; ++i ) { const FT_Bitmap bitmap = bm_glyphs[i]->bitmap; if( bitmap.width == 0 || bitmap.rows == 0 ) continue; memset((void*)bm_buffer, 255, bm_max_width * bm_max_height * 4); switch( bitmap.pixel_mode ) { default: { mojo_assertf(0, "Font::CreateFromFile()\n -> Pixel mode not supported.\n"); } break; case FT_PIXEL_MODE_MONO: { const uint8_t* bm_pixels = (const uint8_t*)bitmap.buffer; const uint32_t y_offset = 0; for( uint32_t y = 0; y < bitmap.rows; ++y ) { for( uint32_t x = 0; x < bitmap.width; ++x ) bm_buffer[(x + y * bitmap.width) * 4 + 3] = ((bm_pixels[x / 8]) & (1 << (7 - (x % 8)))) ? 0xFF : 0x00; bm_pixels += bitmap.pitch; } } break; case FT_PIXEL_MODE_GRAY: { const uint8_t* bm_pixels = (const uint8_t*)bitmap.buffer; for( uint32_t y = 0; y < bitmap.rows; ++y ) { for( uint32_t x = 0; x < bitmap.width; ++x ) bm_buffer[(x + y * bitmap.width) * 4 + 3] = bm_pixels[x]; bm_pixels += bitmap.pitch; } } break; } Mojo::Recti packed_rect; if( !tex_packer.Pack(bitmap.width, bitmap.rows, 1, bm_buffer, packed_rect) ) { Mojo::DebugPrintf(DBG_WARNING, "Font::CreateFromFile()\n -> unable to pack character '%c'\n", i + start_char); } glyphs[i].tex_coords[0] = (float)packed_rect.x / atlas_width; glyphs[i].tex_coords[1] = (float)packed_rect.y / atlas_height; glyphs[i].tex_coords[2] = (float)(packed_rect.x + packed_rect.width) / atlas_width; glyphs[i].tex_coords[3] = (float)(packed_rect.y + packed_rect.height) / atlas_height; } delete[] bm_buffer; // Unload the glyphs for( uint32_t i = 0; i < num_chars; ++i ) FT_Done_Glyph(ft_glyphs[i]); delete[] ft_glyphs; FT_Done_Face(ft_face); delete[] buffer; // Update _font_atlas = tex_atlas.Compile(); _start_glyph = start_char; _num_glyphs = num_chars; _glyphs = glyphs; _base_line = bm_max_height - blo; _line_height = line_height; return true; }
void font_instance::LoadGlyph(int glyph_id) { if ( pFont == NULL ) { return; } InitTheFace(); #ifndef USE_PANGO_WIN32 if ( !FT_IS_SCALABLE(theFace) ) { return; // bitmap font } #endif if ( id_to_no.find(glyph_id) == id_to_no.end() ) { Geom::PathBuilder path_builder; if ( nbGlyph >= maxGlyph ) { maxGlyph=2*nbGlyph+1; glyphs=(font_glyph*)realloc(glyphs,maxGlyph*sizeof(font_glyph)); } font_glyph n_g; n_g.pathvector=NULL; n_g.bbox[0]=n_g.bbox[1]=n_g.bbox[2]=n_g.bbox[3]=0; n_g.h_advance = 0; n_g.v_advance = 0; n_g.h_width = 0; n_g.v_width = 0; bool doAdd=false; #ifdef USE_PANGO_WIN32 #ifndef GGO_UNHINTED // For compatibility with old SDKs. #define GGO_UNHINTED 0x0100 #endif MAT2 identity = {{0,1},{0,0},{0,0},{0,1}}; OUTLINETEXTMETRIC otm; GetOutlineTextMetrics(daddy->hScreenDC, sizeof(otm), &otm); GLYPHMETRICS metrics; DWORD bufferSize=GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity); double scale=1.0/daddy->fontSize; n_g.h_advance=metrics.gmCellIncX*scale; n_g.v_advance=otm.otmTextMetrics.tmHeight*scale; n_g.h_width=metrics.gmBlackBoxX*scale; n_g.v_width=metrics.gmBlackBoxY*scale; if ( bufferSize == GDI_ERROR) { // shit happened } else if ( bufferSize == 0) { // character has no visual representation, but is valid (eg whitespace) doAdd=true; } else { char *buffer = new char[bufferSize]; if ( GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, bufferSize, buffer, &identity) <= 0 ) { // shit happened } else { // Platform SDK is rubbish, read KB87115 instead DWORD polyOffset=0; while ( polyOffset < bufferSize ) { TTPOLYGONHEADER const *polyHeader=(TTPOLYGONHEADER const *)(buffer+polyOffset); if (polyOffset+polyHeader->cb > bufferSize) break; if (polyHeader->dwType == TT_POLYGON_TYPE) { path_builder.moveTo(pointfx_to_nrpoint(polyHeader->pfxStart, scale)); DWORD curveOffset=polyOffset+sizeof(TTPOLYGONHEADER); while ( curveOffset < polyOffset+polyHeader->cb ) { TTPOLYCURVE const *polyCurve=(TTPOLYCURVE const *)(buffer+curveOffset); POINTFX const *p=polyCurve->apfx; POINTFX const *endp=p+polyCurve->cpfx; switch (polyCurve->wType) { case TT_PRIM_LINE: while ( p != endp ) path_builder.lineTo(pointfx_to_nrpoint(*p++, scale)); break; case TT_PRIM_QSPLINE: { g_assert(polyCurve->cpfx >= 2); // The list of points specifies one or more control points and ends with the end point. // The intermediate points (on the curve) are the points between the control points. Geom::Point this_control = pointfx_to_nrpoint(*p++, scale); while ( p+1 != endp ) { // Process all "midpoints" (all points except the last) Geom::Point new_control = pointfx_to_nrpoint(*p++, scale); path_builder.quadTo(this_control, (new_control+this_control)/2); this_control = new_control; } Geom::Point end = pointfx_to_nrpoint(*p++, scale); path_builder.quadTo(this_control, end); } break; case 3: // TT_PRIM_CSPLINE g_assert(polyCurve->cpfx % 3 == 0); while ( p != endp ) { path_builder.curveTo(pointfx_to_nrpoint(p[0], scale), pointfx_to_nrpoint(p[1], scale), pointfx_to_nrpoint(p[2], scale)); p += 3; } break; } curveOffset += sizeof(TTPOLYCURVE)+sizeof(POINTFX)*(polyCurve->cpfx-1); } } polyOffset += polyHeader->cb; } doAdd=true; } delete [] buffer; } #else if (FT_Load_Glyph (theFace, glyph_id, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) { // shit happened } else { if ( FT_HAS_HORIZONTAL(theFace) ) { n_g.h_advance=((double)theFace->glyph->metrics.horiAdvance)/((double)theFace->units_per_EM); n_g.h_width=((double)theFace->glyph->metrics.width)/((double)theFace->units_per_EM); } else { n_g.h_width=n_g.h_advance=((double)(theFace->bbox.xMax-theFace->bbox.xMin))/((double)theFace->units_per_EM); } if ( FT_HAS_VERTICAL(theFace) ) { n_g.v_advance=((double)theFace->glyph->metrics.vertAdvance)/((double)theFace->units_per_EM); n_g.v_width=((double)theFace->glyph->metrics.height)/((double)theFace->units_per_EM); } else { n_g.v_width=n_g.v_advance=((double)theFace->height)/((double)theFace->units_per_EM); } if ( theFace->glyph->format == ft_glyph_format_outline ) { FT_Outline_Funcs ft2_outline_funcs = { ft2_move_to, ft2_line_to, ft2_conic_to, ft2_cubic_to, 0, 0 }; FT2GeomData user(path_builder, 1.0/((double)theFace->units_per_EM)); FT_Outline_Decompose (&theFace->glyph->outline, &ft2_outline_funcs, &user); } doAdd=true; } #endif path_builder.finish(); if ( doAdd ) { Geom::PathVector pv = path_builder.peek(); // close all paths for (Geom::PathVector::iterator i = pv.begin(); i != pv.end(); ++i) { i->close(); } if ( !pv.empty() ) { n_g.pathvector = new Geom::PathVector(pv); Geom::OptRect bounds = bounds_exact(*n_g.pathvector); if (bounds) { n_g.bbox[0] = bounds->left(); n_g.bbox[1] = bounds->top(); n_g.bbox[2] = bounds->right(); n_g.bbox[3] = bounds->bottom(); } } glyphs[nbGlyph]=n_g; id_to_no[glyph_id]=nbGlyph; nbGlyph++; } } else { } }