You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/kpovmodeler/pmtruetypecache.cpp

396 lines
10 KiB

/*
**************************************************************************
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 TQString& 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 TQString& 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( TQChar c )
{
PMTrueTypeOutline* ol = 0;
if( isValid( ) )
{
TQString 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( TQChar c1, TQChar 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( TQChar 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 TQString& )
{
return 0;
}
PMTrueTypeFont::PMTrueTypeFont( )
{
}
bool PMTrueTypeFont::isValid( )
{
return false;
}
PMTrueTypeOutline* PMTrueTypeFont::outline( TQChar )
{
return 0;
}
double PMTrueTypeFont::kerning( TQChar, TQChar )
{
return 0;
}
#endif //HAVE_FREETYPE