0024272: Provide basic text formatting routines for Font_BRepFont
[occt.git] / src / Font / Font_TextFormatter.cxx
CommitLineData
317d68c9 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
18namespace
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// =======================================================================
62Font_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// =======================================================================
89void 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// =======================================================================
100void 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// =======================================================================
115void 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// =======================================================================
177void 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// =======================================================================
226void 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;
317d68c9 301 }
302 else if (myAlignY == Graphic3d_VTA_CENTER)
303 {
304 myBndTop = 0.5f * (myLineSpacing * Standard_ShortReal(myLinesNb));
ac84fcf6 305 }
306 else if (myAlignY == Graphic3d_VTA_TOPFIRSTLINE)
307 {
308 myBndTop = myAscender;
309 }
310
311 if (myAlignY != Graphic3d_VTA_TOP)
312 {
317d68c9 313 moveY (myCorners, myBndTop, 0, myRectsNb - 1);
314 }
315}