1 // Created on: 2013-01-29
2 // Created by: Kirill GAVRILOV
3 // Copyright (c) 2013-2014 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
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.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
16 #include <Font_TextFormatter.hxx>
18 #include <Font_FTFont.hxx>
20 #include <Precision.hxx>
22 IMPLEMENT_STANDARD_RTTIEXT (Font_TextFormatter, Standard_Transient)
26 typedef NCollection_Vec2<Standard_ShortReal> Vec2f;
28 //! Auxiliary function to translate corners by the vector.
29 inline void move (NCollection_Vector< Vec2f >& theCorners,
30 const Vec2f& theMoveVec,
31 Standard_Integer theCharLower,
32 const Standard_Integer theCharUpper)
34 for(; theCharLower <= theCharUpper; ++theCharLower)
36 theCorners.ChangeValue (theCharLower) += theMoveVec;
40 //! Auxiliary function to translate corners in vertical direction.
41 inline void moveY (NCollection_Vector<Vec2f>& theCorners,
42 const Standard_ShortReal theMoveVec,
43 Standard_Integer theCharLower,
44 const Standard_Integer theCharUpper)
46 for(; theCharLower <= theCharUpper; ++theCharLower)
48 theCorners.ChangeValue (theCharLower).y() += theMoveVec;
54 // =======================================================================
55 // function : Font_TextFormatter
57 // =======================================================================
58 Font_TextFormatter::Font_TextFormatter()
59 : myAlignX (Graphic3d_HTA_LEFT),
60 myAlignY (Graphic3d_VTA_TOP),
62 myWrappingWidth (0.0f),
63 myLastSymbolWidth (0.0f),
64 myMaxSymbolWidth (0.0f),
69 myIsFormatted (false),
77 myMoveVec (0.0f, 0.0f)
82 // =======================================================================
83 // function : SetupAlignment
85 // =======================================================================
86 void Font_TextFormatter::SetupAlignment (const Graphic3d_HorizontalTextAlignment theAlignX,
87 const Graphic3d_VerticalTextAlignment theAlignY)
93 // =======================================================================
96 // =======================================================================
97 void Font_TextFormatter::Reset()
99 myIsFormatted = false;
101 myPen.x() = myPen.y() = 0.0f;
102 myLineSpacing = myAscender = 0.0f;
106 myLastSymbolWidth = 0.0f;
107 myMaxSymbolWidth = 0.0f;
110 // =======================================================================
113 // =======================================================================
114 void Font_TextFormatter::Append (const NCollection_String& theString,
115 Font_FTFont& theFont)
117 if (theString.IsEmpty())
122 myAscender = Max (myAscender, theFont.Ascender());
123 myLineSpacing = Max (myLineSpacing, theFont.LineSpacing());
124 myString += theString;
126 int aSymbolsCounter = 0; // special counter to process tabulation symbols
128 // first pass - render all symbols using associated font on single ZERO baseline
129 for (Font_TextFormatter::Iterator aFormatterIt (*this); aFormatterIt.More(); aFormatterIt.Next())
131 const Standard_Utf32Char aCharThis = aFormatterIt.Symbol();
132 const Standard_Utf32Char aCharNext = aFormatterIt.SymbolNext();
134 Standard_ShortReal anAdvanceX = 0;
135 if (IsCommandSymbol (aCharThis))
137 continue; // skip unsupported carriage control codes
139 else if (aCharThis == '\x0A') // LF (line feed, new line)
142 myNewLines.Append (myPen.x());
143 anAdvanceX = 0; // the symbol has null width
145 else if (aCharThis == ' ')
147 anAdvanceX = theFont.AdvanceX (' ', aCharNext);
149 else if (aCharThis == '\t')
151 const Standard_Integer aSpacesNum = (myTabSize - (aSymbolsCounter - 1) % myTabSize);
152 anAdvanceX = theFont.AdvanceX (' ', aCharNext) * Standard_ShortReal(aSpacesNum);
153 aSymbolsCounter += aSpacesNum;
157 anAdvanceX = theFont.AdvanceX (aCharThis, aCharNext);
160 myCorners.Append (myPen);
161 myPen.x() += anAdvanceX;
162 myMaxSymbolWidth = Max (myMaxSymbolWidth, anAdvanceX);
164 myLastSymbolWidth = myPen.x() - myCorners.Last().x();
167 // =======================================================================
168 // function : newLine
170 // =======================================================================
171 void Font_TextFormatter::newLine (const Standard_Integer theLastRect,
172 const Standard_ShortReal theMaxLineWidth)
174 Standard_Integer aFirstCornerId = myRectLineStart;
175 Standard_Integer aLastCornerId = theLastRect;
177 if (aFirstCornerId >= myCorners.Length())
180 myPenCurrLine -= myLineSpacing;
184 Standard_ShortReal aXMin = BottomLeft (aFirstCornerId).x();
186 GlyphBoundingBox (aLastCornerId, aBndBox);
187 Standard_ShortReal aXMax = aBndBox.Right;
189 myMoveVec.y() = myPenCurrLine;
193 case Graphic3d_HTA_LEFT: myMoveVec.x() = -aXMin;
195 case Graphic3d_HTA_RIGHT: myMoveVec.x() = -aXMin + (theMaxLineWidth - (aXMax - aXMin)) - theMaxLineWidth;
197 case Graphic3d_HTA_CENTER: myMoveVec.x() = -aXMin + 0.5f * (theMaxLineWidth - (aXMax - aXMin)) - 0.5f * theMaxLineWidth;
201 move (myCorners, myMoveVec, myRectLineStart, theLastRect);
204 myPenCurrLine -= myLineSpacing;
205 myRectLineStart = theLastRect + 1;
208 // =======================================================================
211 // =======================================================================
212 void Font_TextFormatter::Format()
214 if (myCorners.Length() == 0 || myIsFormatted)
219 myIsFormatted = true;
220 myLinesNb = myRectLineStart = 0;
223 myMoveVec.x() = myMoveVec.y() = 0.0f;
225 // split text into lines and apply horizontal alignment
226 myPenCurrLine = -myAscender;
227 Standard_Integer aRectIter = 0;
230 Standard_ShortReal aMaxLineWidth = Wrapping();
233 // it is not possible to wrap less than symbol width
234 aMaxLineWidth = Max (aMaxLineWidth, MaximumSymbolWidth());
238 if (myNewLines.IsEmpty()) // If only one line
240 aMaxLineWidth = myPen.x();
244 for (int aLineIt = 0; aLineIt < myNewLines.Size(); aLineIt++)
246 aMaxLineWidth = Max (aMaxLineWidth, LineWidth (aLineIt));
248 aMaxLineWidth = Max (aMaxLineWidth, LineWidth (myNewLines.Size())); // processing the last line also
252 for (Font_TextFormatter::Iterator aFormatterIt(*this);
253 aFormatterIt.More(); aFormatterIt.Next())
255 const Standard_Utf32Char aCharThis = aFormatterIt.Symbol();
256 aRectIter = aFormatterIt.SymbolPosition();
258 if (aCharThis == '\x0A') // LF (line feed, new line)
260 const Standard_Integer aLastRect = aRectIter; // last rect on current line
261 newLine (aLastRect, aMaxLineWidth);
265 else if (HasWrapping()) // wrap lines longer than maximum width
267 Standard_Integer aFirstCornerId = myRectLineStart;
270 GlyphBoundingBox (aRectIter, aBndBox);
271 const Standard_ShortReal aNextXPos = aBndBox.Right - BottomLeft (aFirstCornerId).x();
272 if (aNextXPos > aMaxLineWidth) // wrap the line and do processing of the symbol
274 const Standard_Integer aLastRect = aRectIter - 1; // last rect on current line
275 newLine (aLastRect, aMaxLineWidth);
280 myBndWidth = aMaxLineWidth;
283 newLine (myCorners.Length() - 1, aMaxLineWidth);
285 // apply vertical alignment style
286 if (myAlignY == Graphic3d_VTA_BOTTOM)
288 myBndTop = -myLineSpacing - myPenCurrLine;
290 else if (myAlignY == Graphic3d_VTA_CENTER)
292 myBndTop = 0.5f * (myLineSpacing * Standard_ShortReal(myLinesNb));
294 else if (myAlignY == Graphic3d_VTA_TOPFIRSTLINE)
296 myBndTop = myAscender;
299 if (myAlignY != Graphic3d_VTA_TOP)
301 moveY (myCorners, myBndTop, 0, myCorners.Length() - 1);
305 // =======================================================================
306 // function : GlyphBoundingBox
308 // =======================================================================
309 Standard_Boolean Font_TextFormatter::GlyphBoundingBox (const Standard_Integer theIndex,
310 Font_Rect& theBndBox) const
312 if (theIndex < 0 || theIndex >= Corners().Size())
314 return Standard_False;
317 const NCollection_Vec2<Standard_ShortReal>& aLeftCorner = BottomLeft (theIndex);
318 theBndBox.Left = aLeftCorner.x();
319 theBndBox.Right = aLeftCorner.x() + myLastSymbolWidth;
320 theBndBox.Bottom = aLeftCorner.y();
321 theBndBox.Top = theBndBox.Bottom + myLineSpacing;
322 if (theIndex + 1 >= myCorners.Length())
325 return Standard_True;
328 const NCollection_Vec2<Standard_ShortReal>& aNextLeftCorner = BottomLeft (theIndex + 1);
329 if (Abs (aLeftCorner.y() - aNextLeftCorner.y()) < Precision::Confusion()) // in the same row
331 theBndBox.Right = aNextLeftCorner.x();
335 // the next symbol is on the next row either by '\n' or by wrapping
336 Standard_ShortReal aLineWidth = LineWidth (LineIndex (theIndex));
337 theBndBox.Left = aLeftCorner.x();
340 case Graphic3d_HTA_LEFT: theBndBox.Right = aLineWidth; break;
341 case Graphic3d_HTA_RIGHT: theBndBox.Right = myBndWidth; break;
342 case Graphic3d_HTA_CENTER: theBndBox.Right = 0.5f * (myBndWidth + aLineWidth); break;
345 return Standard_True;
349 // =======================================================================
350 // function : IsLFSymbol
352 // =======================================================================
353 Standard_Boolean Font_TextFormatter::IsLFSymbol (const Standard_Integer theIndex) const
356 if (!GlyphBoundingBox (theIndex, aBndBox))
358 return Standard_False;
361 return Abs (aBndBox.Right - aBndBox.Left) < Precision::Confusion();
364 // =======================================================================
365 // function : FirstPosition
367 // =======================================================================
368 Standard_ShortReal Font_TextFormatter::FirstPosition() const
373 case Graphic3d_HTA_LEFT: return 0;
374 case Graphic3d_HTA_RIGHT: return myBndWidth;
375 case Graphic3d_HTA_CENTER: return 0.5f * myBndWidth;
379 // =======================================================================
380 // function : LinePositionIndex
382 // =======================================================================
383 Standard_Integer Font_TextFormatter::LinePositionIndex (const Standard_Integer theIndex) const
385 Standard_Integer anIndex = 0;
387 Standard_ShortReal anIndexHeight = BottomLeft (theIndex).y();
388 for (Standard_Integer aPrevIndex = theIndex-1; aPrevIndex >= 0; aPrevIndex--)
390 if (BottomLeft (aPrevIndex).y() > anIndexHeight)
399 // =======================================================================
400 // function : LineIndex
402 // =======================================================================
403 Standard_Integer Font_TextFormatter::LineIndex (const Standard_Integer theIndex) const
405 if (myLineSpacing < 0.0f)
410 return (Standard_Integer)Abs((BottomLeft (theIndex).y() + myAscender) / myLineSpacing);
413 // =======================================================================
414 // function : LineWidth
416 // =======================================================================
417 Standard_ShortReal Font_TextFormatter::LineWidth (const Standard_Integer theIndex) const
424 if (theIndex < myNewLines.Length())
426 return theIndex == 0 ? myNewLines[0] : myNewLines[theIndex] - myNewLines[theIndex -1];
429 if (theIndex == myNewLines.Length()) // the last line
431 return theIndex == 0 ? myPen.x() : myPen.x() - myNewLines[theIndex -1];