mirror of
https://git.dev.opencascade.org/repos/occt.git
synced 2025-05-31 11:15:31 +03:00
Apply new regex replacement with method's guards in .cxx Update GH workflow with style checking
444 lines
13 KiB
C++
444 lines
13 KiB
C++
// Created on: 2013-01-29
|
|
// Created by: Kirill GAVRILOV
|
|
// Copyright (c) 2013-2014 OPEN CASCADE SAS
|
|
//
|
|
// This file is part of Open CASCADE Technology software library.
|
|
//
|
|
// This library is free software; you can redistribute it and/or modify it under
|
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
|
// by the Free Software Foundation, with special exception defined in the file
|
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
|
// distribution for complete text of the license and disclaimer of any warranty.
|
|
//
|
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
|
// commercial license or contractual agreement.
|
|
|
|
#include <Font_TextFormatter.hxx>
|
|
|
|
#include <Font_FTFont.hxx>
|
|
|
|
#include <Precision.hxx>
|
|
|
|
IMPLEMENT_STANDARD_RTTIEXT(Font_TextFormatter, Standard_Transient)
|
|
|
|
namespace
|
|
{
|
|
typedef NCollection_Vec2<Standard_ShortReal> Vec2f;
|
|
|
|
//! Auxiliary function to translate corners by the vector.
|
|
inline void move(NCollection_Vector<Vec2f>& theCorners,
|
|
const Vec2f& theMoveVec,
|
|
Standard_Integer theCharLower,
|
|
const Standard_Integer theCharUpper)
|
|
{
|
|
for (; theCharLower <= theCharUpper; ++theCharLower)
|
|
{
|
|
theCorners.ChangeValue(theCharLower) += theMoveVec;
|
|
}
|
|
}
|
|
|
|
//! Auxiliary function to translate corners in vertical direction.
|
|
inline void moveY(NCollection_Vector<Vec2f>& theCorners,
|
|
const Standard_ShortReal theMoveVec,
|
|
Standard_Integer theCharLower,
|
|
const Standard_Integer theCharUpper)
|
|
{
|
|
for (; theCharLower <= theCharUpper; ++theCharLower)
|
|
{
|
|
theCorners.ChangeValue(theCharLower).y() += theMoveVec;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
//=================================================================================================
|
|
|
|
Font_TextFormatter::Font_TextFormatter()
|
|
: myAlignX(Graphic3d_HTA_LEFT),
|
|
myAlignY(Graphic3d_VTA_TOP),
|
|
myTabSize(8),
|
|
myWrappingWidth(0.0f),
|
|
myIsWordWrapping(true),
|
|
myLastSymbolWidth(0.0f),
|
|
myMaxSymbolWidth(0.0f),
|
|
//
|
|
myPen(0.0f, 0.0f),
|
|
myLineSpacing(0.0f),
|
|
myAscender(0.0f),
|
|
myIsFormatted(false),
|
|
//
|
|
myLinesNb(0),
|
|
myRectLineStart(0),
|
|
myNewLineNb(0),
|
|
myPenCurrLine(0.0f),
|
|
myBndTop(0.0f),
|
|
myBndWidth(0.0f),
|
|
myMoveVec(0.0f, 0.0f)
|
|
{
|
|
//
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Font_TextFormatter::SetupAlignment(const Graphic3d_HorizontalTextAlignment theAlignX,
|
|
const Graphic3d_VerticalTextAlignment theAlignY)
|
|
{
|
|
myAlignX = theAlignX;
|
|
myAlignY = theAlignY;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Font_TextFormatter::Reset()
|
|
{
|
|
myIsFormatted = false;
|
|
myString.Clear();
|
|
myPen.x() = myPen.y() = 0.0f;
|
|
myLineSpacing = myAscender = 0.0f;
|
|
myCorners.Clear();
|
|
myNewLines.Clear();
|
|
|
|
myLastSymbolWidth = 0.0f;
|
|
myMaxSymbolWidth = 0.0f;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Font_TextFormatter::Append(const NCollection_String& theString, Font_FTFont& theFont)
|
|
{
|
|
if (theString.IsEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
myAscender = Max(myAscender, theFont.Ascender());
|
|
myLineSpacing = Max(myLineSpacing, theFont.LineSpacing());
|
|
myString += theString;
|
|
|
|
int aSymbolsCounter = 0; // special counter to process tabulation symbols
|
|
|
|
// first pass - render all symbols using associated font on single ZERO baseline
|
|
for (Font_TextFormatter::Iterator aFormatterIt(*this); aFormatterIt.More(); aFormatterIt.Next())
|
|
{
|
|
const Standard_Utf32Char aCharThis = aFormatterIt.Symbol();
|
|
const Standard_Utf32Char aCharNext = aFormatterIt.SymbolNext();
|
|
|
|
Standard_ShortReal anAdvanceX = 0;
|
|
if (IsCommandSymbol(aCharThis))
|
|
{
|
|
continue; // skip unsupported carriage control codes
|
|
}
|
|
else if (aCharThis == '\x0A') // LF (line feed, new line)
|
|
{
|
|
aSymbolsCounter = 0;
|
|
myNewLines.Append(myPen.x());
|
|
anAdvanceX = 0; // the symbol has null width
|
|
}
|
|
else if (aCharThis == ' ')
|
|
{
|
|
anAdvanceX = theFont.AdvanceX(' ', aCharNext);
|
|
}
|
|
else if (aCharThis == '\t')
|
|
{
|
|
const Standard_Integer aSpacesNum = (myTabSize - (aSymbolsCounter - 1) % myTabSize);
|
|
anAdvanceX = theFont.AdvanceX(' ', aCharNext) * Standard_ShortReal(aSpacesNum);
|
|
aSymbolsCounter += aSpacesNum;
|
|
}
|
|
else
|
|
{
|
|
anAdvanceX = theFont.AdvanceX(aCharThis, aCharNext);
|
|
}
|
|
++aSymbolsCounter;
|
|
myCorners.Append(myPen);
|
|
myPen.x() += anAdvanceX;
|
|
myMaxSymbolWidth = Max(myMaxSymbolWidth, anAdvanceX);
|
|
}
|
|
myLastSymbolWidth = myPen.x() - myCorners.Last().x();
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Font_TextFormatter::newLine(const Standard_Integer theLastRect,
|
|
const Standard_ShortReal theMaxLineWidth)
|
|
{
|
|
Standard_Integer aFirstCornerId = myRectLineStart;
|
|
Standard_Integer aLastCornerId = theLastRect;
|
|
|
|
if (aFirstCornerId >= myCorners.Length())
|
|
{
|
|
++myLinesNb;
|
|
myPenCurrLine -= myLineSpacing;
|
|
return;
|
|
}
|
|
|
|
Standard_ShortReal aXMin = BottomLeft(aFirstCornerId).x();
|
|
Font_Rect aBndBox;
|
|
GlyphBoundingBox(aLastCornerId, aBndBox);
|
|
Standard_ShortReal aXMax = aBndBox.Right;
|
|
|
|
myMoveVec.y() = myPenCurrLine;
|
|
switch (myAlignX)
|
|
{
|
|
default:
|
|
case Graphic3d_HTA_LEFT:
|
|
myMoveVec.x() = -aXMin;
|
|
break;
|
|
case Graphic3d_HTA_RIGHT:
|
|
myMoveVec.x() = -aXMin + (theMaxLineWidth - (aXMax - aXMin)) - theMaxLineWidth;
|
|
break;
|
|
case Graphic3d_HTA_CENTER:
|
|
myMoveVec.x() = -aXMin + 0.5f * (theMaxLineWidth - (aXMax - aXMin)) - 0.5f * theMaxLineWidth;
|
|
break;
|
|
}
|
|
|
|
move(myCorners, myMoveVec, myRectLineStart, theLastRect);
|
|
|
|
++myLinesNb;
|
|
myPenCurrLine -= myLineSpacing;
|
|
myRectLineStart = theLastRect + 1;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
void Font_TextFormatter::Format()
|
|
{
|
|
if (myCorners.Length() == 0 || myIsFormatted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
myIsFormatted = true;
|
|
myLinesNb = myRectLineStart = 0;
|
|
myBndTop = 0.0f;
|
|
myBndWidth = 0.0f;
|
|
myMoveVec.x() = myMoveVec.y() = 0.0f;
|
|
|
|
// split text into lines and apply horizontal alignment
|
|
myPenCurrLine = -myAscender;
|
|
Standard_Integer aRectIter = 0;
|
|
myNewLineNb = 0;
|
|
|
|
Standard_ShortReal aMaxLineWidth = Wrapping();
|
|
if (HasWrapping())
|
|
{
|
|
// it is not possible to wrap less than symbol width
|
|
aMaxLineWidth = Max(aMaxLineWidth, MaximumSymbolWidth());
|
|
}
|
|
else
|
|
{
|
|
if (myNewLines.IsEmpty()) // If only one line
|
|
{
|
|
aMaxLineWidth = myPen.x();
|
|
}
|
|
else
|
|
{
|
|
for (int aLineIt = 0; aLineIt < myNewLines.Size(); aLineIt++)
|
|
{
|
|
aMaxLineWidth = Max(aMaxLineWidth, LineWidth(aLineIt));
|
|
}
|
|
// clang-format off
|
|
aMaxLineWidth = Max (aMaxLineWidth, LineWidth (myNewLines.Size())); // processing the last line also
|
|
// clang-format on
|
|
}
|
|
}
|
|
|
|
Standard_Utf32Char aCharPrev = 0;
|
|
for (Font_TextFormatter::Iterator aFormatterIt(*this); aFormatterIt.More(); aFormatterIt.Next())
|
|
{
|
|
const Standard_Utf32Char aCharThis = aFormatterIt.Symbol();
|
|
aRectIter = aFormatterIt.SymbolPosition();
|
|
|
|
if (aCharThis == '\x0A') // LF (line feed, new line)
|
|
{
|
|
const Standard_Integer aLastRect = aRectIter; // last rect on current line
|
|
newLine(aLastRect, aMaxLineWidth);
|
|
++myNewLineNb;
|
|
continue;
|
|
}
|
|
else if (HasWrapping()) // wrap lines longer than maximum width
|
|
{
|
|
Standard_Integer aFirstCornerId = myRectLineStart;
|
|
|
|
Font_Rect aBndBox;
|
|
GlyphBoundingBox(aRectIter, aBndBox);
|
|
const Standard_ShortReal aNextXPos = aBndBox.Right - BottomLeft(aFirstCornerId).x();
|
|
Standard_Boolean isCurWordFits = true;
|
|
if (myIsWordWrapping && IsSeparatorSymbol(aCharPrev))
|
|
{
|
|
for (Font_TextFormatter::Iterator aWordIt = aFormatterIt; aWordIt.More(); aWordIt.Next())
|
|
{
|
|
if (IsSeparatorSymbol(aWordIt.Symbol()))
|
|
{
|
|
break;
|
|
}
|
|
float aWordWidthPx = myCorners[aWordIt.SymbolPosition()].x() - myCorners[aRectIter].x();
|
|
if (aNextXPos + aWordWidthPx > aMaxLineWidth)
|
|
{
|
|
isCurWordFits = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (aNextXPos > aMaxLineWidth
|
|
|| !isCurWordFits) // wrap the line and do processing of the symbol
|
|
{
|
|
const Standard_Integer aLastRect = aRectIter - 1; // last rect on current line
|
|
newLine(aLastRect, aMaxLineWidth);
|
|
}
|
|
}
|
|
aCharPrev = aCharThis;
|
|
}
|
|
|
|
myBndWidth = aMaxLineWidth;
|
|
|
|
// move last line
|
|
newLine(myCorners.Length() - 1, aMaxLineWidth);
|
|
|
|
// apply vertical alignment style
|
|
if (myAlignY == Graphic3d_VTA_BOTTOM)
|
|
{
|
|
myBndTop = -myLineSpacing - myPenCurrLine;
|
|
}
|
|
else if (myAlignY == Graphic3d_VTA_CENTER)
|
|
{
|
|
myBndTop = 0.5f * (myLineSpacing * Standard_ShortReal(myLinesNb));
|
|
}
|
|
else if (myAlignY == Graphic3d_VTA_TOPFIRSTLINE)
|
|
{
|
|
myBndTop = myAscender;
|
|
}
|
|
|
|
if (myAlignY != Graphic3d_VTA_TOP)
|
|
{
|
|
moveY(myCorners, myBndTop, 0, myCorners.Length() - 1);
|
|
}
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
Standard_Boolean Font_TextFormatter::GlyphBoundingBox(const Standard_Integer theIndex,
|
|
Font_Rect& theBndBox) const
|
|
{
|
|
if (theIndex < 0 || theIndex >= Corners().Size())
|
|
{
|
|
return Standard_False;
|
|
}
|
|
|
|
const NCollection_Vec2<Standard_ShortReal>& aLeftCorner = BottomLeft(theIndex);
|
|
theBndBox.Left = aLeftCorner.x();
|
|
theBndBox.Right = aLeftCorner.x() + myLastSymbolWidth;
|
|
theBndBox.Bottom = aLeftCorner.y();
|
|
theBndBox.Top = theBndBox.Bottom + myLineSpacing;
|
|
if (theIndex + 1 >= myCorners.Length())
|
|
{
|
|
// the last symbol
|
|
return Standard_True;
|
|
}
|
|
|
|
const NCollection_Vec2<Standard_ShortReal>& aNextLeftCorner = BottomLeft(theIndex + 1);
|
|
if (Abs(aLeftCorner.y() - aNextLeftCorner.y()) < Precision::Confusion()) // in the same row
|
|
{
|
|
theBndBox.Right = aNextLeftCorner.x();
|
|
}
|
|
else
|
|
{
|
|
// the next symbol is on the next row either by '\n' or by wrapping
|
|
Standard_ShortReal aLineWidth = LineWidth(LineIndex(theIndex));
|
|
theBndBox.Left = aLeftCorner.x();
|
|
switch (myAlignX)
|
|
{
|
|
case Graphic3d_HTA_LEFT:
|
|
theBndBox.Right = aLineWidth;
|
|
break;
|
|
case Graphic3d_HTA_RIGHT:
|
|
theBndBox.Right = myBndWidth;
|
|
break;
|
|
case Graphic3d_HTA_CENTER:
|
|
theBndBox.Right = 0.5f * (myBndWidth + aLineWidth);
|
|
break;
|
|
}
|
|
}
|
|
return Standard_True;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
Standard_Boolean Font_TextFormatter::IsLFSymbol(const Standard_Integer theIndex) const
|
|
{
|
|
Font_Rect aBndBox;
|
|
if (!GlyphBoundingBox(theIndex, aBndBox))
|
|
{
|
|
return Standard_False;
|
|
}
|
|
|
|
return Abs(aBndBox.Right - aBndBox.Left) < Precision::Confusion();
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
Standard_ShortReal Font_TextFormatter::FirstPosition() const
|
|
{
|
|
switch (myAlignX)
|
|
{
|
|
default:
|
|
case Graphic3d_HTA_LEFT:
|
|
return 0;
|
|
case Graphic3d_HTA_RIGHT:
|
|
return myBndWidth;
|
|
case Graphic3d_HTA_CENTER:
|
|
return 0.5f * myBndWidth;
|
|
}
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
Standard_Integer Font_TextFormatter::LinePositionIndex(const Standard_Integer theIndex) const
|
|
{
|
|
Standard_Integer anIndex = 0;
|
|
|
|
Standard_ShortReal anIndexHeight = BottomLeft(theIndex).y();
|
|
for (Standard_Integer aPrevIndex = theIndex - 1; aPrevIndex >= 0; aPrevIndex--)
|
|
{
|
|
if (BottomLeft(aPrevIndex).y() > anIndexHeight)
|
|
{
|
|
break;
|
|
}
|
|
anIndex++;
|
|
}
|
|
return anIndex;
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
Standard_Integer Font_TextFormatter::LineIndex(const Standard_Integer theIndex) const
|
|
{
|
|
if (myLineSpacing < 0.0f)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return (Standard_Integer)Abs((BottomLeft(theIndex).y() + myAscender) / myLineSpacing);
|
|
}
|
|
|
|
//=================================================================================================
|
|
|
|
Standard_ShortReal Font_TextFormatter::LineWidth(const Standard_Integer theIndex) const
|
|
{
|
|
if (theIndex < 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (theIndex < myNewLines.Length())
|
|
{
|
|
return theIndex == 0 ? myNewLines[0] : myNewLines[theIndex] - myNewLines[theIndex - 1];
|
|
}
|
|
|
|
if (theIndex == myNewLines.Length()) // the last line
|
|
{
|
|
return theIndex == 0 ? myPen.x() : myPen.x() - myNewLines[theIndex - 1];
|
|
}
|
|
|
|
return 0;
|
|
}
|