1
/* This file is part of the KDE project
2
Copyright (C) 2002, The Karbon Developers
4
This library is free software; you can redistribute it and/or
5
modify it under the terms of the GNU Library General Public
6
License as published by the Free Software Foundation; either
7
version 2 of the License, or (at your option) any later version.
9
This library is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
Library General Public License for more details.
14
You should have received a copy of the GNU Library General Public License
15
along with this library; see the file COPYING.LIB. If not, write to
16
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
Boston, MA 02111-1307, USA.
29
#include "vtext_iface.h"
36
#include "commands/vtransformcmd.h"
38
#ifdef HAVE_KARBONTEXT
41
#include <fontconfig/fontconfig.h>
44
#include FT_FREETYPE_H
48
#define FT_TOFLOAT(x) ((x) * (1.0 / 64.0))
49
#define FT_FROMFLOAT(x) ((int) floor ((x) * 64.0 + 0.5))
52
// Trace routines for ttf / ps font -> VSubpath
54
int traceMoveto( FT_Vector *to, VPath *composite )
56
double tox = ( to->x / 64.0 );
57
double toy = ( -to->y / 64.0 );
59
//QString add = "M" + QString::number(tox) + "," + QString::number(toy) + " ";
60
//kdDebug(38000) << add.latin1() << endl;
61
composite->moveTo( KoPoint( tox, toy ) );
66
int traceLineto( FT_Vector *to, VPath *composite )
68
double tox = ( to->x / 64.0 );
69
double toy = ( -to->y / 64.0 );
71
//QString add = "L" + QString::number(tox) + "," + QString::number(toy) + " ";
72
//kdDebug(38000) << add.latin1() << endl;
74
composite->lineTo( KoPoint( tox, toy ) );
79
int traceQuadraticBezier( FT_Vector *control, FT_Vector *to, VPath *composite )
81
double x1 = ( control->x / 64.0 );
82
double y1 = ( -control->y / 64.0 );
83
double x2 = ( to->x / 64.0 );
84
double y2 = ( -to->y / 64.0 );
86
//QString add = "Q" + QString::number(x1) + "," + QString::number(y1) + "," + QString::number(x2) + "," + QString::number(y2) + " ";
87
//kdDebug(38000) << add.latin1() << endl;
88
composite->curveTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), KoPoint( x2, y2 ) );
89
//composite->curve2To( KoPoint( x1, y1 ), KoPoint( x2, y2 ) );
94
int traceCubicBezier( FT_Vector *p, FT_Vector *q, FT_Vector *to, VPath *composite )
96
double x1 = ( p->x / 64.0 );
97
double y1 = ( -p->y / 64.0 );
98
double x2 = ( q->x / 64.0 );
99
double y2 = ( -q->y / 64.0 );
100
double x3 = ( to->x / 64.0 );
101
double y3 = ( -to->y / 64.0 );
103
//QString add = "C" + QString::number(x1) + "," + QString::number(y1) + "," + QString::number(x2) + "," + QString::number(y2) + "," + QString::number(x3) + "," + QString::number(y3);
104
//kdDebug(38000) << add.latin1() << endl;
106
composite->curveTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), KoPoint( x3, y3 ) );
111
FT_Outline_Funcs OutlineMethods =
113
(FT_Outline_MoveTo_Func) traceMoveto,
114
(FT_Outline_LineTo_Func) traceLineto,
115
(FT_Outline_ConicTo_Func) traceQuadraticBezier,
116
(FT_Outline_CubicTo_Func) traceCubicBezier,
121
#endif // HAVE_KARBONTEXT
123
VText::VText( VObject* parent, VState state )
124
: VObject( parent, state ), m_basePath( 0L )
126
m_glyphs.setAutoDelete( true );
127
m_boundingBoxIsInvalid = true;
128
m_stroke = new VStroke( this );
129
m_fill = new VFill();
130
m_position = (VText::Position)0;
131
m_alignment = (VText::Alignment)0;
133
m_translucentShadow = false;
135
m_shadowDistance = 0;
139
VText::VText( const QFont &font, const VSubpath& basePath, Position position, Alignment alignment, const QString& text )
140
: VObject( 0L ), m_font( font ), m_basePath( basePath ), m_position( position ), m_alignment( alignment ), m_text( text )
142
m_glyphs.setAutoDelete( true );
143
m_boundingBoxIsInvalid = true;
144
m_stroke = new VStroke( this );
145
m_fill = new VFill();
148
VText::VText( const VText& text )
149
: VObject( text ), m_font( text.m_font ), m_basePath( text.m_basePath ), m_position( text.m_position ), m_alignment( text.m_alignment ), m_text( text.m_text )
151
m_stroke = new VStroke( *text.m_stroke );
152
m_stroke->setParent( this );
153
m_fill = new VFill( *text.m_fill );
156
VPathListIterator itr( text.m_glyphs );
157
for( ; itr.current() ; ++itr )
159
VPath* c = itr.current()->clone();
160
c->setParent( this );
161
m_glyphs.append( c );
164
m_boundingBoxIsInvalid = true;
171
DCOPObject* VText::dcopObject()
174
m_dcop = new VTextIface( this );
181
VText::draw( VPainter* painter, const KoRect* /*rect*/ ) const
184
state() == deleted ||
186
state() == hidden_locked )
193
VPathListIterator itr( m_glyphs );
195
if( state() != edit )
203
if ( m_translucentShadow )
205
color.set( 0., 0., 0. );
206
color.setOpacity( .3 );
210
color.set( .3, .3, .3 );
211
color.setOpacity( 1. );
213
int shadowDx = int( m_shadowDistance * cos( m_shadowAngle / 360. * 6.2832 ) );
214
int shadowDy = int( m_shadowDistance * sin( m_shadowAngle / 360. * 6.2832 ) );
216
VTransformCmd trafo( 0L, QWMatrix() );
217
for( itr.toFirst(); itr.current(); ++itr )
219
trafo.setMatrix( QWMatrix( 1, 0, 0, 1, shadowDx, shadowDy ) );
220
trafo.visit( *( itr.current() ) );
221
itr.current()->setFill( VFill( color ) );
222
itr.current()->setStroke( VStroke( color ) );
223
itr.current()->draw( painter );
224
trafo.setMatrix( QWMatrix( 1, 0, 0, 1, -shadowDx, -shadowDy ) );
225
trafo.visit( *( itr.current() ) );
229
for( itr.toFirst(); itr.current(); ++itr )
231
itr.current()->setFill( *m_fill );
232
itr.current()->setStroke( *m_stroke );
233
itr.current()->draw( painter );
237
// draw simplistic contour:
238
if( state() == edit )//|| state() == selected )
241
painter->setRasterOp( Qt::XorROP );
242
painter->setPen( Qt::yellow );
243
painter->setBrush( Qt::NoBrush );
245
for( itr.toFirst(); itr.current(); ++itr )
246
itr.current()->draw( painter );
248
painter->strokePath();
255
VText::boundingBox() const
257
if( m_boundingBoxIsInvalid )
259
VPathListIterator itr( m_glyphs );
262
m_boundingBox = itr.current() ? itr.current()->boundingBox() : KoRect();
263
for( ++itr; itr.current(); ++itr )
264
if( !itr.current()->boundingBox().isEmpty() )
265
m_boundingBox |= itr.current()->boundingBox();
267
// take line width into account:
268
m_boundingBox.setCoords(
269
m_boundingBox.left() - 0.5 * stroke()->lineWidth(),
270
m_boundingBox.top() - 0.5 * stroke()->lineWidth(),
271
m_boundingBox.right() + 0.5 * stroke()->lineWidth(),
272
m_boundingBox.bottom() + 0.5 * stroke()->lineWidth() );
274
m_boundingBoxIsInvalid = false;
277
return m_boundingBox;
283
return new VText( *this );
286
VGroup* VText::toVGroup() const
288
VGroup* group = new VGroup( parent() );
290
VPathListIterator itr( m_glyphs );
291
for( itr.toFirst(); itr.current(); ++itr )
293
VPath* c = itr.current()->clone();
294
c->setParent( group );
298
group->setFill( *m_fill );
299
group->setStroke( *m_stroke );
305
VText::save( QDomElement& element ) const
307
if( state() != deleted )
309
QDomElement me = element.ownerDocument().createElement( "TEXT" );
311
m_basePath.save( me );
315
// save font properties
316
me.setAttribute( "text", m_text );
317
me.setAttribute( "family", m_font.family() );
318
me.setAttribute( "size", m_font.pointSize() );
319
me.setAttribute( "italic", m_font.italic() );
320
me.setAttribute( "bold", m_font.bold() );
321
me.setAttribute( "position", m_position );
322
me.setAttribute( "alignment", m_alignment );
323
me.setAttribute( "shadow", m_shadow );
324
me.setAttribute( "translucentshadow", m_translucentShadow );
325
me.setAttribute( "shadowangle", m_shadowAngle );
326
me.setAttribute( "shadowdist", m_shadowDistance );
328
element.appendChild( me );
330
// save all glyphs / paths
331
VPathListIterator itr = m_glyphs;
332
for( itr.toFirst(); itr.current(); ++itr )
333
itr.current()->save( me );
338
VText::load( const QDomElement& element )
342
m_font.setFamily( element.attribute( "family", "Times" ) );
343
m_font.setPointSize( element.attribute( "size", "12" ).toInt() );
344
m_font.setItalic( element.attribute( "italic" ) == 0 ? false : true );
345
m_font.setWeight( QFont::Normal );
346
m_font.setBold( element.attribute( "bold" ) == 0 ? false : true );
347
m_position = (Position)element.attribute( "position", "0" ).toInt();
348
m_alignment = (Alignment)element.attribute( "alignment", "0" ).toInt();
349
m_shadow = ( element.attribute( "shadow" ).toInt() == 1 );
350
m_translucentShadow = ( element.attribute( "translucentshadow" ).toInt() == 1 );
351
m_shadowAngle = element.attribute( "shadowangle" ).toInt();
352
m_shadowDistance = element.attribute( "shadowdist" ).toInt();
354
m_text = element.attribute( "text", "" );
356
QDomNodeList list = element.childNodes();
357
QDomElement e = list.item( 0 ).toElement();
358
if( e.tagName() == "PATH" )
359
m_basePath.load( e );
362
for( uint i = 1; i < list.count(); ++i )
364
if( list.item( i ).isElement() )
366
e = list.item( i ).toElement();
367
if( e.tagName() == "PATH" )
369
VPath *composite = new VPath( this );
370
composite->load( e );
371
m_glyphs.append( composite );
373
if( e.tagName() == "STROKE" )
375
if( e.tagName() == "FILL" )
379
// if no glyphs yet, trace them
380
#ifdef HAVE_KARBONTEXT
381
if( m_glyphs.count() == 0 )
384
m_boundingBoxIsInvalid = true;
385
//m_fill->setFillRule( VFill::evenOdd );
389
VText::setText( const QString& text )
395
#ifdef HAVE_KARBONTEXT
402
VText::setState( const VState state )
404
VObject::setState( state );
406
VPathListIterator itr( m_glyphs );
407
for( itr.toFirst(); itr.current(); ++itr )
409
itr.current()->setState( state );
414
VText::accept( VVisitor& visitor )
416
visitor.visitVText( *this );
419
#ifdef HAVE_KARBONTEXT
424
if( m_basePath.count() == 0 )
426
kdDebug(38000) << "Can't draw a text without base path (was: " << m_text << ")." << endl;
430
// TODO : set more options
431
int slant = FC_SLANT_ROMAN;
432
if( m_font.italic() )
433
slant = FC_SLANT_ITALIC;
437
weight = FC_WEIGHT_BOLD;
439
// Build FontConfig request pattern
441
QString filename = buildRequest( m_font.family(), weight, slant, m_font.pointSize(), id );
444
kdDebug(38000) << "Loading " << filename.latin1() << " for requested font \"" << m_font.family().latin1() << "\", " << m_font.pointSize() << " pt." << endl;
448
// TODO : this lib should probably be a singleton (Rob)
450
FT_Init_FreeType( &library );
451
FT_Error error = FT_New_Face( library, QFile::encodeName(filename), id, &fontFace );
455
kdDebug(38000) << "traceText(), could not load font. Aborting!" << endl;
459
// Choose unicode charmap
460
for( int charmap = 0; charmap < fontFace->num_charmaps; charmap++ )
462
if( fontFace->charmaps[charmap]->encoding == ft_encoding_unicode )
464
FT_Error error = FT_Set_Charmap( fontFace, fontFace->charmaps[charmap] );
467
kdDebug(38000) << "traceText(), unable to select unicode charmap. Aborting!" << endl;
468
FT_Done_Face( fontFace );
474
FT_Set_Char_Size( fontFace, FT_FROMFLOAT( m_font.pointSize() ), FT_FROMFLOAT( m_font.pointSize() ), 0, 0 );
478
QValueList<float> glyphXAdvance;
479
QValueList<float> glyphYAdvance;
480
for( unsigned int i = 0; i < m_text.length(); i++ )
482
// get the glyph index for the current character
483
QChar character = m_text.at( i );
484
glyphIndex = FT_Get_Char_Index( fontFace, character.unicode() );
485
//kdDebug(38000) << "glyphIndex : " << glyphIndex << endl;
486
FT_Error error = FT_Load_Glyph( fontFace, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP );
489
kdDebug(38000) << "Houston, we have a problem : " << error << endl;
493
// decompose to vpaths
495
FT_Get_Glyph( fontFace->glyph, reinterpret_cast<FT_Glyph *>( &g ) );
496
VPath *composite = new VPath( this );
497
//VFill *fill = composite->fill();
498
//fill->setFillRule( VFill::evenOdd );
499
//composite->setFill( *fill );
500
FT_Outline_Decompose(&g->outline, &OutlineMethods, composite );
501
//composite->close();
503
m_glyphs.append( composite );
504
glyphXAdvance.append( FT_TOFLOAT( fontFace->glyph->advance.x ) );
505
glyphYAdvance.append( FT_TOFLOAT( fontFace->glyph->advance.y ) );
506
l += FT_TOFLOAT( fontFace->glyph->advance.x );
509
// Placing the stored glyphs.
510
float pathLength = 0;
511
VSubpathIterator pIt( m_basePath );
513
while ( ( seg = ++pIt ) )
514
pathLength += seg->length();
516
switch( m_alignment )
518
case Left: x = 0; break;
519
case Center: x = ( pathLength - l ) / 2; break;
520
case Right: x = pathLength - l; break;
528
VSubpathIterator pathIt( m_basePath );
529
VSegment* oldSeg = pathIt.current();
534
float yoffset = ( m_position == Above ? 0 : ( m_position == On ? m_font.pointSize() / 3 : m_font.pointSize() / 1.5 ) );
535
kdDebug(38000) << "Position: " << m_position << " -> " << yoffset << endl;
536
for( unsigned int i = 0; i < m_text.length(); i++ )
538
VPath* composite = m_glyphs.at( i );
540
// Step 1: place (0, 0) to the rotation center of the glyph.
541
dx = *glyphXAdvance.at( i ) / 2;
543
VTransformCmd trafo( 0L, QWMatrix( 1, 0, 0, 1, -dx, y + yoffset ) );
544
trafo.visit( *composite );
546
// Step 2: find the position where to draw.
547
// 3 possibilities: before, on, and after the basePath...
551
seg->pointTangentNormalAt( 0, &extPoint, &tangent, &normal );
552
point = extPoint + x * tangent;
557
while ( seg && x > fsx + seg->length() )
559
fsx += seg->length();
566
sp = ( x - fsx ) / seg->length();
567
seg->pointTangentNormalAt( sp, &point, &tangent, &normal );
572
oldSeg->pointTangentNormalAt( 1, &extPoint, &tangent, &normal );
573
point = extPoint + ( x - fsx ) * tangent;
578
// Step 3: transform glyph and append it. That's it, we've got
579
// text following a path. Really easy, isn't it ;) ?
580
trafo.setMatrix( QWMatrix( tangent.x(), tangent.y(), tangent.y(), -tangent.x(), point.x(), point.y() ) );
581
trafo.visit( *composite );
582
composite->setState( state() );
584
//kdDebug(38000) << "Glyph: " << (QString)character << " [String pos: " << x << ", " << y << " / Canvas pos: " << point.x() << ", " << point.y() << "]" << endl;
587
y += *glyphYAdvance.at( i );
589
FT_Done_Face( fontFace );
590
FT_Done_FreeType( library );
591
m_boundingBoxIsInvalid = true;
594
// This routine is copied from KSVGFont (Rob)
596
VText::buildRequest( QString family, int weight, int slant, double size, int &id )
598
// Strip those stupid [Xft or whatever]...
600
if( ( pos = family.find( '[' ) ) )
601
family = family.left( pos );
603
// Use FontConfig to locate & select fonts and use FreeType2 to open them
607
pattern = FcPatternBuild( 0, FC_WEIGHT, FcTypeInteger, weight,
608
FC_SLANT, FcTypeInteger, slant,
609
FC_SIZE, FcTypeDouble, size, 0 );
612
FcPatternAddString( pattern, FC_FAMILY, reinterpret_cast<const FcChar8 *>( family.latin1() ) );
615
FcPatternAddBool( pattern, FC_HINTING, false );
617
// Perform the default font pattern modification operations.
618
FcDefaultSubstitute( pattern );
619
FcConfigSubstitute( FcConfigGetCurrent(), pattern, FcMatchPattern );
621
// Match the pattern!
623
FcPattern *match = FcFontMatch( 0, pattern, &result );
626
FcPatternDestroy( pattern );
628
// Get index & filename
633
FcPattern *pattern = FcPatternDuplicate( match );
635
// Get index & filename
636
if( FcPatternGetString(pattern, FC_FILE, 0, &temp) != FcResultMatch ||
637
FcPatternGetInteger(pattern, FC_INDEX, 0, &id) != FcResultMatch )
639
kdDebug(38000) << "VText::buildRequest(), could not load font file for requested font \"" << family.latin1() << "\"" << endl;
640
return QString::null;
643
fileName = QFile::decodeName(reinterpret_cast<const char *>( temp ));
646
FcPatternDestroy( pattern );
650
FcPatternDestroy( match );
655
#endif // HAVE_KARBONTEXT