00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <qdom.h>
00021 #include <qfile.h>
00022
00023 #include <kdebug.h>
00024 #include <KoPoint.h>
00025 #include <KoRect.h>
00026
00027 #include "vpath.h"
00028 #include "vtext.h"
00029 #include "vtext_iface.h"
00030 #include "vstroke.h"
00031 #include "vfill.h"
00032 #include "vvisitor.h"
00033 #include "vsegment.h"
00034 #include "vgroup.h"
00035 #include "vpainter.h"
00036 #include "commands/vtransformcmd.h"
00037
00038 #ifdef HAVE_KARBONTEXT
00039
00040 #include <ft2build.h>
00041 #include <fontconfig/fontconfig.h>
00042
00043
00044 #include FT_FREETYPE_H
00045 #include FT_OUTLINE_H
00046 #include FT_GLYPH_H
00047
00048 #define FT_TOFLOAT(x) ((x) * (1.0 / 64.0))
00049 #define FT_FROMFLOAT(x) ((int) floor ((x) * 64.0 + 0.5))
00050
00051
00052
00053
00054 int traceMoveto( FT_Vector *to, VPath *composite )
00055 {
00056 double tox = ( to->x / 64.0 );
00057 double toy = ( -to->y / 64.0 );
00058
00059
00060
00061 composite->moveTo( KoPoint( tox, toy ) );
00062
00063 return 0;
00064 }
00065
00066 int traceLineto( FT_Vector *to, VPath *composite )
00067 {
00068 double tox = ( to->x / 64.0 );
00069 double toy = ( -to->y / 64.0 );
00070
00071
00072
00073
00074 composite->lineTo( KoPoint( tox, toy ) );
00075
00076 return 0;
00077 }
00078
00079 int traceQuadraticBezier( FT_Vector *control, FT_Vector *to, VPath *composite )
00080 {
00081 double x1 = ( control->x / 64.0 );
00082 double y1 = ( -control->y / 64.0 );
00083 double x2 = ( to->x / 64.0 );
00084 double y2 = ( -to->y / 64.0 );
00085
00086
00087
00088 composite->curveTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), KoPoint( x2, y2 ) );
00089
00090
00091 return 0;
00092 }
00093
00094 int traceCubicBezier( FT_Vector *p, FT_Vector *q, FT_Vector *to, VPath *composite )
00095 {
00096 double x1 = ( p->x / 64.0 );
00097 double y1 = ( -p->y / 64.0 );
00098 double x2 = ( q->x / 64.0 );
00099 double y2 = ( -q->y / 64.0 );
00100 double x3 = ( to->x / 64.0 );
00101 double y3 = ( -to->y / 64.0 );
00102
00103
00104
00105
00106 composite->curveTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), KoPoint( x3, y3 ) );
00107
00108 return 0;
00109 }
00110
00111 FT_Outline_Funcs OutlineMethods =
00112 {
00113 (FT_Outline_MoveTo_Func) traceMoveto,
00114 (FT_Outline_LineTo_Func) traceLineto,
00115 (FT_Outline_ConicTo_Func) traceQuadraticBezier,
00116 (FT_Outline_CubicTo_Func) traceCubicBezier,
00117 0,
00118 0
00119 };
00120
00121 #endif // HAVE_KARBONTEXT
00122
00123 VText::VText( VObject* parent, VState state )
00124 : VObject( parent, state ), m_basePath( 0L )
00125 {
00126 m_glyphs.setAutoDelete( true );
00127 m_boundingBoxIsInvalid = true;
00128 m_stroke = new VStroke( this );
00129 m_fill = new VFill();
00130 m_position = (VText::Position)0;
00131 m_alignment = (VText::Alignment)0;
00132 m_shadow = false;
00133 m_translucentShadow = false;
00134 m_shadowAngle = 0;
00135 m_shadowDistance = 0;
00136 m_offset = 0.0;
00137 }
00138
00139
00140 VText::VText( const QFont &font, const VSubpath& basePath, Position position, Alignment alignment, const QString& text )
00141 : VObject( 0L ), m_font( font ), m_basePath( basePath ), m_position( position ), m_alignment( alignment ), m_text( text )
00142 {
00143 m_glyphs.setAutoDelete( true );
00144 m_boundingBoxIsInvalid = true;
00145 m_stroke = new VStroke( this );
00146 m_fill = new VFill();
00147 m_offset = 0.0;
00148 }
00149
00150 VText::VText( const VText& text )
00151 : 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 ), m_shadow( text.m_shadow ), m_translucentShadow( text.m_translucentShadow ), m_shadowDistance( text.m_shadowDistance ), m_shadowAngle( text.m_shadowAngle ), m_offset( text.m_offset )
00152 {
00153 m_stroke = new VStroke( *text.m_stroke );
00154 m_stroke->setParent( this );
00155 m_fill = new VFill( *text.m_fill );
00156
00157
00158 VPathListIterator itr( text.m_glyphs );
00159 for( ; itr.current() ; ++itr )
00160 {
00161 VPath* c = itr.current()->clone();
00162 c->setParent( this );
00163 m_glyphs.append( c );
00164 }
00165
00166 m_boundingBoxIsInvalid = true;
00167 }
00168
00169 VText::~VText()
00170 {
00171 }
00172
00173 DCOPObject* VText::dcopObject()
00174 {
00175 if( !m_dcop )
00176 m_dcop = new VTextIface( this );
00177
00178 return m_dcop;
00179 }
00180
00181
00182 void
00183 VText::draw( VPainter* painter, const KoRect* ) const
00184 {
00185 if(
00186 state() == deleted ||
00187 state() == hidden ||
00188 state() == hidden_locked )
00189 {
00190 return;
00191 }
00192
00193 painter->save();
00194
00195 VPathListIterator itr( m_glyphs );
00196
00197 if( state() != edit )
00198 {
00199
00200 painter->newPath();
00201
00202 if ( m_shadow )
00203 {
00204 VColor color;
00205 if ( m_translucentShadow )
00206 {
00207 color.set( 0., 0., 0. );
00208 color.setOpacity( .3 );
00209 }
00210 else
00211 {
00212 color.set( .3, .3, .3 );
00213 color.setOpacity( 1. );
00214 }
00215 int shadowDx = int( m_shadowDistance * cos( m_shadowAngle / 360. * 6.2832 ) );
00216 int shadowDy = int( m_shadowDistance * sin( m_shadowAngle / 360. * 6.2832 ) );
00217
00218 VTransformCmd trafo( 0L, QWMatrix() );
00219 for( itr.toFirst(); itr.current(); ++itr )
00220 {
00221 trafo.setMatrix( QWMatrix( 1, 0, 0, 1, shadowDx, shadowDy ) );
00222 trafo.visit( *( itr.current() ) );
00223 itr.current()->setFill( VFill( color ) );
00224 itr.current()->setStroke( VStroke( color ) );
00225 itr.current()->draw( painter );
00226 trafo.setMatrix( QWMatrix( 1, 0, 0, 1, -shadowDx, -shadowDy ) );
00227 trafo.visit( *( itr.current() ) );
00228 }
00229 }
00230
00231 for( itr.toFirst(); itr.current(); ++itr )
00232 {
00233 itr.current()->setFill( *m_fill );
00234 itr.current()->setStroke( *m_stroke );
00235 itr.current()->draw( painter );
00236 }
00237 }
00238
00239
00240 if( state() == edit )
00241 {
00242 painter->newPath();
00243 painter->setRasterOp( Qt::XorROP );
00244 painter->setPen( Qt::yellow );
00245 painter->setBrush( Qt::NoBrush );
00246
00247 for( itr.toFirst(); itr.current(); ++itr )
00248 itr.current()->draw( painter );
00249
00250 painter->strokePath();
00251 }
00252
00253 painter->restore();
00254 }
00255
00256 const KoRect&
00257 VText::boundingBox() const
00258 {
00259 if( m_boundingBoxIsInvalid )
00260 {
00261 VPathListIterator itr( m_glyphs );
00262 itr.toFirst();
00263
00264 m_boundingBox = itr.current() ? itr.current()->boundingBox() : KoRect();
00265 for( ++itr; itr.current(); ++itr )
00266 if( !itr.current()->boundingBox().isEmpty() )
00267 m_boundingBox |= itr.current()->boundingBox();
00268
00269
00270 m_boundingBox.setCoords(
00271 m_boundingBox.left() - 0.5 * stroke()->lineWidth(),
00272 m_boundingBox.top() - 0.5 * stroke()->lineWidth(),
00273 m_boundingBox.right() + 0.5 * stroke()->lineWidth(),
00274 m_boundingBox.bottom() + 0.5 * stroke()->lineWidth() );
00275
00276 m_boundingBoxIsInvalid = false;
00277 }
00278
00279 return m_boundingBox;
00280 }
00281
00282 VText*
00283 VText::clone() const
00284 {
00285 return new VText( *this );
00286 }
00287
00288 VGroup* VText::toVGroup() const
00289 {
00290 VGroup* group = new VGroup( parent() );
00291
00292 VPathListIterator itr( m_glyphs );
00293 for( itr.toFirst(); itr.current(); ++itr )
00294 {
00295 VPath* c = itr.current()->clone();
00296 c->setParent( group );
00297 group->append( c );
00298 }
00299
00300 group->setFill( *m_fill );
00301 group->setStroke( *m_stroke );
00302
00303 return group;
00304 }
00305
00306 void
00307 VText::save( QDomElement& element ) const
00308 {
00309 if( state() != deleted )
00310 {
00311 QDomElement me = element.ownerDocument().createElement( "TEXT" );
00312
00313 VPath path( 0L );
00314 path.combinePath( m_basePath );
00315 path.save( me );
00316
00317 VObject::save( me );
00318
00319
00320 me.setAttribute( "text", m_text );
00321 me.setAttribute( "family", m_font.family() );
00322 me.setAttribute( "size", m_font.pointSize() );
00323 me.setAttribute( "italic", m_font.italic() );
00324 me.setAttribute( "bold", m_font.bold() );
00325 me.setAttribute( "position", m_position );
00326 me.setAttribute( "alignment", m_alignment );
00327 me.setAttribute( "shadow", m_shadow );
00328 me.setAttribute( "translucentshadow", m_translucentShadow );
00329 me.setAttribute( "shadowangle", m_shadowAngle );
00330 me.setAttribute( "shadowdist", m_shadowDistance );
00331 me.setAttribute( "offset", m_offset );
00332 element.appendChild( me );
00333
00334
00335 VPathListIterator itr = m_glyphs;
00336 for( itr.toFirst(); itr.current(); ++itr )
00337 itr.current()->save( me );
00338 }
00339 }
00340
00341 void
00342 VText::load( const QDomElement& element )
00343 {
00344 m_glyphs.clear();
00345
00346 m_font.setFamily( element.attribute( "family", "Times" ) );
00347 m_font.setPointSize( element.attribute( "size", "12" ).toInt() );
00348 m_font.setItalic( element.attribute( "italic" ).toInt() == 1 );
00349 m_font.setWeight( QFont::Normal );
00350 m_font.setBold( element.attribute( "bold" ).toInt() == 1 );
00351 m_position = (Position)element.attribute( "position", "0" ).toInt();
00352 m_alignment = (Alignment)element.attribute( "alignment", "0" ).toInt();
00353 m_shadow = ( element.attribute( "shadow" ).toInt() == 1 );
00354 m_translucentShadow = ( element.attribute( "translucentshadow" ).toInt() == 1 );
00355 m_shadowAngle = element.attribute( "shadowangle" ).toInt();
00356 m_shadowDistance = element.attribute( "shadowdist" ).toInt();
00357 m_offset = element.attribute( "offset" ).toDouble();
00358 m_text = element.attribute( "text", "" );
00359
00360 VObject::load( element );
00361
00362 QDomNodeList list = element.childNodes();
00363 QDomElement e = list.item( 0 ).toElement();
00364
00365
00366 uint startElement = 0;
00367
00368 if( e.tagName() == "PATH" )
00369 {
00370 VPath path( 0L );
00371 path.load( e );
00372 m_basePath = *path.paths().getFirst();
00373 startElement++;
00374 }
00375
00376
00377 for( uint i = startElement; i < list.count(); ++i )
00378 {
00379 if( list.item( i ).isElement() )
00380 {
00381 e = list.item( i ).toElement();
00382 if( e.tagName() == "PATH" )
00383 {
00384 VPath *composite = new VPath( this );
00385 composite->load( e );
00386 m_glyphs.append( composite );
00387 }
00388 if( e.tagName() == "STROKE" )
00389 m_stroke->load( e );
00390 if( e.tagName() == "FILL" )
00391 m_fill->load( e );
00392 }
00393 }
00394
00395 #ifdef HAVE_KARBONTEXT
00396 if( m_glyphs.count() == 0 )
00397 traceText();
00398 #endif
00399 m_boundingBoxIsInvalid = true;
00400
00401 }
00402
00403 void
00404 VText::setText( const QString& text )
00405 {
00406 if( m_text != text )
00407 {
00408 m_text = text;
00409 m_glyphs.clear();
00410 #ifdef HAVE_KARBONTEXT
00411 traceText();
00412 #endif
00413 }
00414 }
00415
00416 void
00417 VText::setState( const VState state )
00418 {
00419 VObject::setState( state );
00420
00421 VPathListIterator itr( m_glyphs );
00422 for( itr.toFirst(); itr.current(); ++itr )
00423 {
00424 itr.current()->setState( state );
00425 }
00426 }
00427
00428 void
00429 VText::accept( VVisitor& visitor )
00430 {
00431 visitor.visitVText( *this );
00432 }
00433
00434 #ifdef HAVE_KARBONTEXT
00435
00436 void
00437 VText::traceText()
00438 {
00439 if( m_basePath.count() == 0 )
00440 {
00441 kdDebug(38000) << "Can't draw a text without base path (was: " << m_text << ")." << endl;
00442 return;
00443 }
00444
00445
00446 int slant = FC_SLANT_ROMAN;
00447 if( m_font.italic() )
00448 slant = FC_SLANT_ITALIC;
00449
00450 int weight = 0;
00451 if( m_font.bold() )
00452 weight = FC_WEIGHT_BOLD;
00453
00454
00455 int id = -1;
00456 QString filename = buildRequest( m_font.family(), weight, slant, m_font.pointSize(), id );
00457 m_glyphs.clear();
00458
00459 kdDebug(38000) << "Loading " << filename.latin1() << " for requested font \"" << m_font.family().latin1() << "\", " << m_font.pointSize() << " pt." << endl;
00460
00461 FT_UInt glyphIndex;
00462 FT_Face fontFace;
00463
00464 FT_Library library;
00465 FT_Init_FreeType( &library );
00466 FT_Error error = FT_New_Face( library, QFile::encodeName(filename), id, &fontFace );
00467
00468 if( error )
00469 {
00470 kdDebug(38000) << "traceText(), could not load font. Aborting!" << endl;
00471 return;
00472 }
00473
00474 bool foundCharmap = false;
00475
00476
00477 for( int charmap = 0; charmap < fontFace->num_charmaps; charmap++ )
00478 {
00479 if( fontFace->charmaps[charmap]->encoding == ft_encoding_unicode )
00480 {
00481 FT_Error error = FT_Set_Charmap( fontFace, fontFace->charmaps[charmap] );
00482 if( error )
00483 {
00484 kdDebug(38000) << "traceText(), unable to select unicode charmap." << endl;
00485 continue;
00486 }
00487 foundCharmap = true;
00488 }
00489 }
00490
00491
00492 if( ! foundCharmap )
00493 {
00494 error = FT_Set_Charmap( fontFace, fontFace->charmaps[0] );
00495 if( error )
00496 {
00497 kdDebug(38000) << "traceText(), unable to select charmap. Aborting!" << endl;
00498 FT_Done_Face( fontFace );
00499 FT_Done_FreeType( library );
00500 return;
00501 }
00502 }
00503
00504 error = FT_Set_Char_Size( fontFace, FT_FROMFLOAT( m_font.pointSize() ), FT_FROMFLOAT( m_font.pointSize() ), 0, 0 );
00505 if( error )
00506 {
00507 kdDebug(38000) << "traceText(), unable to set font size. Aborting!" << endl;
00508 FT_Done_Face( fontFace );
00509 FT_Done_FreeType( library );
00510 return;
00511 }
00512
00513
00514 float l = 0;
00515 QValueList<float> glyphXAdvance;
00516 QValueList<float> glyphYAdvance;
00517 for( unsigned int i = 0; i < m_text.length(); i++ )
00518 {
00519
00520 QChar character = m_text.at( i );
00521 glyphIndex = FT_Get_Char_Index( fontFace, character.unicode() );
00522 if( ! glyphIndex )
00523 {
00524 kdDebug(38000) << "traceText(), unable get index of char : " << character << endl;
00525 continue;
00526 }
00527
00528 FT_Error error = FT_Load_Glyph( fontFace, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP );
00529 if( error )
00530 {
00531 kdDebug(38000) << "traceText(), unable to load glyph : " << error << endl;
00532 continue;
00533 }
00534
00535
00536 FT_OutlineGlyph g;
00537 error = FT_Get_Glyph( fontFace->glyph, reinterpret_cast<FT_Glyph *>( &g ) );
00538 if( error )
00539 {
00540 kdDebug(38000) << "traceText(), unable to get glyph: " << error << endl;
00541 continue;
00542 }
00543
00544 VPath *composite = new VPath( this );
00545 error = FT_Outline_Check( &g->outline );
00546 if( error )
00547 {
00548 kdDebug(38000) << "traceText(), outline is broken : " << error << endl;
00549 continue;
00550 }
00551
00552 error = FT_Outline_Decompose(&g->outline, &OutlineMethods, composite );
00553 if( error )
00554 {
00555 kdDebug(38000) << "traceText(), unable to decompose outline : " << error << endl;
00556 continue;
00557 }
00558
00559 m_glyphs.append( composite );
00560 glyphXAdvance.append( FT_TOFLOAT( fontFace->glyph->advance.x ) );
00561 glyphYAdvance.append( FT_TOFLOAT( fontFace->glyph->advance.y ) );
00562 l += FT_TOFLOAT( fontFace->glyph->advance.x );
00563 FT_Done_Glyph( reinterpret_cast<FT_Glyph>( g ) );
00564 }
00565
00566
00567 float pathLength = 0;
00568 VSubpathIterator pIt( m_basePath );
00569
00570 VSegment* seg;
00571 for( ; pIt.current(); ++pIt )
00572 if( (seg = pIt.current() ) )
00573 pathLength += seg->length();
00574
00575 kdDebug(38000) << "traceText(), using offset : " << m_offset << endl;
00576 float x = m_offset * pathLength;
00577
00578 switch( m_alignment )
00579 {
00580 case Left: x += 0; break;
00581 case Center: x -= 0.5 * l; break;
00582 case Right: x -= l; break;
00583 }
00584 float y = 0;
00585 float dx = 0;
00586 float sp = 0;
00587 KoPoint point;
00588 KoPoint normal;
00589 KoPoint tangent;
00590 VSubpathIterator pathIt( m_basePath );
00591 VSegment* oldSeg = pathIt.current();
00592 seg = ++pathIt;
00593 KoPoint extPoint;
00594 bool ext = false;
00595 float fsx = 0;
00596 float yoffset = ( m_position == Above ? 0 : ( m_position == On ? m_font.pointSize() / 3 : m_font.pointSize() / 1.5 ) );
00597 kdDebug(38000) << "Position: " << m_position << " -> " << yoffset << endl;
00598 for( unsigned int i = 0; i < m_text.length(); i++ )
00599 {
00600 VPath* composite = m_glyphs.at( i );
00601 if( ! composite )
00602 continue;
00603
00604 dx = *glyphXAdvance.at( i ) / 2;
00605 x += dx;
00606 VTransformCmd trafo( 0L, QWMatrix( 1, 0, 0, 1, -dx, y + yoffset ) );
00607 trafo.visit( *composite );
00608
00609
00610
00611 if ( x < 0 )
00612 {
00613 if( !ext )
00614 seg->pointTangentNormalAt( 0, &extPoint, &tangent, &normal );
00615 point = extPoint + x * tangent;
00616 ext = true;
00617 }
00618 else
00619 {
00620 while ( seg && x > fsx + seg->length() )
00621 {
00622 fsx += seg->length();
00623 oldSeg = seg;
00624 seg = ++pathIt;
00625 }
00626 if( seg )
00627 {
00628 ext = false;
00629 sp = ( x - fsx ) / seg->length();
00630 seg->pointTangentNormalAt( sp, &point, &tangent, &normal );
00631 }
00632 else
00633 {
00634 if( !ext )
00635 oldSeg->pointTangentNormalAt( 1, &extPoint, &tangent, &normal );
00636 point = extPoint + ( x - fsx ) * tangent;
00637 ext = true;
00638 }
00639 }
00640
00641
00642
00643 trafo.setMatrix( QWMatrix( tangent.x(), tangent.y(), tangent.y(), -tangent.x(), point.x(), point.y() ) );
00644 trafo.visit( *composite );
00645 composite->setState( state() );
00646
00647
00648
00649 x += dx;
00650 y += *glyphYAdvance.at( i );
00651 }
00652 FT_Done_Face( fontFace );
00653 FT_Done_FreeType( library );
00654 m_boundingBoxIsInvalid = true;
00655 }
00656
00657
00658 QString
00659 VText::buildRequest( QString family, int weight, int slant, double size, int &id )
00660 {
00661
00662 int pos;
00663 if( ( pos = family.find( '[' ) ) )
00664 family = family.left( pos );
00665
00666
00667 FcPattern *pattern;
00668 QString fileName;
00669
00670 pattern = FcPatternBuild( 0, FC_WEIGHT, FcTypeInteger, weight,
00671 FC_SLANT, FcTypeInteger, slant,
00672 FC_SIZE, FcTypeDouble, size, NULL );
00673
00674
00675 FcPatternAddString( pattern, FC_FAMILY, reinterpret_cast<const FcChar8 *>( family.latin1() ) );
00676
00677
00678 FcPatternAddBool( pattern, FC_HINTING, FcFalse );
00679
00680 FcPatternAddBool( pattern, FC_SCALABLE, FcTrue );
00681
00682
00683 FcDefaultSubstitute( pattern );
00684 FcConfigSubstitute( FcConfigGetCurrent(), pattern, FcMatchPattern );
00685
00686 FcResult result;
00687
00688
00689
00690 FcFontSet *fset = FcFontSort( 0, pattern, FcFalse, 0L, &result );
00691
00692
00693 FcPatternDestroy( pattern );
00694
00695 if( fset )
00696 {
00697 FcBool scalable;
00698 FcChar8 *temp;
00699
00700
00701 for( int i = 0; i < fset->nfont; ++i )
00702 {
00703 pattern = fset->fonts[i];
00704 if( FcResultMatch != FcPatternGetBool( pattern, FC_SCALABLE, 0, &scalable ) )
00705 continue;
00706 if( scalable == FcTrue )
00707 {
00708
00709 if( FcPatternGetString(pattern, FC_FILE, 0, &temp) != FcResultMatch ||
00710 FcPatternGetInteger(pattern, FC_INDEX, 0, &id) != FcResultMatch )
00711 {
00712 kdDebug(38000) << "VText::buildRequest(), could not load font file for requested font \"" << family.latin1() << "\"" << endl;
00713 return QString::null;
00714 }
00715
00716 fileName = QFile::decodeName(reinterpret_cast<const char *>( temp ));
00717
00718
00719 QString newFamily;
00720
00721 if( FcResultMatch == FcPatternGetString( pattern, FC_FAMILY, 0, &temp ) )
00722 m_font.setFamily( reinterpret_cast<const char *>( temp ) );
00723
00724 break;
00725 }
00726 }
00727
00728 FcFontSetDestroy( fset );
00729 }
00730
00731
00732 return fileName;
00733 }
00734
00735 void VText::setOffset( double offset )
00736 {
00737 if( offset < 0.0 )
00738 m_offset = 0.0;
00739 else if( offset > 1.0 )
00740 m_offset = 1.0;
00741 else
00742 m_offset = offset;
00743 }
00744
00745 #endif // HAVE_KARBONTEXT