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 | |
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; |
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 | } |