diff options
Diffstat (limited to 'kpovmodeler/pmtruetypecache.cpp')
-rw-r--r-- | kpovmodeler/pmtruetypecache.cpp | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/kpovmodeler/pmtruetypecache.cpp b/kpovmodeler/pmtruetypecache.cpp new file mode 100644 index 00000000..9aa28d89 --- /dev/null +++ b/kpovmodeler/pmtruetypecache.cpp @@ -0,0 +1,395 @@ +/* +************************************************************************** + description + -------------------- + copyright : (C) 2002 by Andreas Zehender + email : zehender@kde.org +************************************************************************** + +************************************************************************** +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +**************************************************************************/ + +#include "pmtruetypecache.h" +#include "pmdebug.h" + +//*********************************************************************** +// Part with freetype support +//*********************************************************************** + +#ifdef HAVE_FREETYPE +#define PMFREETYPEDEBUG + +PMTrueTypeCache* PMTrueTypeCache::s_pInstance = 0; +KStaticDeleter<PMTrueTypeCache> PMTrueTypeCache::s_staticDeleter; + +PMTrueTypeCache::PMTrueTypeCache( ) + : m_cache( 10, 17, true ) +{ + bool error = FT_Init_FreeType( &m_library ); + if( error ) + kdError( PMArea ) << "Failed to initialize the freetype library\n"; + +#ifdef PMFREETYPEDEBUG + else + kdDebug( PMArea ) << "Freetype 2 initialized\n"; +#endif + + m_cache.setAutoDelete( true ); +} + +PMTrueTypeCache::~PMTrueTypeCache( ) +{ + m_cache.clear( ); + if( m_library ) + FT_Done_FreeType( m_library ); +} + +PMTrueTypeFont* PMTrueTypeCache::lookUp( const QString& file ) +{ + if( !m_library ) + return 0; + if( file.isEmpty( ) ) + return 0; + + PMTrueTypeFont* f = m_cache.find( file ); + if( !f ) + { + FT_Face face; + FT_New_Face( m_library, file.latin1( ), 0, &face ); + f = new PMTrueTypeFont( m_library, face ); + +#ifdef PMFREETYPEDEBUG + if( face ) + kdDebug( PMArea ) << "Successfully opened font " << file << endl; + + if( f->isValid( ) ) + m_cache.insert( file, f, 1 ); + else + m_cache.insert( file, f, 0 ); +#endif + } + + if( f->isValid( ) ) + return f; + + return 0; +} + +PMTrueTypeFont* PMTrueTypeCache::font( const QString& file ) +{ + if( !s_pInstance ) + s_staticDeleter.setObject( s_pInstance, new PMTrueTypeCache( ) ); + + return s_pInstance->lookUp( file ); +} + +PMTrueTypeFont::PMTrueTypeFont( FT_Library lib, FT_Face face ) + : m_cache( 100, 127 ) +{ + m_library = lib; + m_face = face; + m_valid = false; + m_validChecked = false; + m_useKerning = false; + if( m_face ) + { + m_useKerning = FT_HAS_KERNING( m_face ); + // find the correct encoding + int i; + bool found = false; + for( i = 0; ( i < m_face->num_charmaps ) && !found; i++ ) + if( m_face->charmaps[i]->platform_id == 3 ) // microsoft encodings + FT_Set_Charmap( m_face, m_face->charmaps[i] ); + for( i = 0; ( i < m_face->num_charmaps ) && !found; i++ ) + if( m_face->charmaps[i]->platform_id == 1 ) // mac encodings + FT_Set_Charmap( m_face, m_face->charmaps[i] ); + } + + m_cache.setAutoDelete( true ); +} + +PMTrueTypeFont::~PMTrueTypeFont( ) +{ + if( m_face ) + FT_Done_Face( m_face ); + m_cache.clear( ); +} + +bool PMTrueTypeFont::isValid( ) +{ + if( !m_validChecked ) + { + if( !m_face ) + m_valid = false; + else + m_valid = m_face->face_flags & FT_FACE_FLAG_SCALABLE; + +#ifdef PMFREETYPEDEBUG + if( m_valid ) + kdDebug( PMArea ) << "Font: " << m_face->family_name + << " style " << m_face->style_name + << " units_per_EM " << m_face->units_per_EM + << " height " << m_face->height << endl; +#endif + + m_validChecked = true; + } + + return m_valid; +} + +PMTrueTypeOutline* PMTrueTypeFont::outline( QChar c ) +{ + PMTrueTypeOutline* ol = 0; + + if( isValid( ) ) + { + QString str( c ); + ol = m_cache.find( str ); + if( !ol ) + { + FT_UInt glyphIndex = findGlyphIndex( c ); + + bool error = !glyphIndex; + FT_Glyph glyph = 0; + + if( !error ) + error = FT_Load_Glyph( m_face, glyphIndex, + FT_LOAD_NO_BITMAP | FT_LOAD_NO_SCALE ); + if( !error ) + error = FT_Get_Glyph( m_face->glyph, &glyph ); + +#ifdef PMFREETYPEDEBUG + if( error ) + kdDebug( PMArea ) << "Failed to load glyph for " << c.latin1( ) << "\n"; + else + { + FT_Glyph_Metrics* m = &( m_face->glyph->metrics ); + kdDebug( PMArea ) << "Glyph w: " << m->width << " h: " << m->height + << " hbx: " << m->horiBearingX << " hby: " << m->horiBearingY + << " ha: " << m->horiAdvance << endl; + } +#endif + + if( !error && glyph && ( glyph->format == ft_glyph_format_outline ) ) + { + FT_OutlineGlyph outlineGlyph = ( FT_OutlineGlyph ) glyph; + ol = new PMTrueTypeOutline( outlineGlyph, m_face ); + } + + if( glyph ) + FT_Done_Glyph( glyph ); + if( ol ) + m_cache.insert( str, ol ); + } + } + + return ol; +} + +double PMTrueTypeFont::kerning( QChar c1, QChar c2 ) +{ + double k = 0.0; + if( m_useKerning && !c1.isNull( ) && !c2.isNull( ) ) + { + FT_UInt gi1 = findGlyphIndex( c1 ); + FT_UInt gi2 = findGlyphIndex( c2 ); + if( gi1 && gi2 ) + { + FT_Vector delta; + FT_Get_Kerning( m_face, gi1, gi2, ft_kerning_unscaled, &delta ); + k = ( double ) delta.x / ( double ) m_face->units_per_EM; + } + } + + return k; +} + +FT_UInt PMTrueTypeFont::findGlyphIndex( QChar c ) +{ + FT_UInt glyphIndex = 0; + + if( m_face ) + { + // glyphIndex = FT_Get_Char_Index( m_face, c.unicode( ) ); + // if( !glyphIndex ) + char ch = c.latin1( ); + if( !ch ) + ch = '?'; + glyphIndex = FT_Get_Char_Index( m_face, ch ); + } + return glyphIndex; +} + +PMTrueTypeOutline::PMTrueTypeOutline( FT_OutlineGlyph glyph, FT_Face face ) +{ + int n = 0, p = 0, si; + FT_Outline* ol = &( glyph->outline ); + + PMVector v[4]; + bool onPoint[4] = { false, false, false, false }; + bool cubic[4] = { false, false, false, false }; + + double dfh = ( double ) face->units_per_EM; + double horiBearing = ( double ) face->glyph->metrics.horiBearingX / dfh; + + m_segments = 0; + m_contours = ol->n_contours; + m_advance = ( double ) face->glyph->metrics.horiAdvance / dfh; + +#ifdef PMFREETYPEDEBUG + + /** + kdDebug( PMArea ) << "New outline:\n"; + int dn, dp = 0; + for( dn = 0; dn < m_contours; dn++ ) + { + kdDebug( PMArea ) << " Contour " << dn << ":\n"; + for( ; dp <= ol->contours[dn]; dp++ ) + { + kdDebug( PMArea ) << " <" << ol->points[dp].x << ", " + << ol->points[dp].y << ">, " + << ( ( ol->tags[dp] & 1 ) == 1 ) << " " + << ( ( ol->tags[dp] & 2 ) == 2 ) << endl; + } + } + */ + +#endif + + for( n = 0; n < m_contours; n++ ) + { + PMSegmentList os; + PMSplineSegment s; + bool segmentCreated = false; + bool quadricSpecialCase = false; + bool contourEnd = false; + int firstPoint = p; + + si = 0; + + for( ; !contourEnd; p++, si++ ) + { + segmentCreated = false; + quadricSpecialCase = false; + + // last point = first point + if( p > ol->contours[n] ) + { + p = firstPoint; + contourEnd = true; + } + + // scale the point + v[si] = PMVector( ( double ) ol->points[p].x / dfh - horiBearing, + ( double ) ol->points[p].y / dfh ); + // point type + onPoint[si] = ( ( ol->tags[p] & 1 ) == 1 ); + cubic[si] = ( ( ol->tags[p] & 2 ) == 2 ); + + if( onPoint[si] ) + { + switch( si ) + { + case 0: + break; + case 1: + // line + s.calculateLinear( v[0], v[1] ); + segmentCreated = true; + break; + case 2: + // quadric bezier + s.calculateQuadricBezier( v[0], v[1], v[2] ); + segmentCreated = true; + break; + case 3: + // cubic bezier + s.calculateBezier( v[0], v[1], v[2], v[3] ); + segmentCreated = true; + break; + default: + kdError( PMArea ) << "Glyph outline seems incorrect. No on point.\n"; + si = 0; + break; + } + } + else if( ( si == 2 ) && ( !cubic[si] ) ) + { + // two quadric off points + // add an on point between them + v[3] = v[2]; + onPoint[3] = onPoint[2]; + cubic[3] = cubic[2]; + v[2] = ( v[1] + v[3] ) / 2.0; + onPoint[2] = true; + + s.calculateQuadricBezier( v[0], v[1], v[2] ); + segmentCreated = true; + quadricSpecialCase = true; + } + + if( segmentCreated ) + { + os.append( s ); + v[0] = v[si]; + onPoint[0] = true; + si = 0; + + if( quadricSpecialCase ) + { + v[1] = v[3]; + onPoint[1] = onPoint[3]; + cubic[1] = onPoint[3]; + si++; + } + } + } + + m_outline.append( os ); + m_segments += os.count( ); + p = ol->contours[n] + 1; + } +} + +#else //!HAVE_FREETYPE + +//*********************************************************************** +// Part without freetype support +//*********************************************************************** + +PMTrueTypeCache::PMTrueTypeCache( ) +{ +} + +PMTrueTypeFont* PMTrueTypeCache::font( const QString& ) +{ + return 0; +} + +PMTrueTypeFont::PMTrueTypeFont( ) +{ +} + +bool PMTrueTypeFont::isValid( ) +{ + return false; +} + +PMTrueTypeOutline* PMTrueTypeFont::outline( QChar ) +{ + return 0; +} + +double PMTrueTypeFont::kerning( QChar, QChar ) +{ + return 0; +} + +#endif //HAVE_FREETYPE |