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