]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasText.cxx
Fix #1783: repeated error message on console
[simgear.git] / simgear / canvas / elements / CanvasText.cxx
1 // A text on the Canvas
2 //
3 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Library General Public License for more details.
14 //
15 // You should have received a copy of the GNU Library General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
18
19 #include "CanvasText.hxx"
20 #include <simgear/canvas/Canvas.hxx>
21 #include <simgear/canvas/CanvasSystemAdapter.hxx>
22 #include <simgear/scene/util/parse_color.hxx>
23 #include <osg/Version>
24 #include <osgDB/Registry>
25 #include <osgText/Text>
26
27 namespace simgear
28 {
29 namespace canvas
30 {
31   class Text::TextOSG:
32     public osgText::Text
33   {
34     public:
35       TextOSG(canvas::Text* text);
36
37       void setFontResolution(int res);
38       void setCharacterAspect(float aspect);
39       void setLineHeight(float factor);
40       void setFill(const std::string& fill);
41       void setStroke(const std::string& color);
42       void setBackgroundColor(const std::string& fill);
43
44       float lineHeight() const;
45
46       /// Get the number of lines
47       size_t lineCount() const;
48
49       /// Get line @a i
50       TextLine lineAt(size_t i) const;
51
52       /// Get nearest line to given y-coordinate
53       TextLine nearestLine(float pos_y) const;
54
55
56       SGVec2i sizeForWidth(int w) const;
57
58       virtual osg::BoundingBox
59 #if OSG_VERSION_LESS_THAN(3,3,2)
60       computeBound()
61 #else
62       computeBoundingBox()
63 #endif
64       const;
65
66     protected:
67
68       friend class TextLine;
69
70       canvas::Text *_text_element;
71
72       virtual void computePositions(unsigned int contextID) const;
73   };
74
75   class TextLine
76   {
77     public:
78       TextLine();
79       TextLine(size_t line, Text::TextOSG const* text);
80
81       /// Number of characters on this line
82       size_t size() const;
83       bool empty() const;
84
85       osg::Vec2 cursorPos(size_t i) const;
86       osg::Vec2 nearestCursor(float x) const;
87
88     protected:
89       typedef Text::TextOSG::GlyphQuads GlyphQuads;
90
91       Text::TextOSG const *_text;
92       GlyphQuads const    *_quads;
93
94       size_t _line,
95              _begin,
96              _end;
97   };
98
99   //----------------------------------------------------------------------------
100   TextLine::TextLine():
101     _text(NULL),
102     _quads(NULL),
103     _line(0),
104     _begin(-1),
105     _end(-1)
106   {
107
108   }
109
110   //----------------------------------------------------------------------------
111   TextLine::TextLine(size_t line, Text::TextOSG const* text):
112     _text(text),
113     _quads(NULL),
114     _line(line),
115     _begin(-1),
116     _end(-1)
117   {
118     if( !text || text->_textureGlyphQuadMap.empty() || !_text->lineCount() )
119       return;
120
121     _quads = &text->_textureGlyphQuadMap.begin()->second;
122
123     GlyphQuads::LineNumbers const& line_numbers = _quads->_lineNumbers;
124     GlyphQuads::LineNumbers::const_iterator begin_it =
125       std::lower_bound(line_numbers.begin(), line_numbers.end(), _line);
126
127     if( begin_it == line_numbers.end() || *begin_it != _line )
128       // empty line or past last line
129       return;
130
131     _begin = begin_it - line_numbers.begin();
132     _end = std::upper_bound(begin_it, line_numbers.end(), _line)
133          - line_numbers.begin();
134   }
135
136   //----------------------------------------------------------------------------
137   size_t TextLine::size() const
138   {
139     return _end - _begin;
140   }
141
142   //----------------------------------------------------------------------------
143   bool TextLine::empty() const
144   {
145     return _end == _begin;
146   }
147
148   //----------------------------------------------------------------------------
149   osg::Vec2 TextLine::cursorPos(size_t i) const
150   {
151     if( !_quads )
152       return osg::Vec2(0, 0);
153
154     if( i > size() )
155       // Position after last character if out of range (TODO better exception?)
156       i = size();
157
158     osg::Vec2 pos(0, _text->_offset.y() + _line * _text->lineHeight());
159
160     if( empty() )
161       return pos;
162 #if OSG_VERSION_LESS_THAN(3,3,5)
163     GlyphQuads::Coords2 const& coords = _quads->_coords;
164 #else
165     GlyphQuads::Coords2 refCoords = _quads->_coords;
166     GlyphQuads::Coords2::element_type &coords = *refCoords.get();
167 #endif
168     size_t global_i = _begin + i;
169
170     if( global_i == _begin )
171       // before first character of line
172       pos.x() = coords[_begin * 4].x();
173     else if( global_i == _end )
174       // After Last character of line
175       pos.x() = coords[(_end - 1) * 4 + 2].x();
176     else
177     {
178       float prev_l = coords[(global_i - 1) * 4].x(),
179             prev_r = coords[(global_i - 1) * 4 + 2].x(),
180             cur_l = coords[global_i * 4].x();
181
182       if( prev_l == prev_r )
183         // If previous character width is zero set to begin of next character
184         // (Happens eg. with spaces)
185         pos.x() = cur_l;
186       else
187         // position at center between characters
188         pos.x() = 0.5 * (prev_r + cur_l);
189     }
190
191     return pos;
192   }
193
194   //----------------------------------------------------------------------------
195   osg::Vec2 TextLine::nearestCursor(float x) const
196   {
197     if( empty() )
198       return cursorPos(0);
199
200     GlyphQuads::Glyphs const& glyphs = _quads->_glyphs;
201     #if OSG_VERSION_LESS_THAN(3,3,5)
202     GlyphQuads::Coords2 const& coords = _quads->_coords;
203 #else
204     GlyphQuads::Coords2 refCoords = _quads->_coords;
205     GlyphQuads::Coords2::element_type &coords = *refCoords.get();
206 #endif
207
208     float const HIT_FRACTION = 0.6;
209     float const character_width = _text->getCharacterHeight()
210                                 * _text->getCharacterAspectRatio();
211
212     size_t i = _begin;
213     for(; i < _end; ++i)
214     {
215       // Get threshold for mouse x position for setting cursor before or after
216       // current character
217       float threshold = coords[i * 4].x()
218                       + HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
219                                      * character_width;
220
221       if( x <= threshold )
222         break;
223     }
224
225     return cursorPos(i - _begin);
226   }
227
228   //----------------------------------------------------------------------------
229   Text::TextOSG::TextOSG(canvas::Text* text):
230     _text_element(text)
231   {
232     setBackdropImplementation(NO_DEPTH_BUFFER);
233   }
234
235   //----------------------------------------------------------------------------
236   void Text::TextOSG::setFontResolution(int res)
237   {
238     TextBase::setFontResolution(res, res);
239   }
240
241   //----------------------------------------------------------------------------
242   void Text::TextOSG::setCharacterAspect(float aspect)
243   {
244     setCharacterSize(getCharacterHeight(), aspect);
245   }
246
247   //----------------------------------------------------------------------------
248   void Text::TextOSG::setLineHeight(float factor)
249   {
250     setLineSpacing(factor - 1);
251   }
252
253   //----------------------------------------------------------------------------
254   void Text::TextOSG::setFill(const std::string& fill)
255   {
256 //    if( fill == "none" )
257 //      TODO No text
258 //    else
259     osg::Vec4 color;
260     if( parseColor(fill, color) )
261       setColor( color );
262   }
263
264   //----------------------------------------------------------------------------
265   void Text::TextOSG::setStroke(const std::string& stroke)
266   {
267     osg::Vec4 color;
268     if( stroke == "none" || !parseColor(stroke, color) )
269       setBackdropType(NONE);
270     else
271     {
272       setBackdropType(OUTLINE);
273       setBackdropColor(color);
274     }
275   }
276
277   //----------------------------------------------------------------------------
278   void Text::TextOSG::setBackgroundColor(const std::string& fill)
279   {
280     osg::Vec4 color;
281     if( parseColor(fill, color) )
282       setBoundingBoxColor( color );
283   }
284
285   //----------------------------------------------------------------------------
286   float Text::TextOSG::lineHeight() const
287   {
288     return (1 + _lineSpacing) * _characterHeight;
289   }
290
291   //----------------------------------------------------------------------------
292   size_t Text::TextOSG::lineCount() const
293   {
294     return _lineCount;
295   }
296
297   //----------------------------------------------------------------------------
298   TextLine Text::TextOSG::lineAt(size_t i) const
299   {
300     return TextLine(i, this);
301   }
302
303   //----------------------------------------------------------------------------
304   TextLine Text::TextOSG::nearestLine(float pos_y) const
305   {
306     osgText::Font const* font = getActiveFont();
307     if( !font || lineCount() <= 0 )
308       return TextLine(0, this);
309
310     float asc = .9f, desc = -.2f;
311     font->getVerticalSize(asc, desc);
312
313     float first_line_y = _offset.y()
314                        - (1 + _lineSpacing / 2 + desc) * _characterHeight;
315
316     size_t line_num = std::min<size_t>(
317       std::max<size_t>(0, (pos_y - first_line_y) / lineHeight()),
318       lineCount() - 1
319     );
320
321     return TextLine(line_num, this);
322   }
323
324   //----------------------------------------------------------------------------
325   // simplified version of osgText::Text::computeGlyphRepresentation() to
326   // just calculate the size for a given weight. Glpyh calculations/creating
327   // is not necessary for this...
328   SGVec2i Text::TextOSG::sizeForWidth(int w) const
329   {
330     if( _text.empty() )
331       return SGVec2i(0, 0);
332
333     osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont());
334     if( !activefont )
335       return SGVec2i(-1, -1);
336
337     float max_width_safe = _maximumWidth;
338     const_cast<TextOSG*>(this)->_maximumWidth = w;
339
340     SGRecti bb;
341
342     osg::Vec2 startOfLine_coords(0.0f,0.0f);
343     osg::Vec2 cursor(startOfLine_coords);
344     osg::Vec2 local(0.0f,0.0f);
345
346     unsigned int previous_charcode = 0;
347     unsigned int line_length = 0;
348     bool horizontal = _layout != VERTICAL;
349     bool kerning = true;
350
351     float hr = _characterHeight;
352     float wr = hr / getCharacterAspectRatio();
353
354     // osg should really care more about const :-/
355     osgText::String& text = const_cast<osgText::String&>(_text);
356     typedef osgText::String::iterator TextIterator;
357
358     for( TextIterator itr = text.begin(); itr != text.end(); )
359     {
360       // record the start of the current line
361       TextIterator startOfLine_itr = itr;
362
363       // find the end of the current line.
364       osg::Vec2 endOfLine_coords(cursor);
365       TextIterator endOfLine_itr =
366         const_cast<TextOSG*>(this)->computeLastCharacterOnLine(
367           endOfLine_coords, itr, text.end()
368         );
369
370       line_length = endOfLine_itr - startOfLine_itr;
371
372       // Set line position to correct alignment.
373       switch( _layout )
374       {
375         case LEFT_TO_RIGHT:
376         {
377           switch( _alignment )
378           {
379             // nothing to be done for these
380             //case LEFT_TOP:
381             //case LEFT_CENTER:
382             //case LEFT_BOTTOM:
383             //case LEFT_BASE_LINE:
384             //case LEFT_BOTTOM_BASE_LINE:
385             //  break;
386             case CENTER_TOP:
387             case CENTER_CENTER:
388             case CENTER_BOTTOM:
389             case CENTER_BASE_LINE:
390             case CENTER_BOTTOM_BASE_LINE:
391               cursor.x() = (cursor.x() - endOfLine_coords.x()) * 0.5f;
392               break;
393             case RIGHT_TOP:
394             case RIGHT_CENTER:
395             case RIGHT_BOTTOM:
396             case RIGHT_BASE_LINE:
397             case RIGHT_BOTTOM_BASE_LINE:
398               cursor.x() = cursor.x() - endOfLine_coords.x();
399               break;
400             default:
401               break;
402           }
403           break;
404         }
405         case RIGHT_TO_LEFT:
406         {
407           switch( _alignment )
408           {
409             case LEFT_TOP:
410             case LEFT_CENTER:
411             case LEFT_BOTTOM:
412             case LEFT_BASE_LINE:
413             case LEFT_BOTTOM_BASE_LINE:
414               cursor.x() = 2 * cursor.x() - endOfLine_coords.x();
415               break;
416             case CENTER_TOP:
417             case CENTER_CENTER:
418             case CENTER_BOTTOM:
419             case CENTER_BASE_LINE:
420             case CENTER_BOTTOM_BASE_LINE:
421               cursor.x() = cursor.x()
422                   + (cursor.x() - endOfLine_coords.x()) * 0.5f;
423               break;
424               // nothing to be done for these
425               //case RIGHT_TOP:
426               //case RIGHT_CENTER:
427               //case RIGHT_BOTTOM:
428               //case RIGHT_BASE_LINE:
429               //case RIGHT_BOTTOM_BASE_LINE:
430               //  break;
431             default:
432               break;
433           }
434           break;
435         }
436         case VERTICAL:
437         {
438           switch( _alignment )
439           {
440             // TODO: current behaviour top baselines lined up in both cases - need to implement
441             //       top of characters alignment - Question is this necessary?
442             // ... otherwise, nothing to be done for these 6 cases
443             //case LEFT_TOP:
444             //case CENTER_TOP:
445             //case RIGHT_TOP:
446             //  break;
447             //case LEFT_BASE_LINE:
448             //case CENTER_BASE_LINE:
449             //case RIGHT_BASE_LINE:
450             //  break;
451             case LEFT_CENTER:
452             case CENTER_CENTER:
453             case RIGHT_CENTER:
454               cursor.y() = cursor.y()
455                   + (cursor.y() - endOfLine_coords.y()) * 0.5f;
456               break;
457             case LEFT_BOTTOM_BASE_LINE:
458             case CENTER_BOTTOM_BASE_LINE:
459             case RIGHT_BOTTOM_BASE_LINE:
460               cursor.y() = cursor.y() - (line_length * _characterHeight);
461               break;
462             case LEFT_BOTTOM:
463             case CENTER_BOTTOM:
464             case RIGHT_BOTTOM:
465               cursor.y() = 2 * cursor.y() - endOfLine_coords.y();
466               break;
467             default:
468               break;
469           }
470           break;
471         }
472       }
473
474       if( itr != endOfLine_itr )
475       {
476
477         for(;itr != endOfLine_itr;++itr)
478         {
479           unsigned int charcode = *itr;
480
481           osgText::Glyph* glyph = activefont->getGlyph(_fontSize, charcode);
482           if( glyph )
483           {
484             float width = (float) (glyph->getWidth()) * wr;
485             float height = (float) (glyph->getHeight()) * hr;
486
487             if( _layout == RIGHT_TO_LEFT )
488             {
489               cursor.x() -= glyph->getHorizontalAdvance() * wr;
490             }
491
492             // adjust cursor position w.r.t any kerning.
493             if( kerning && previous_charcode )
494             {
495               switch( _layout )
496               {
497                 case LEFT_TO_RIGHT:
498                 {
499                   osg::Vec2 delta( activefont->getKerning( previous_charcode,
500                                                            charcode,
501                                                            _kerningType ) );
502                   cursor.x() += delta.x() * wr;
503                   cursor.y() += delta.y() * hr;
504                   break;
505                 }
506                 case RIGHT_TO_LEFT:
507                 {
508                   osg::Vec2 delta( activefont->getKerning( charcode,
509                                                            previous_charcode,
510                                                            _kerningType ) );
511                   cursor.x() -= delta.x() * wr;
512                   cursor.y() -= delta.y() * hr;
513                   break;
514                 }
515                 case VERTICAL:
516                   break; // no kerning when vertical.
517               }
518             }
519
520             local = cursor;
521             osg::Vec2 bearing( horizontal ? glyph->getHorizontalBearing()
522                                           : glyph->getVerticalBearing() );
523             local.x() += bearing.x() * wr;
524             local.y() += bearing.y() * hr;
525
526             // set up the coords of the quad
527             osg::Vec2 upLeft = local + osg::Vec2(0.f, height);
528             osg::Vec2 lowLeft = local;
529             osg::Vec2 lowRight = local + osg::Vec2(width, 0.f);
530             osg::Vec2 upRight = local + osg::Vec2(width, height);
531
532             // move the cursor onto the next character.
533             // also expand bounding box
534             switch( _layout )
535             {
536               case LEFT_TO_RIGHT:
537                 cursor.x() += glyph->getHorizontalAdvance() * wr;
538                 bb.expandBy(lowLeft.x(), lowLeft.y());
539                 bb.expandBy(upRight.x(), upRight.y());
540                 break;
541               case VERTICAL:
542                 cursor.y() -= glyph->getVerticalAdvance() * hr;
543                 bb.expandBy(upLeft.x(), upLeft.y());
544                 bb.expandBy(lowRight.x(), lowRight.y());
545                 break;
546               case RIGHT_TO_LEFT:
547                 bb.expandBy(lowRight.x(), lowRight.y());
548                 bb.expandBy(upLeft.x(), upLeft.y());
549                 break;
550             }
551             previous_charcode = charcode;
552           }
553         }
554
555         // skip over spaces and return.
556         while( itr != text.end() && *itr == ' ' )
557           ++itr;
558         if( itr != text.end() && *itr == '\n' )
559           ++itr;
560       }
561       else
562       {
563         ++itr;
564       }
565
566       // move to new line.
567       switch( _layout )
568       {
569         case LEFT_TO_RIGHT:
570         {
571           startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
572           cursor = startOfLine_coords;
573           previous_charcode = 0;
574           break;
575         }
576         case RIGHT_TO_LEFT:
577         {
578           startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
579           cursor = startOfLine_coords;
580           previous_charcode = 0;
581           break;
582         }
583         case VERTICAL:
584         {
585           startOfLine_coords.x() += _characterHeight * (1.0 + _lineSpacing)
586                                   / getCharacterAspectRatio();
587           cursor = startOfLine_coords;
588           previous_charcode = 0;
589           break;
590         }
591       }
592     }
593
594     const_cast<TextOSG*>(this)->_maximumWidth = max_width_safe;
595
596     return bb.size();
597   }
598
599   //----------------------------------------------------------------------------
600   osg::BoundingBox
601 #if OSG_VERSION_LESS_THAN(3,3,2)
602   Text::TextOSG::computeBound()
603 #else
604   Text::TextOSG::computeBoundingBox()
605 #endif
606   const
607   {
608     osg::BoundingBox bb =
609 #if OSG_VERSION_LESS_THAN(3,3,2)
610       osgText::Text::computeBound();
611 #else
612       osgText::Text::computeBoundingBox();
613 #endif
614
615 #if OSG_VERSION_LESS_THAN(3,1,0)
616     if( bb.valid() )
617     {
618       // TODO bounding box still doesn't seem always right (eg. with center
619       //      horizontal alignment not completely accurate)
620       bb._min.y() += _offset.y();
621       bb._max.y() += _offset.y();
622     }
623 #endif
624
625     return bb;
626   }
627
628   //----------------------------------------------------------------------------
629   void Text::TextOSG::computePositions(unsigned int contextID) const
630   {
631     if( _textureGlyphQuadMap.empty() || _layout == VERTICAL )
632       return osgText::Text::computePositions(contextID);
633
634     // TODO check when it can be larger
635     assert( _textureGlyphQuadMap.size() == 1 );
636
637     const GlyphQuads& quads = _textureGlyphQuadMap.begin()->second;
638     const GlyphQuads::Glyphs& glyphs = quads._glyphs;
639 #if OSG_VERSION_LESS_THAN(3,3,5)
640     GlyphQuads::Coords2 const& coords = quads._coords;
641 #else
642     GlyphQuads::Coords2 refCoords = quads._coords;
643     GlyphQuads::Coords2::element_type &coords = *refCoords.get();
644 #endif
645
646     const GlyphQuads::LineNumbers& line_numbers = quads._lineNumbers;
647
648     float wr = _characterHeight / getCharacterAspectRatio();
649
650     size_t cur_line = static_cast<size_t>(-1);
651     for(size_t i = 0; i < glyphs.size(); ++i)
652     {
653       // Check horizontal offsets
654
655       bool first_char = cur_line != line_numbers[i];
656       cur_line = line_numbers[i];
657
658       bool last_char = (i + 1 == glyphs.size())
659                     || (cur_line != line_numbers[i + 1]);
660
661       if( first_char || last_char )
662       {
663         // From osg/src/osgText/Text.cpp:
664         //
665         // osg::Vec2 upLeft = local+osg::Vec2(0.0f-fHorizQuadMargin, ...);
666         // osg::Vec2 lowLeft = local+osg::Vec2(0.0f-fHorizQuadMargin, ...);
667         // osg::Vec2 lowRight = local+osg::Vec2(width+fHorizQuadMargin, ...);
668         // osg::Vec2 upRight = local+osg::Vec2(width+fHorizQuadMargin, ...);
669
670         float left = coords[i * 4].x(),
671               right = coords[i * 4 + 2].x(),
672               width = glyphs[i]->getWidth() * wr;
673
674         // (local + width + fHoriz) - (local - fHoriz) = width + 2*fHoriz | -width
675         float margin = 0.5f * (right - left - width),
676               cursor_x = left + margin
677                        - glyphs[i]->getHorizontalBearing().x() * wr;
678
679         if( first_char )
680         {
681           if( cur_line == 0 || cursor_x < _textBB._min.x() )
682             _textBB._min.x() = cursor_x;
683         }
684
685         if( last_char )
686         {
687           float cursor_w = cursor_x + glyphs[i]->getHorizontalAdvance() * wr;
688
689           if( cur_line == 0 || cursor_w > _textBB._max.x() )
690             _textBB._max.x() = cursor_w;
691         }
692       }
693     }
694
695     return osgText::Text::computePositions(contextID);
696   }
697
698   //----------------------------------------------------------------------------
699   const std::string Text::TYPE_NAME = "text";
700
701   //----------------------------------------------------------------------------
702   void Text::staticInit()
703   {
704     if( isInit<Text>() )
705       return;
706
707     osg::ref_ptr<TextOSG> Text::*text = &Text::_text;
708
709     addStyle("fill", "color", &TextOSG::setFill, text);
710     addStyle("background", "color", &TextOSG::setBackgroundColor, text);
711     addStyle("stroke", "color", &TextOSG::setStroke, text);
712     addStyle("character-size",
713              "numeric",
714              static_cast<
715                void (TextOSG::*)(float)
716              > (&TextOSG::setCharacterSize),
717              text);
718     addStyle("character-aspect-ratio",
719              "numeric",
720              &TextOSG::setCharacterAspect, text);
721     addStyle("line-height", "numeric", &TextOSG::setLineHeight, text);
722     addStyle("font-resolution", "numeric", &TextOSG::setFontResolution, text);
723     addStyle("padding", "numeric", &TextOSG::setBoundingBoxMargin, text);
724     //  TEXT              = 1 default
725     //  BOUNDINGBOX       = 2
726     //  FILLEDBOUNDINGBOX = 4
727     //  ALIGNMENT         = 8
728     addStyle<int>("draw-mode", "", &TextOSG::setDrawMode, text);
729     addStyle("max-width", "numeric", &TextOSG::setMaximumWidth, text);
730     addStyle("font", "", &Text::setFont);
731     addStyle("alignment", "", &Text::setAlignment);
732     addStyle("text", "", &Text::setText, false);
733
734     osgDB::Registry* reg = osgDB::Registry::instance();
735     if( !reg->getReaderWriterForExtension("ttf") )
736       SG_LOG(SG_GL, SG_ALERT, "canvas::Text: Missing 'ttf' font reader");
737   }
738
739   //----------------------------------------------------------------------------
740   Text::Text( const CanvasWeakPtr& canvas,
741               const SGPropertyNode_ptr& node,
742               const Style& parent_style,
743               Element* parent ):
744     Element(canvas, node, parent_style, parent),
745     _text( new Text::TextOSG(this) )
746   {
747     staticInit();
748
749     setDrawable(_text);
750     _text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
751     _text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
752     _text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
753
754     setupStyle();
755   }
756
757   //----------------------------------------------------------------------------
758   Text::~Text()
759   {
760
761   }
762
763   //----------------------------------------------------------------------------
764   void Text::setText(const char* text)
765   {
766     _text->setText(text, osgText::String::ENCODING_UTF8);
767   }
768
769   //----------------------------------------------------------------------------
770   void Text::setFont(const char* name)
771   {
772     _text->setFont( Canvas::getSystemAdapter()->getFont(name) );
773   }
774
775   //----------------------------------------------------------------------------
776   void Text::setAlignment(const char* align)
777   {
778     const std::string align_string(align);
779     if( 0 ) return;
780 #define ENUM_MAPPING(enum_val, string_val) \
781     else if( align_string == string_val )\
782       _text->setAlignment( osgText::Text::enum_val );
783 #include "text-alignment.hxx"
784 #undef ENUM_MAPPING
785     else
786     {
787       if( !align_string.empty() )
788         SG_LOG
789         (
790           SG_GENERAL,
791           SG_WARN,
792           "canvas::Text: unknown alignment '" << align_string << "'"
793         );
794       _text->setAlignment(osgText::Text::LEFT_BASE_LINE);
795     }
796   }
797
798   //----------------------------------------------------------------------------
799 #if 0
800   const char* Text::getAlignment() const
801   {
802     switch( _text->getAlignment() )
803     {
804 #define ENUM_MAPPING(enum_val, string_val) \
805       case osgText::Text::enum_val:\
806         return string_val;
807 #include "text-alignment.hxx"
808 #undef ENUM_MAPPING
809       default:
810         return "unknown";
811     }
812   }
813 #endif
814
815   //----------------------------------------------------------------------------
816   int Text::heightForWidth(int w) const
817   {
818     return _text->sizeForWidth(w).y();
819   }
820
821   //----------------------------------------------------------------------------
822   int Text::maxWidth() const
823   {
824     return _text->sizeForWidth(INT_MAX).x();
825   }
826
827   //----------------------------------------------------------------------------
828   size_t Text::lineCount() const
829   {
830     return _text->lineCount();
831   }
832
833   //----------------------------------------------------------------------------
834   size_t Text::lineLength(size_t line) const
835   {
836     return _text->lineAt(line).size();
837   }
838
839   //----------------------------------------------------------------------------
840   osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const
841   {
842     return _text->nearestLine(pos.y()).nearestCursor(pos.x());
843   }
844
845   //----------------------------------------------------------------------------
846   osg::Vec2 Text::getCursorPos(size_t line, size_t character) const
847   {
848     return _text->lineAt(line).cursorPos(character);
849   }
850
851   //----------------------------------------------------------------------------
852   osg::StateSet* Text::getOrCreateStateSet()
853   {
854     if( !_transform.valid() )
855       return 0;
856
857     // Only check for StateSet on Transform, as the text stateset is shared
858     // between all text instances using the same font (texture).
859     return _transform->getOrCreateStateSet();
860   }
861
862 } // namespace canvas
863 } // namespace simgear