29#include "vtext_iface.h"
36#include "commands/vtransformcmd.h"
41#include <fontconfig/fontconfig.h>
48#define FT_TOFLOAT(x) ((x) * (1.0 / 64.0))
49#define FT_FROMFLOAT(x) ((int) floor ((x) * 64.0 + 0.5))
54int traceMoveto( FT_Vector *to,
VPath *composite )
56 double tox = ( to->x / 64.0 );
57 double toy = ( -to->y / 64.0 );
61 composite->moveTo( KoPoint( tox, toy ) );
66int traceLineto( FT_Vector *to,
VPath *composite )
68 double tox = ( to->x / 64.0 );
69 double toy = ( -to->y / 64.0 );
74 composite->lineTo( KoPoint( tox, toy ) );
79int 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 );
88 composite->curveTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), KoPoint( x2, y2 ) );
94int 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 );
106 composite->curveTo( KoPoint( x1, y1 ), KoPoint( x2, y2 ), KoPoint( x3, y3 ) );
111FT_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,
123VText::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;
140VText::VText(
const TQFont &font,
const VSubpath& basePath, Position position, Alignment alignment,
const TQString& text )
141 :
VObject( 0L ), m_font( font ), m_basePath( basePath ), m_position( position ), m_alignment( alignment ), m_text( text )
143 m_glyphs.setAutoDelete(
true );
144 m_boundingBoxIsInvalid =
true;
145 m_stroke =
new VStroke(
this );
146 m_fill =
new VFill();
150VText::VText(
const VText& text )
151 :
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 )
153 m_stroke =
new VStroke( *text.m_stroke );
154 m_stroke->setParent(
this );
155 m_fill =
new VFill( *text.m_fill );
158 VPathListIterator itr( text.m_glyphs );
159 for( ; itr.current() ; ++itr )
163 m_glyphs.append( c );
166 m_boundingBoxIsInvalid =
true;
173DCOPObject* VText::dcopObject()
176 m_dcop =
new VTextIface(
this );
183VText::draw( VPainter* painter,
const KoRect* )
const
186 state() == deleted ||
188 state() == hidden_locked )
195 VPathListIterator itr( m_glyphs );
197 if( state() != edit )
205 if ( m_translucentShadow )
207 color.
set( 0., 0., 0. );
212 color.
set( .3, .3, .3 );
215 int shadowDx = int( m_shadowDistance * cos( m_shadowAngle / 360. * 6.2832 ) );
216 int shadowDy = int( m_shadowDistance * sin( m_shadowAngle / 360. * 6.2832 ) );
218 VTransformCmd trafo( 0L, TQWMatrix() );
219 for( itr.toFirst(); itr.current(); ++itr )
221 trafo.setMatrix( TQWMatrix( 1, 0, 0, 1, shadowDx, shadowDy ) );
222 trafo.visit( *( itr.current() ) );
223 itr.current()->setFill(
VFill( color ) );
224 itr.current()->setStroke(
VStroke( color ) );
225 itr.current()->draw( painter );
226 trafo.setMatrix( TQWMatrix( 1, 0, 0, 1, -shadowDx, -shadowDy ) );
227 trafo.visit( *( itr.current() ) );
231 for( itr.toFirst(); itr.current(); ++itr )
233 itr.current()->setFill( *m_fill );
234 itr.current()->setStroke( *m_stroke );
235 itr.current()->draw( painter );
240 if( state() == edit )
243 painter->setRasterOp( TQt::XorROP );
244 painter->setPen( TQt::yellow );
245 painter->setBrush( TQt::NoBrush );
247 for( itr.toFirst(); itr.current(); ++itr )
248 itr.current()->draw( painter );
250 painter->strokePath();
257VText::boundingBox()
const
259 if( m_boundingBoxIsInvalid )
261 VPathListIterator itr( m_glyphs );
264 m_boundingBox = itr.current() ? itr.current()->boundingBox() : KoRect();
265 for( ++itr; itr.current(); ++itr )
266 if( !itr.current()->boundingBox().isEmpty() )
267 m_boundingBox |= itr.current()->boundingBox();
270 m_boundingBox.setCoords(
271 m_boundingBox.left() - 0.5 * stroke()->lineWidth(),
272 m_boundingBox.top() - 0.5 * stroke()->lineWidth(),
273 m_boundingBox.right() + 0.5 * stroke()->lineWidth(),
274 m_boundingBox.bottom() + 0.5 * stroke()->lineWidth() );
276 m_boundingBoxIsInvalid =
false;
279 return m_boundingBox;
285 return new VText( *
this );
288VGroup* VText::toVGroup()
const
292 VPathListIterator itr( m_glyphs );
293 for( itr.toFirst(); itr.current(); ++itr )
307VText::save( TQDomElement& element )
const
309 if( state() != deleted )
311 TQDomElement me = element.ownerDocument().createElement(
"TEXT" );
314 path.combinePath( m_basePath );
320 me.setAttribute(
"text", m_text );
321 me.setAttribute(
"family", m_font.family() );
322 me.setAttribute(
"size", m_font.pointSize() );
323 me.setAttribute(
"italic", m_font.italic() );
324 me.setAttribute(
"bold", m_font.bold() );
325 me.setAttribute(
"position", m_position );
326 me.setAttribute(
"alignment", m_alignment );
327 me.setAttribute(
"shadow", m_shadow );
328 me.setAttribute(
"translucentshadow", m_translucentShadow );
329 me.setAttribute(
"shadowangle", m_shadowAngle );
330 me.setAttribute(
"shadowdist", m_shadowDistance );
331 me.setAttribute(
"offset", m_offset );
332 element.appendChild( me );
335 VPathListIterator itr = m_glyphs;
336 for( itr.toFirst(); itr.current(); ++itr )
337 itr.current()->save( me );
342VText::load(
const TQDomElement& element )
346 m_font.setFamily( element.attribute(
"family",
"Times" ) );
347 m_font.setPointSize( element.attribute(
"size",
"12" ).toInt() );
348 m_font.setItalic( element.attribute(
"italic" ).toInt() == 1 );
349 m_font.setWeight( TQFont::Normal );
350 m_font.setBold( element.attribute(
"bold" ).toInt() == 1 );
351 m_position = (Position)element.attribute(
"position",
"0" ).toInt();
352 m_alignment = (Alignment)element.attribute(
"alignment",
"0" ).toInt();
353 m_shadow = ( element.attribute(
"shadow" ).toInt() == 1 );
354 m_translucentShadow = ( element.attribute(
"translucentshadow" ).toInt() == 1 );
355 m_shadowAngle = element.attribute(
"shadowangle" ).toInt();
356 m_shadowDistance = element.attribute(
"shadowdist" ).toInt();
357 m_offset = element.attribute(
"offset" ).toDouble();
358 m_text = element.attribute(
"text",
"" );
362 TQDomNodeList list = element.childNodes();
363 TQDomElement e = list.item( 0 ).toElement();
366 uint startElement = 0;
368 if( e.tagName() ==
"PATH" )
372 m_basePath = *path.paths().getFirst();
377 for( uint i = startElement; i < list.count(); ++i )
379 if( list.item( i ).isElement() )
381 e = list.item( i ).toElement();
382 if( e.tagName() ==
"PATH" )
385 composite->
load( e );
386 m_glyphs.append( composite );
388 if( e.tagName() ==
"STROKE" )
390 if( e.tagName() ==
"FILL" )
395#ifdef HAVE_KARBONTEXT
396 if( m_glyphs.count() == 0 )
399 m_boundingBoxIsInvalid =
true;
404VText::setText(
const TQString& text )
410#ifdef HAVE_KARBONTEXT
417VText::setState(
const VState state )
421 VPathListIterator itr( m_glyphs );
422 for( itr.toFirst(); itr.current(); ++itr )
424 itr.current()->setState( state );
434#ifdef HAVE_KARBONTEXT
439 if( m_basePath.count() == 0 )
441 kdDebug(38000) <<
"Can't draw a text without base path (was: " << m_text <<
")." << endl;
446 int slant = FC_SLANT_ROMAN;
447 if( m_font.italic() )
448 slant = FC_SLANT_ITALIC;
452 weight = FC_WEIGHT_BOLD;
456 TQString filename = buildRequest( m_font.family(), weight, slant, m_font.pointSize(),
id );
459 kdDebug(38000) <<
"Loading " << filename.latin1() <<
" for requested font \"" << TQString(m_font.family()).latin1() <<
"\", " << m_font.pointSize() <<
" pt." << endl;
465 FT_Init_FreeType( &library );
466 FT_Error error = FT_New_Face( library, TQFile::encodeName(filename),
id, &fontFace );
470 kdDebug(38000) <<
"traceText(), could not load font. Aborting!" << endl;
474 bool foundCharmap =
false;
477 for(
int charmap = 0; charmap < fontFace->num_charmaps; charmap++ )
479 if( fontFace->charmaps[charmap]->encoding == ft_encoding_unicode )
481 FT_Error error = FT_Set_Charmap( fontFace, fontFace->charmaps[charmap] );
484 kdDebug(38000) <<
"traceText(), unable to select unicode charmap." << endl;
494 error = FT_Set_Charmap( fontFace, fontFace->charmaps[0] );
497 kdDebug(38000) <<
"traceText(), unable to select charmap. Aborting!" << endl;
498 FT_Done_Face( fontFace );
499 FT_Done_FreeType( library );
504 error = FT_Set_Char_Size( fontFace, FT_FROMFLOAT( m_font.pointSize() ), FT_FROMFLOAT( m_font.pointSize() ), 0, 0 );
507 kdDebug(38000) <<
"traceText(), unable to set font size. Aborting!" << endl;
508 FT_Done_Face( fontFace );
509 FT_Done_FreeType( library );
515 TQValueList<float> glyphXAdvance;
516 TQValueList<float> glyphYAdvance;
517 for(
unsigned int i = 0; i < m_text.length(); i++ )
520 TQChar character = m_text.at( i );
521 glyphIndex = FT_Get_Char_Index( fontFace, character.unicode() );
524 kdDebug(38000) <<
"traceText(), unable get index of char : " << character << endl;
528 FT_Error error = FT_Load_Glyph( fontFace, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP );
531 kdDebug(38000) <<
"traceText(), unable to load glyph : " << error << endl;
537 error = FT_Get_Glyph( fontFace->glyph,
reinterpret_cast<FT_Glyph *
>( &g ) );
540 kdDebug(38000) <<
"traceText(), unable to get glyph: " << error << endl;
545 error = FT_Outline_Check( &g->outline );
548 kdDebug(38000) <<
"traceText(), outline is broken : " << error << endl;
552 error = FT_Outline_Decompose(&g->outline, &OutlineMethods, composite );
555 kdDebug(38000) <<
"traceText(), unable to decompose outline : " << error << endl;
559 m_glyphs.append( composite );
560 glyphXAdvance.append( FT_TOFLOAT( fontFace->glyph->advance.x ) );
561 glyphYAdvance.append( FT_TOFLOAT( fontFace->glyph->advance.y ) );
562 l += FT_TOFLOAT( fontFace->glyph->advance.x );
563 FT_Done_Glyph(
reinterpret_cast<FT_Glyph
>( g ) );
567 float pathLength = 0;
571 for( ; pIt.current(); ++pIt )
572 if( (seg = pIt.current() ) )
573 pathLength += seg->
length();
575 kdDebug(38000) <<
"traceText(), using offset : " << m_offset << endl;
576 float x = m_offset * pathLength;
578 switch( m_alignment )
580 case Left: x += 0;
break;
581 case Center: x -= 0.5 * l;
break;
582 case Right: x -= l;
break;
591 VSegment* oldSeg = pathIt.current();
596 float yoffset = ( m_position == Above ? 0 : ( m_position == On ? m_font.pointSize() / 3 : m_font.pointSize() / 1.5 ) );
597 kdDebug(38000) <<
"Position: " << m_position <<
" -> " << yoffset << endl;
598 for(
unsigned int i = 0; i < m_text.length(); i++ )
600 VPath* composite = m_glyphs.at( i );
604 dx = *glyphXAdvance.at( i ) / 2;
606 VTransformCmd trafo( 0L, TQWMatrix( 1, 0, 0, 1, -dx, y + yoffset ) );
607 trafo.visit( *composite );
615 point = extPoint + x * tangent;
620 while ( seg && x > fsx + seg->
length() )
629 sp = ( x - fsx ) / seg->
length();
636 point = extPoint + ( x - fsx ) * tangent;
643 trafo.setMatrix( TQWMatrix( tangent.x(), tangent.y(), tangent.y(), -tangent.x(), point.x(), point.y() ) );
644 trafo.visit( *composite );
650 y += *glyphYAdvance.at( i );
652 FT_Done_Face( fontFace );
653 FT_Done_FreeType( library );
654 m_boundingBoxIsInvalid =
true;
659VText::buildRequest( TQString family,
int weight,
int slant,
double size,
int &
id )
663 if( ( pos = family.find(
'[' ) ) )
664 family = family.left( pos );
670 pattern = FcPatternBuild( 0, FC_WEIGHT, FcTypeInteger, weight,
671 FC_SLANT, FcTypeInteger, slant,
672 FC_SIZE, FcTypeDouble, size, NULL );
675 FcPatternAddString( pattern, FC_FAMILY,
reinterpret_cast<const FcChar8 *
>( family.latin1() ) );
678 FcPatternAddBool( pattern, FC_HINTING, FcFalse );
680 FcPatternAddBool( pattern, FC_SCALABLE, FcTrue );
683 FcDefaultSubstitute( pattern );
684 FcConfigSubstitute( FcConfigGetCurrent(), pattern, FcMatchPattern );
690 FcFontSet *fset = FcFontSort( 0, pattern, FcFalse, 0L, &result );
693 FcPatternDestroy( pattern );
701 for(
int i = 0; i < fset->nfont; ++i )
703 pattern = fset->fonts[i];
704 if( FcResultMatch != FcPatternGetBool( pattern, FC_SCALABLE, 0, &scalable ) )
706 if( scalable == FcTrue )
709 if( FcPatternGetString(pattern, FC_FILE, 0, &temp) != FcResultMatch ||
710 FcPatternGetInteger(pattern, FC_INDEX, 0, &
id) != FcResultMatch )
712 kdDebug(38000) <<
"VText::buildRequest(), could not load font file for requested font \"" << family.latin1() <<
"\"" << endl;
716 fileName = TQFile::decodeName(
reinterpret_cast<const char *
>( temp ));
721 if( FcResultMatch == FcPatternGetString( pattern, FC_FAMILY, 0, &temp ) )
722 m_font.setFamily(
reinterpret_cast<const char *
>( temp ) );
728 FcFontSetDestroy( fset );
737void VText::setOffset(
double offset )
741 else if( offset > 1.0 )
This class keeps track of color properties.
void set(float v1)
Sets the first color component.
void setOpacity(float opacity)
Sets the color opacity.
Manages the fill of shapes.
Base class for all sort of VObject conglomerats.
virtual void setStroke(const VStroke &stroke)
Sets the stroke to a given new stroke.
virtual void setFill(const VFill &fill)
Sets the fill to a given new fill.
void append(VObject *object)
Appends a new child object.
The base class for all karbon objects.
virtual void setState(const VState state)
Sets the state to a specified new state.
void setParent(VObject *parent)
Sets a new parent object.
virtual void load(const TQDomElement &element)
Load this object's state from xml and initialize this object accordingly.
virtual void save(TQDomElement &element) const
Save this object's state to xml.
A composite path consists of one or many subpaths.
virtual void load(const TQDomElement &element)
Load this object's state from xml and initialize this object accordingly.
virtual VPath * clone() const
Create an exact copy of this object.
A class representing lines and beziers.
double length(double t=1.0) const
Calculates the arclength from p0 to the point parametrized by 0 <= t <= 1.
void pointTangentNormalAt(double t, KoPoint *p=0L, KoPoint *tn=0L, KoPoint *n=0L) const
Calculates the point, the tangent vector and the normal vector for 0 <= t <= 1.
Manages stroke properties.
VSubpathIterator provides an iterator class for highlevel path access.
VSubpath provides a sophisticated list of VSegment.
The abstract visitor class.
virtual void visitVText(VText &text)
Visit method for a VText.