0026361: Visualization - move OpenGl_TextFormatter to Font_TextFormatter
[occt.git] / src / Font / Font_TextFormatter.cxx
1 // Created on: 2013-01-29
2 // Created by: Kirill GAVRILOV
3 // Copyright (c) 2013-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <Font_TextFormatter.hxx>
17
18 namespace
19 {
20   typedef NCollection_Vec2<Standard_ShortReal> Vec2f;
21
22   //! Auxiliary function to translate corners by the vector.
23   inline void move (NCollection_Vector< Vec2f >& theCorners,
24                     const Vec2f&                 theMoveVec,
25                     Standard_Integer             theCharLower,
26                     const Standard_Integer       theCharUpper)
27   {
28     for(; theCharLower <= theCharUpper; ++theCharLower)
29     {
30       theCorners.ChangeValue (theCharLower) += theMoveVec;
31     }
32   }
33
34   //! Auxiliary function to translate corners in vertical direction.
35   inline void moveY (NCollection_Vector<Vec2f>& theCorners,
36                      const Standard_ShortReal   theMoveVec,
37                      Standard_Integer           theCharLower,
38                      const Standard_Integer     theCharUpper)
39   {
40     for(; theCharLower <= theCharUpper; ++theCharLower)
41     {
42       theCorners.ChangeValue (theCharLower).y() += theMoveVec;
43     }
44   }
45
46   //! Apply floor to vector components.
47   //! @param  theVec - vector to change (by reference!)
48   //! @return modified vector
49   inline Vec2f& floor (Vec2f& theVec)
50   {
51     theVec.x() = std::floor (theVec.x());
52     theVec.y() = std::floor (theVec.y());
53     return theVec;
54   }
55
56 }
57
58 // =======================================================================
59 // function : Font_TextFormatter
60 // purpose  :
61 // =======================================================================
62 Font_TextFormatter::Font_TextFormatter()
63 : myAlignX (Graphic3d_HTA_LEFT),
64   myAlignY (Graphic3d_VTA_TOP),
65   myTabSize (8),
66   //
67   myPen (0.0f, 0.0f),
68   myRectsNb (0),
69   myLineSpacing (0.0f),
70   myAscender (0.0f),
71   myIsFormatted (false),
72   //
73   myLinesNb (0),
74   myRectLineStart (0),
75   myRectWordStart (0),
76   myNewLineNb(0),
77   myPenCurrLine (0.0f),
78   myBndTop   (0.0f),
79   myBndWidth (0.0f),
80   myMoveVec (0.0f, 0.0f)
81 {
82   //
83 }
84
85 // =======================================================================
86 // function : SetupAlignment
87 // purpose  :
88 // =======================================================================
89 void Font_TextFormatter::SetupAlignment (const Graphic3d_HorizontalTextAlignment theAlignX,
90                                          const Graphic3d_VerticalTextAlignment   theAlignY)
91 {
92   myAlignX = theAlignX;
93   myAlignY = theAlignY;
94 }
95
96 // =======================================================================
97 // function : Reset
98 // purpose  :
99 // =======================================================================
100 void Font_TextFormatter::Reset()
101 {
102   myIsFormatted = false;
103   myString.Clear();
104   myPen.x() = myPen.y() = 0.0f;
105   myRectsNb = 0;
106   myLineSpacing = myAscender = 0.0f;
107   myCorners.Clear();
108   myNewLines.Clear();
109 }
110
111 // =======================================================================
112 // function : Append
113 // purpose  :
114 // =======================================================================
115 void Font_TextFormatter::Append (const NCollection_String& theString,
116                                  Font_FTFont&              theFont)
117 {
118   if (theString.IsEmpty())
119   {
120     return;
121   }
122
123   myAscender    = Max (myAscender,    theFont.Ascender());
124   myLineSpacing = Max (myLineSpacing, theFont.LineSpacing());
125   myString     += theString;
126
127   int aSymbolsCounter = 0; // special counter to process tabulation symbols
128
129   // first pass - render all symbols using associated font on single ZERO baseline
130   for (NCollection_Utf8Iter anIter = theString.Iterator(); *anIter != 0;)
131   {
132     const Standard_Utf32Char aCharThis =   *anIter;
133     const Standard_Utf32Char aCharNext = *++anIter;
134
135     if (aCharThis == '\x0D' // CR  (carriage return)
136      || aCharThis == '\a'   // BEL (alarm)
137      || aCharThis == '\f'   // FF  (form feed) NP (new page)
138      || aCharThis == '\b'   // BS  (backspace)
139      || aCharThis == '\v')  // VT  (vertical tab)
140     {
141       continue; // skip unsupported carriage control codes
142     }
143     else if (aCharThis == '\x0A') // LF (line feed, new line)
144     {
145       aSymbolsCounter = 0;
146       myNewLines.Append (myPen.x());
147       continue; // will be processed on second pass
148     }
149     else if (aCharThis == ' ')
150     {
151       ++aSymbolsCounter;
152       myPen.x() += theFont.AdvanceX (' ', aCharNext);
153       continue;
154     }
155     else if (aCharThis == '\t')
156     {
157       const Standard_Integer aSpacesNum = (myTabSize - (aSymbolsCounter - 1) % myTabSize);
158       myPen.x() += theFont.AdvanceX (' ', aCharNext) * Standard_ShortReal(aSpacesNum);
159       aSymbolsCounter += aSpacesNum;
160       continue;
161     }
162
163     ++aSymbolsCounter;
164
165     myCorners.Append (myPen);
166
167     myPen.x() += theFont.AdvanceX (aCharThis, aCharNext);
168
169     ++myRectsNb;
170   }
171 }
172
173 // =======================================================================
174 // function : newLine
175 // purpose  :
176 // =======================================================================
177 void Font_TextFormatter::newLine (const Standard_Integer theLastRect)
178 {
179   if (myRectLineStart >= myRectsNb)
180   {
181     ++myLinesNb;
182     myPenCurrLine -= myLineSpacing;
183     return;
184   }
185
186   myMoveVec.y() = myPenCurrLine;
187   switch (myAlignX)
188   {
189     default:
190     case Graphic3d_HTA_LEFT:
191     {
192       myMoveVec.x() = (myNewLineNb > 0) ? -myNewLines.Value (myNewLineNb - 1) : 0.0f;
193       break;
194     }
195     case Graphic3d_HTA_RIGHT:
196     {
197       myMoveVec.x() = (myNewLineNb < myNewLines.Length())
198                     ? -myNewLines.Value (myNewLineNb)
199                     : -myPen.x();
200       break;
201     }
202     case Graphic3d_HTA_CENTER:
203     {
204       const Standard_ShortReal aFrom = (myNewLineNb > 0)
205                                      ? myNewLines.Value (myNewLineNb - 1)
206                                      : 0.0f;
207       const Standard_ShortReal aTo   = (myNewLineNb < myNewLines.Length())
208                                      ? myNewLines.Value (myNewLineNb)
209                                      : myPen.x();
210       myMoveVec.x() = -0.5f * (aFrom + aTo);
211       break;
212     }
213   }
214
215   move (myCorners, myMoveVec, myRectLineStart, theLastRect);
216
217   ++myLinesNb;
218   myPenCurrLine -= myLineSpacing;
219   myRectLineStart = myRectWordStart = theLastRect + 1;
220 }
221
222 // =======================================================================
223 // function : Format
224 // purpose  :
225 // =======================================================================
226 void Font_TextFormatter::Format()
227 {
228   if (myRectsNb == 0 || myIsFormatted)
229   {
230     return;
231   }
232
233   myIsFormatted = true;
234   myLinesNb = myRectLineStart = myRectWordStart = 0;
235   myBndTop     = 0.0f;
236   myBndWidth   = 0.0f;
237   myMoveVec.x() = myMoveVec.y() = 0.0f;
238
239   // split text into lines and apply horizontal alignment
240   myPenCurrLine = -myAscender;
241   Standard_Integer aRectIter = 0;
242   myNewLineNb = 0;
243   Standard_ShortReal aMaxLineWidth = -1.0f;
244   for (NCollection_Utf8Iter anIter = myString.Iterator(); *anIter != 0; ++anIter)
245   {
246     const Standard_Utf32Char aCharThis = *anIter;
247     if (aCharThis == '\x0D' // CR  (carriage return)
248      || aCharThis == '\a'   // BEL (alarm)
249      || aCharThis == '\f'   // FF  (form feed) NP (new page)
250      || aCharThis == '\b'   // BS  (backspace)
251      || aCharThis == '\v')  // VT  (vertical tab)
252     {
253       continue; // skip unsupported carriage control codes
254     }
255     else if (aCharThis == '\x0A') // LF (line feed, new line)
256     {
257       // calculate max line width
258       if (myNewLineNb == 0)
259       {
260         aMaxLineWidth = myNewLines.Value(0);
261       }
262       else
263       {
264         aMaxLineWidth = Max (aMaxLineWidth, myNewLines.Value (myNewLineNb) - myNewLines.Value (myNewLineNb - 1));
265       }
266
267       const Standard_Integer aLastRect = aRectIter - 1; // last rect on current line
268       newLine (aLastRect);
269       ++myNewLineNb;
270       continue;
271     }
272     else if (aCharThis == ' '
273           || aCharThis == '\t')
274     {
275       myRectWordStart = aRectIter;
276       continue;
277     }
278
279     ++aRectIter;
280   }
281
282   // If only one line
283   if (aMaxLineWidth < 0.0f)
284   {
285     aMaxLineWidth = myPen.x();
286   }
287   else // Consider last line
288   {
289     aMaxLineWidth = Max (aMaxLineWidth, myPen.x() - myNewLines.Value (myNewLineNb - 1));
290   }
291
292   myBndWidth = aMaxLineWidth;
293
294   // move last line
295   newLine (myRectsNb - 1);
296
297   // apply vertical alignment style
298   if (myAlignY == Graphic3d_VTA_BOTTOM)
299   {
300     myBndTop = -myLineSpacing - myPenCurrLine;
301     moveY (myCorners, myBndTop, 0, myRectsNb - 1);
302   }
303   else if (myAlignY == Graphic3d_VTA_CENTER)
304   {
305     myBndTop = 0.5f * (myLineSpacing * Standard_ShortReal(myLinesNb));
306     moveY (myCorners, myBndTop, 0, myRectsNb - 1);
307   }
308 }