Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

OSGTextFace.cpp

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------------*\
00002  *                                OpenSG                                     *
00003  *                                                                           *
00004  *                                                                           *
00005  *             Copyright (C) 2000-2002 by the OpenSG Forum                   *
00006  *                                                                           *
00007  *                            www.opensg.org                                 *
00008  *                                                                           *
00009  *   contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de          *
00010  *                                                                           *
00011 \*---------------------------------------------------------------------------*/
00012 /*---------------------------------------------------------------------------*\
00013  *                                License                                    *
00014  *                                                                           *
00015  * This library is free software; you can redistribute it and/or modify it   *
00016  * under the terms of the GNU Library General Public License as published    *
00017  * by the Free Software Foundation, version 2.                               *
00018  *                                                                           *
00019  * This library is distributed in the hope that it will be useful, but       *
00020  * WITHOUT ANY WARRANTY; without even the implied warranty of                *
00021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         *
00022  * Library General Public License for more details.                          *
00023  *                                                                           *
00024  * You should have received a copy of the GNU Library General Public         *
00025  * License along with this library; if not, write to the Free Software       *
00026  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                 *
00027  *                                                                           *
00028 \*---------------------------------------------------------------------------*/
00029 /*---------------------------------------------------------------------------*\
00030  *                                Changes                                    *
00031  *                                                                           *
00032  *                                                                           *
00033  *                                                                           *
00034  *                                                                           *
00035  *                                                                           *
00036  *                                                                           *
00037 \*---------------------------------------------------------------------------*/
00038 
00039 #ifdef _MSC_VER
00040 # pragma warning (disable: 4786)
00041 #endif
00042 
00043 #include "OSGTextFace.h"
00044 #include "OSGTextLayoutParam.h"
00045 #include "OSGTextLayoutResult.h"
00046 #ifdef __sgi
00047 # include <assert.h>
00048 # include <float.h>
00049 #else
00050 # include <cassert>
00051 # include <cfloat>
00052 #endif
00053 
00054 
00055 using namespace std;
00056 
00057 
00058 OSG_BEGIN_NAMESPACE
00059 
00060 
00061 //----------------------------------------------------------------------
00062 // Destructor
00063 // Author: pdaehne
00064 //----------------------------------------------------------------------
00065 TextFace::~TextFace() {}
00066 
00067 
00068 //----------------------------------------------------------------------
00069 // Lays out one line of text.
00070 // Author: pdaehne
00071 //----------------------------------------------------------------------
00072 void TextFace::layout(const string &utf8Text,
00073                       const TextLayoutParam &param,
00074                       TextLayoutResult &result)
00075 {
00076     // Convert the UTF8 string to a unicode string
00077     wstring text;
00078     convertUTF8ToUnicode(utf8Text, text);
00079 
00080     // Call the unicode layout method
00081     layout(text, param, result);
00082 }
00083 
00084 
00085 //----------------------------------------------------------------------
00086 // Lays out multiple lines of text
00087 // Author: pdaehne
00088 //----------------------------------------------------------------------
00089 void TextFace::layout(const vector<string> &lines,
00090                       const TextLayoutParam &param,
00091                       TextLayoutResult &result)
00092 {
00093     // Convert the UTF8 strings to unicode strings
00094     vector<wstring> unicodeLines(lines.size());
00095     vector<wstring>::iterator unicodeLineIt;
00096     vector<string>::const_iterator lineIt;
00097     for (lineIt = lines.begin(), unicodeLineIt = unicodeLines.begin();
00098          lineIt != lines.end(); ++lineIt, ++unicodeLineIt)
00099         convertUTF8ToUnicode(*lineIt, *unicodeLineIt);
00100 
00101     // Call the unicode layout method
00102     layout(unicodeLines, param, result);
00103 }
00104 
00105 
00106 //----------------------------------------------------------------------
00107 // Lays out multiple lines of text
00108 // Author: pdaehne
00109 //----------------------------------------------------------------------
00110 void TextFace::layout(const vector<wstring> &lines,
00111                       const TextLayoutParam &param,
00112                       TextLayoutResult &result)
00113 {
00114     // Initialize return values
00115     result.clear();
00116 
00117     TextLayoutParam lineParam = param;
00118     Vec2f offset, lineOffset;
00119     if (param.horizontal == true)
00120     {
00121         Real32 lineHeight = _horiAscent - _horiDescent;
00122         Real32 spacing = param.spacing * lineHeight;
00123         Real32 leading = spacing - lineHeight;
00124         Real32 fullHeight = spacing * lines.size() - leading;
00125         result.textBounds[1] = fullHeight;
00126         lineParam.minorAlignment = TextLayoutParam::ALIGN_FIRST;
00127         if (param.topToBottom == true)
00128         {
00129             lineOffset[1] = -spacing;
00130             switch (param.minorAlignment)
00131             {
00132                 default:
00133                 case TextLayoutParam::ALIGN_FIRST:
00134                     break;
00135                 case TextLayoutParam::ALIGN_BEGIN:
00136                     offset[1] = -_horiAscent;
00137                     break;
00138                 case TextLayoutParam::ALIGN_MIDDLE:
00139                     offset[1] = -_horiAscent + fullHeight / 2.f;
00140                     break;
00141                 case TextLayoutParam::ALIGN_END:
00142                     offset[1] = -_horiAscent + fullHeight;
00143                     break;
00144             }
00145         }
00146         else // topToBottom == false
00147         {
00148             lineOffset[1] = spacing;
00149             switch (param.minorAlignment)
00150             {
00151                 default:
00152                 case TextLayoutParam::ALIGN_FIRST:
00153                     break;
00154                 case TextLayoutParam::ALIGN_BEGIN:
00155                     offset[1] = -_horiDescent;
00156                     break;
00157                 case TextLayoutParam::ALIGN_MIDDLE:
00158                     offset[1] = -_horiDescent - fullHeight / 2.f;
00159                     break;
00160                 case TextLayoutParam::ALIGN_END:
00161                     offset[1] = -_horiDescent - fullHeight;
00162                     break;
00163             }
00164         }
00165     }
00166     else // horizontal == false
00167     {
00168         Real32 lineHeight = _vertDescent - _vertAscent;
00169         Real32 spacing = param.spacing * lineHeight;
00170         Real32 leading = spacing - lineHeight;
00171         Real32 fullHeight = spacing * lines.size() - leading;
00172         result.textBounds[0] = fullHeight;
00173         lineParam.minorAlignment = TextLayoutParam::ALIGN_FIRST;
00174         if (param.leftToRight == true)
00175         {
00176             lineOffset[0] = spacing;
00177             switch (param.minorAlignment)
00178             {
00179                 default:
00180                 case TextLayoutParam::ALIGN_FIRST:
00181                     break;
00182                 case TextLayoutParam::ALIGN_BEGIN:
00183                     offset[0] = -_vertAscent;
00184                     break;
00185                 case TextLayoutParam::ALIGN_MIDDLE:
00186                     offset[0] = -_vertAscent - fullHeight / 2.f;
00187                     break;
00188                 case TextLayoutParam::ALIGN_END:
00189                     offset[0] = -_vertAscent - fullHeight;
00190                     break;
00191             }
00192         }
00193         else // leftToRight == false
00194         {
00195             lineOffset[0] = -spacing;
00196             switch (param.minorAlignment)
00197             {
00198                 default:
00199                 case TextLayoutParam::ALIGN_FIRST:
00200                     break;
00201                 case TextLayoutParam::ALIGN_BEGIN:
00202                     offset[0] = -_vertDescent;
00203                     break;
00204                 case TextLayoutParam::ALIGN_MIDDLE:
00205                     offset[0] = -_vertDescent + fullHeight / 2.f;
00206                     break;
00207                 case TextLayoutParam::ALIGN_END:
00208                     offset[0] = -_vertDescent + fullHeight;
00209                     break;
00210             }
00211         }
00212     }
00213 
00214     result.lineBounds.reserve(lines.size());
00215     TextLayoutResult lineResult;
00216     Real32 maxExtend = 0.f;
00217     UInt32 lineIndex;
00218     for (lineIndex = 0; lineIndex < lines.size(); ++lineIndex)
00219     {
00220         lineParam.setLength(param.getLength(lineIndex));
00221         layout(lines[lineIndex], lineParam, lineResult);
00222         UInt32 i, numGlyphs = lineResult.getNumGlyphs();
00223         for (i = 0; i < numGlyphs; ++i)
00224         {
00225             result.indices.push_back(lineResult.indices[i]);
00226             result.positions.push_back(lineResult.positions[i] + offset);
00227         }
00228         assert(lineResult.lineBounds.empty() == false);
00229         result.lineBounds.push_back(lineResult.lineBounds.front());
00230         Real32 extend = lineResult.lineBounds.front()[param.horizontal == true ? 0 : 1];
00231         if (extend > maxExtend)
00232             maxExtend = extend;
00233         offset += lineOffset;
00234     }
00235     result.textBounds[param.horizontal == true ? 0 : 1] = maxExtend;
00236 }
00237 
00238 
00239 //----------------------------------------------------------------------
00240 // Calculates the bounding box of a text after layout
00241 // Author: pdaehne
00242 //----------------------------------------------------------------------
00243 void TextFace::calculateBoundingBox(const TextLayoutResult &layoutResult, Vec2f &lowerLeft, Vec2f &upperRight)
00244 {
00245     // Initialize bounding box
00246     lowerLeft.setValues(FLT_MAX, FLT_MAX);
00247     upperRight.setValues(-FLT_MAX, -FLT_MAX);
00248 
00249     UInt32 i, numGlyphs = layoutResult.getNumGlyphs();
00250     for (i = 0; i < numGlyphs; ++i)
00251     {
00252         const TextGlyph &glyph = getGlyph(layoutResult.indices[i]);
00253         Real32 width = glyph.getWidth();
00254         Real32 height = glyph.getHeight();
00255         // Don't handle invisible glyphs
00256         if ((width <= 0.f) || (height <= 0.f))
00257             continue;
00258 
00259         // Calculate coodinates
00260         const Vec2f &pos = layoutResult.positions[i];
00261         Real32 left = pos.x();
00262         Real32 right = left + glyph.getWidth();
00263         Real32 top = pos.y();
00264         Real32 bottom = top - glyph.getHeight();
00265 
00266         // Adjust bounding box
00267         if (lowerLeft[0] > left)
00268             lowerLeft[0] = left;
00269         if (upperRight[0] < right)
00270             upperRight[0] = right;
00271         if (upperRight[1] < top)
00272             upperRight[1] = top;
00273         if (lowerLeft[1] > bottom)
00274             lowerLeft[1] = bottom;
00275     }
00276 }
00277 
00278 
00279 //----------------------------------------------------------------------
00280 // Converts a UTF8 sequence to unicode
00281 // Author: pdaehne
00282 //----------------------------------------------------------------------
00283 static wchar_t utf8Char2Unicode(const char *&str)
00284 {
00285     char c = *str++;
00286     if ((c & 0x80) == 0)
00287         return c;
00288     if (((c & 0xc0) == 0x80) || ((c & 0xfe) == 0xfe)) // should never happen
00289         return (unsigned char)c;
00290     char mask = 0xe0, value = 0xc0;
00291     int i;
00292     for (i = 1; i < 6; ++i)
00293     {
00294         if ((c & mask) == value)
00295             break;
00296         value = mask;
00297         mask >>= 1;
00298     }
00299     wchar_t result = c & ~mask;
00300     for (; i > 0; --i)
00301     {
00302         c = *str++;
00303         if ((c & 0xc0) != 0x80) // should never happen
00304             ; // TODO
00305         result <<= 6;
00306         result |= c & 0x3f;
00307     }
00308     return result;
00309 }
00310 
00311 
00312 //----------------------------------------------------------------------
00313 // Converts a UTF8 encoded string to a unicode string
00314 // See the following document for more information:
00315 // RFC2279: UTF-8, a transformation format of ISO 10646
00316 // Author: pdaehne
00317 //----------------------------------------------------------------------
00318 void TextFace::convertUTF8ToUnicode(const string &utf8Text, wstring &text)
00319 {
00320     // Clear and prepare the result string
00321     text.erase();
00322     text.reserve(utf8Text.length());
00323 
00324     // Transform UTF8 sequences to UTF16 sequences
00325     const char *pos = utf8Text.c_str();
00326     while (*pos != '\0')
00327         text.append(1, utf8Char2Unicode(pos));
00328 }
00329 
00330 
00331 //----------------------------------------------------------------------
00332 // Justifies one line of text
00333 // Author: pdaehne
00334 //----------------------------------------------------------------------
00335 void TextFace::justifyLine(const TextLayoutParam &param,
00336                            const vector<UInt32> &spaceIndices,
00337                            Vec2f &currPos, TextLayoutResult &layoutResult) const
00338 {
00339     // We need at least two glyphs for justification
00340     UInt32 numGlyphs = layoutResult.positions.size();
00341     if (numGlyphs < 2)
00342         return;
00343 
00344     // Determine how much additional white space we have
00345     Real32 actualLength = osgabs(param.horizontal == true ? currPos.x() : currPos.y());
00346     Real32 delta = param.getLength(0) - actualLength;
00347 
00348     // Determine the amount of space that is added between glyphs and words
00349     UInt32 numSpaces = spaceIndices.size();
00350     numGlyphs -= 1 + numSpaces;
00351     // Space added between glyphs is by this factor smaller than space added between words
00352     Real32 factor = 0.5;
00353     Real32 spaceDelta = delta / (static_cast<Real32>(numSpaces) + static_cast<Real32>(numGlyphs) * factor);
00354     Real32 glyphDelta = spaceDelta * factor;
00355     Vec2f spaceOffset, glyphOffset;
00356     if (param.horizontal == true)
00357         if (param.leftToRight == true)
00358         {
00359             spaceOffset.setValues(spaceDelta, 0.f);
00360             glyphOffset.setValues(glyphDelta, 0.f);
00361         }
00362         else
00363         {
00364             spaceOffset.setValues(-spaceDelta, 0.f);
00365             glyphOffset.setValues(-glyphDelta, 0.f);
00366         }
00367     else
00368         if (param.topToBottom == true)
00369         {
00370             spaceOffset.setValues(0.f, -spaceDelta);
00371             glyphOffset.setValues(0.f, -glyphDelta);
00372         }
00373         else
00374         {
00375             spaceOffset.setValues(0.f, spaceDelta);
00376             glyphOffset.setValues(0.f, glyphDelta);
00377         }
00378 
00379     // Adjust the positions of the glyphs
00380     Vec2f offset;
00381     vector<UInt32>::const_iterator it = spaceIndices.begin();
00382     UInt32 i;
00383     for (i = 1; i < layoutResult.positions.size(); ++i)
00384     {
00385         if ((it == spaceIndices.end()) || (i < *it))
00386             offset += glyphOffset;
00387         else
00388         {
00389             offset += spaceOffset;
00390             ++it;
00391         }
00392         layoutResult.positions[i] += offset;
00393     }
00394     currPos += offset;
00395 }
00396 
00397 
00398 //----------------------------------------------------------------------
00399 // Adjusts the positions of glyphs, depending on the alignment
00400 // Author: pdaehne
00401 //----------------------------------------------------------------------
00402 void TextFace::adjustLineOrigin(const TextLayoutParam &param,
00403                                 const Vec2f &currPos,
00404                                 TextLayoutResult &layoutResult) const
00405 {
00406     Vec2f offset;
00407     if (param.horizontal == true)
00408     {
00409         switch (param.minorAlignment)
00410         {
00411             default:
00412             case TextLayoutParam::ALIGN_FIRST:  offset[1] = 0.f; break;
00413             case TextLayoutParam::ALIGN_BEGIN:  offset[1] = -_horiAscent; break;
00414             case TextLayoutParam::ALIGN_MIDDLE: offset[1] = -(_horiAscent + _horiDescent) / 2.f; break;
00415             case TextLayoutParam::ALIGN_END:    offset[1] = -_horiDescent; break;
00416         }
00417         if (param.leftToRight == true)
00418         {
00419             switch (param.majorAlignment)
00420             {
00421                 default:
00422                 case TextLayoutParam::ALIGN_FIRST:
00423                 case TextLayoutParam::ALIGN_BEGIN:
00424                     if (currPos.x() < 0)
00425                         offset[0] = -currPos.x();
00426                     break;
00427                 case TextLayoutParam::ALIGN_MIDDLE:
00428                     offset[0] = -currPos.x() / 2.f;
00429                     break;
00430                 case TextLayoutParam::ALIGN_END:
00431                     if (currPos.x() > 0)
00432                         offset[0] = -currPos.x();
00433                     break;
00434             }
00435         }
00436         else // leftToRight == false
00437         {
00438             switch (param.majorAlignment)
00439             {
00440                 default:
00441                 case TextLayoutParam::ALIGN_FIRST:
00442                 case TextLayoutParam::ALIGN_BEGIN:
00443                     if (currPos.x() > 0)
00444                         offset[0] = -currPos.x();
00445                     break;
00446                 case TextLayoutParam::ALIGN_MIDDLE:
00447                     offset[0] = -currPos.x() / 2.f;
00448                     break;
00449                 case TextLayoutParam::ALIGN_END:
00450                     if (currPos.x() < 0)
00451                         offset[0] = -currPos.x();
00452                     break;
00453             }
00454         }
00455     }
00456     else // param.horizontal == false
00457     {
00458         switch (param.minorAlignment)
00459         {
00460             default:
00461             case TextLayoutParam::ALIGN_FIRST:  offset[0] = 0.f; break;
00462             case TextLayoutParam::ALIGN_BEGIN:  offset[0] = -_vertAscent; break;
00463             case TextLayoutParam::ALIGN_MIDDLE: offset[0] = -(_vertAscent + _vertDescent) / 2.f; break;
00464             case TextLayoutParam::ALIGN_END:    offset[0] = -_vertDescent; break;
00465         }
00466         if (param.topToBottom == true)
00467         {
00468             switch (param.majorAlignment)
00469             {
00470                 default:
00471                 case TextLayoutParam::ALIGN_FIRST:
00472                 case TextLayoutParam::ALIGN_BEGIN:
00473                     if (currPos.y() > 0)
00474                         offset[1] = -currPos.y();
00475                     break;
00476                 case TextLayoutParam::ALIGN_MIDDLE:
00477                     offset[1] = -currPos.y() / 2.f;
00478                     break;
00479                 case TextLayoutParam::ALIGN_END:
00480                     if (currPos.y() < 0)
00481                         offset[1] = -currPos.y();
00482                     break;
00483             }
00484         }
00485         else // TopToBottom == false
00486         {
00487             switch (param.majorAlignment)
00488             {
00489                 default:
00490                 case TextLayoutParam::ALIGN_FIRST:
00491                 case TextLayoutParam::ALIGN_BEGIN:
00492                     if (currPos.y() < 0)
00493                         offset[1] = -currPos.y();
00494                     break;
00495                 case TextLayoutParam::ALIGN_MIDDLE:
00496                     offset[1] = -currPos.y() / 2.f;
00497                     break;
00498                 case TextLayoutParam::ALIGN_END:
00499                     if (currPos.y() > 0)
00500                         offset[1] = -currPos.y();
00501                     break;
00502             }
00503         }
00504     }
00505 
00506     // Adjust all glyph positions
00507     if ((offset.x() != 0.f) || (offset.y() != 0.f))
00508     {
00509         vector<Vec2f>::iterator it;
00510         for (it = layoutResult.positions.begin(); it != layoutResult.positions.end(); ++it)
00511             *it += offset;
00512     }
00513 }
00514 
00515 
00516 OSG_END_NAMESPACE
00517 
00518 
00519 /*------------------------------------------------------------------------*/
00520 /*                              cvs id's                                  */
00521 
00522 #ifdef OSG_SGI_CC
00523 #pragma set woff 1174
00524 #endif
00525 
00526 #ifdef OSG_LINUX_ICC
00527 #pragma warning( disable : 177 )
00528 #endif
00529 
00530 namespace
00531 {
00532     static OSG::Char8 cvsid_cpp[] = "@(#)$Id: OSGTextFace.cpp,v 1.2 2005/06/01 10:42:15 pdaehne Exp $";
00533     static OSG::Char8 cvsid_hpp[] = OSGTEXTFACE_HEADER_CVSID;
00534     static OSG::Char8 cvsid_inl[] = OSGTEXTFACE_INLINE_CVSID;
00535 }
00536 
00537 #ifdef __sgi
00538 #pragma reset woff 1174
00539 #endif

Generated on Thu Aug 25 04:11:18 2005 for OpenSG by  doxygen 1.4.3