|
|
|
/*
|
|
|
|
**************************************************************************
|
|
|
|
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.tqunicode( ) );
|
|
|
|
// 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
|