From: nds Date: Thu, 7 Mar 2019 13:28:20 +0000 (+0300) Subject: 0030537: Visualization - wrapping text in font text formatter X-Git-Url: http://git.dev.opencascade.org/gitweb/?a=commitdiff_plain;h=c0a38f3a5294cc0f168bc592f17f4ec9837d5a48;p=occt-copy.git 0030537: Visualization - wrapping text in font text formatter (cherry picked from commit 8016b09836d484f061a75ac303953f9d80c05109) # Conflicts: # src/Graphic3d/Graphic3d_Group.hxx # src/OpenGl/OpenGl_Text.hxx --- diff --git a/src/Font/Font_BRepTextBuilder.cxx b/src/Font/Font_BRepTextBuilder.cxx index 2304c19957..dc476acf60 100644 --- a/src/Font/Font_BRepTextBuilder.cxx +++ b/src/Font/Font_BRepTextBuilder.cxx @@ -15,12 +15,14 @@ #include +#include + // ======================================================================= // Function : Perfrom // Purpose : // ======================================================================= TopoDS_Shape Font_BRepTextBuilder::Perform (Font_BRepFont& theFont, - const Font_TextFormatter& theFormatter, + const Handle(Font_TextFormatter)& theFormatter, const gp_Ax3& thePenLoc) { gp_Trsf aTrsf; @@ -31,34 +33,20 @@ TopoDS_Shape Font_BRepTextBuilder::Perform (Font_BRepFont& theFont, myBuilder.MakeCompound (aResult); - Standard_Integer aSymbolCounter = 0; - Standard_Real aScaleUnits = theFont.Scale(); - for (NCollection_Utf8Iter anIter = theFormatter.String().Iterator(); *anIter != 0; ++anIter) + Standard_Real aScaleUnits = theFont.Scale(); + for (Font_TextFormatter::Iterator aFormatterIt (theFormatter, Font_TextFormatter::IterationFilter_ExcludeInvisible); + aFormatterIt .More(); aFormatterIt .Next()) { - const Standard_Utf32Char aCharCurr = *anIter; - if (aCharCurr == '\x0D' // CR (carriage return) - || aCharCurr == '\a' // BEL (alarm) - || aCharCurr == '\f' // FF (form feed) NP (new page) - || aCharCurr == '\b' // BS (backspace) - || aCharCurr == '\v' // VT (vertical tab) - || aCharCurr == ' ' - || aCharCurr == '\t' - || aCharCurr == '\n') - { - continue; // skip unsupported carriage control codes - } + const NCollection_Vec2& aCorner = theFormatter->BottomLeft (aFormatterIt.SymbolPosition()); - const NCollection_Vec2& aCorner = theFormatter.TopLeft (aSymbolCounter); aPen.SetCoord (aCorner.x() * aScaleUnits, aCorner.y() * aScaleUnits, 0.0); - aGlyphShape = theFont.RenderGlyph (aCharCurr); + aGlyphShape = theFont.RenderGlyph (aFormatterIt.Symbol()); if (!aGlyphShape.IsNull()) { aTrsf.SetTranslation (gp_Vec (aPen)); aGlyphShape.Move (aTrsf); myBuilder.Add (aResult, aGlyphShape); } - - ++aSymbolCounter; } aTrsf.SetTransformation (thePenLoc, gp_Ax3 (gp::XOY())); @@ -77,13 +65,13 @@ TopoDS_Shape Font_BRepTextBuilder::Perform (Font_BRepFont& const Graphic3d_HorizontalTextAlignment theHAlign, const Graphic3d_VerticalTextAlignment theVAlign) { - Font_TextFormatter aFormatter; + Handle(Font_TextFormatter) aFormatter = new Font_TextFormatter(); - aFormatter.Reset(); - aFormatter.SetupAlignment (theHAlign, theVAlign); + aFormatter->Reset(); + aFormatter->SetupAlignment (theHAlign, theVAlign); - aFormatter.Append (theString, *(reinterpret_cast (&theFont))); - aFormatter.Format(); + aFormatter->Append (theString, *(reinterpret_cast (&theFont))); + aFormatter->Format(); return Perform (theFont, aFormatter, thePenLoc); } diff --git a/src/Font/Font_BRepTextBuilder.hxx b/src/Font/Font_BRepTextBuilder.hxx index af03f79e3d..fe46b34a5e 100644 --- a/src/Font/Font_BRepTextBuilder.hxx +++ b/src/Font/Font_BRepTextBuilder.hxx @@ -17,9 +17,10 @@ #define Font_BRepTextBuilder_Header #include -#include #include +class Font_TextFormatter; + //! Represents class for applying text formatting. class Font_BRepTextBuilder { @@ -30,7 +31,7 @@ public: //! @param theFormatter formatter which defines alignment for the text //! @return result shape with pen transformation applied as shape location Standard_EXPORT TopoDS_Shape Perform (Font_BRepFont& theFont, - const Font_TextFormatter& theFormatter, + const Handle(Font_TextFormatter)& theFormatter, const gp_Ax3& thePenLoc = gp_Ax3()); //! Render text as BRep shape. //! @param theString text in UTF-8 encoding diff --git a/src/Font/Font_FTFont.cxx b/src/Font/Font_FTFont.cxx index 22a1297c4d..5e91d4e10f 100755 --- a/src/Font/Font_FTFont.cxx +++ b/src/Font/Font_FTFont.cxx @@ -406,14 +406,14 @@ Font_Rect Font_FTFont::BoundingBox (const NCollection_String& theS const Graphic3d_HorizontalTextAlignment theAlignX, const Graphic3d_VerticalTextAlignment theAlignY) { - Font_TextFormatter aFormatter; - aFormatter.SetupAlignment (theAlignX, theAlignY); - aFormatter.Reset(); + Handle(Font_TextFormatter) aFormatter = new Font_TextFormatter(); + aFormatter->SetupAlignment (theAlignX, theAlignY); + aFormatter->Reset(); - aFormatter.Append (theString, *this); - aFormatter.Format(); + aFormatter->Append (theString, *this); + aFormatter->Format(); Font_Rect aBndBox; - aFormatter.BndBox (aBndBox); + aFormatter->BndBox (aBndBox); return aBndBox; } diff --git a/src/Font/Font_TextFormatter.cxx b/src/Font/Font_TextFormatter.cxx index 520b3c9270..7cbb083e19 100644 --- a/src/Font/Font_TextFormatter.cxx +++ b/src/Font/Font_TextFormatter.cxx @@ -17,6 +17,10 @@ #include +#include + +IMPLEMENT_STANDARD_RTTIEXT (Font_TextFormatter, Standard_Transient) + namespace { typedef NCollection_Vec2 Vec2f; @@ -55,16 +59,17 @@ Font_TextFormatter::Font_TextFormatter() : myAlignX (Graphic3d_HTA_LEFT), myAlignY (Graphic3d_VTA_TOP), myTabSize (8), + myWrappingWidth (0.0f), + myLastSymbolWidth (0.0f), + myMaxSymbolWidth (0.0f), // myPen (0.0f, 0.0f), - myRectsNb (0), myLineSpacing (0.0f), myAscender (0.0f), myIsFormatted (false), // myLinesNb (0), myRectLineStart (0), - myRectWordStart (0), myNewLineNb(0), myPenCurrLine (0.0f), myBndTop (0.0f), @@ -94,10 +99,12 @@ void Font_TextFormatter::Reset() myIsFormatted = false; myString.Clear(); myPen.x() = myPen.y() = 0.0f; - myRectsNb = 0; myLineSpacing = myAscender = 0.0f; myCorners.Clear(); myNewLines.Clear(); + + myLastSymbolWidth = 0.0f; + myMaxSymbolWidth = 0.0f; } // ======================================================================= @@ -119,16 +126,15 @@ void Font_TextFormatter::Append (const NCollection_String& theString, int aSymbolsCounter = 0; // special counter to process tabulation symbols // first pass - render all symbols using associated font on single ZERO baseline - for (NCollection_Utf8Iter anIter = theString.Iterator(); *anIter != 0;) + Handle(Font_TextFormatter) aFormatter (this); + Standard_Utf32Char aCharThis; + for (Font_TextFormatter::Iterator aFormatterIt (aFormatter); aFormatterIt .More(); aFormatterIt .Next()) { - const Standard_Utf32Char aCharThis = *anIter; - const Standard_Utf32Char aCharNext = *++anIter; - - if (aCharThis == '\x0D' // CR (carriage return) - || aCharThis == '\a' // BEL (alarm) - || aCharThis == '\f' // FF (form feed) NP (new page) - || aCharThis == '\b' // BS (backspace) - || aCharThis == '\v') // VT (vertical tab) + aCharThis = aFormatterIt.Symbol(); + const Standard_Utf32Char aCharNext = aFormatterIt.SymbolNext(); + + Standard_ShortReal anAdvanceX = 0; + if (Font_TextFormatter::IsCommandSymbol (aCharThis)) { continue; // skip unsupported carriage control codes } @@ -136,79 +142,66 @@ void Font_TextFormatter::Append (const NCollection_String& theString, { aSymbolsCounter = 0; myNewLines.Append (myPen.x()); - continue; // will be processed on second pass + anAdvanceX = 0; // the symbol has null width } else if (aCharThis == ' ') { ++aSymbolsCounter; - myPen.x() += theFont.AdvanceX (' ', aCharNext); - continue; + anAdvanceX = theFont.AdvanceX (' ', aCharNext); } else if (aCharThis == '\t') { const Standard_Integer aSpacesNum = (myTabSize - (aSymbolsCounter - 1) % myTabSize); - myPen.x() += theFont.AdvanceX (' ', aCharNext) * Standard_ShortReal(aSpacesNum); + anAdvanceX = theFont.AdvanceX (' ', aCharNext) * Standard_ShortReal(aSpacesNum); aSymbolsCounter += aSpacesNum; - continue; } + else + anAdvanceX = theFont.AdvanceX (aCharThis, aCharNext); ++aSymbolsCounter; - myCorners.Append (myPen); - - myPen.x() += theFont.AdvanceX (aCharThis, aCharNext); - - ++myRectsNb; + myPen.x() += anAdvanceX; + myMaxSymbolWidth = Max (myMaxSymbolWidth, anAdvanceX); } + myLastSymbolWidth = myPen.x() - myCorners.Last().x(); } // ======================================================================= // function : newLine // purpose : // ======================================================================= -void Font_TextFormatter::newLine (const Standard_Integer theLastRect) +void Font_TextFormatter::newLine (const Standard_Integer theLastRect, + const Standard_ShortReal theMaxLineWidth) { - if (myRectLineStart >= myRectsNb) + Standard_Integer aFirstCornerId = myRectLineStart; + Standard_Integer aLastCornerId = theLastRect; + + if (aFirstCornerId >= getRectsNb()) { ++myLinesNb; myPenCurrLine -= myLineSpacing; return; } + Standard_ShortReal aXMin = BottomLeft (aFirstCornerId).x(); + Font_Rect aBndBox; + BndBox (aLastCornerId, aBndBox); + Standard_ShortReal aXMax = aBndBox.Right; + myMoveVec.y() = myPenCurrLine; switch (myAlignX) { default: - case Graphic3d_HTA_LEFT: - { - myMoveVec.x() = (myNewLineNb > 0) ? -myNewLines.Value (myNewLineNb - 1) : 0.0f; - break; - } - case Graphic3d_HTA_RIGHT: - { - myMoveVec.x() = (myNewLineNb < myNewLines.Length()) - ? -myNewLines.Value (myNewLineNb) - : -myPen.x(); - break; - } - case Graphic3d_HTA_CENTER: - { - const Standard_ShortReal aFrom = (myNewLineNb > 0) - ? myNewLines.Value (myNewLineNb - 1) - : 0.0f; - const Standard_ShortReal aTo = (myNewLineNb < myNewLines.Length()) - ? myNewLines.Value (myNewLineNb) - : myPen.x(); - myMoveVec.x() = -0.5f * (aFrom + aTo); - break; - } + 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 = myRectWordStart = theLastRect + 1; + myRectLineStart = theLastRect + 1; } // ======================================================================= @@ -217,13 +210,13 @@ void Font_TextFormatter::newLine (const Standard_Integer theLastRect) // ======================================================================= void Font_TextFormatter::Format() { - if (myRectsNb == 0 || myIsFormatted) + if (getRectsNb() == 0 || myIsFormatted) { return; } myIsFormatted = true; - myLinesNb = myRectLineStart = myRectWordStart = 0; + myLinesNb = myRectLineStart = 0; myBndTop = 0.0f; myBndWidth = 0.0f; myMoveVec.x() = myMoveVec.y() = 0.0f; @@ -232,59 +225,57 @@ void Font_TextFormatter::Format() myPenCurrLine = -myAscender; Standard_Integer aRectIter = 0; myNewLineNb = 0; - Standard_ShortReal aMaxLineWidth = -1.0f; - for (NCollection_Utf8Iter anIter = myString.Iterator(); *anIter != 0; ++anIter) + + Standard_ShortReal aMaxLineWidth = Wrapping(); + if (HasWrapping()) { - const Standard_Utf32Char aCharThis = *anIter; - if (aCharThis == '\x0D' // CR (carriage return) - || aCharThis == '\a' // BEL (alarm) - || aCharThis == '\f' // FF (form feed) NP (new page) - || aCharThis == '\b' // BS (backspace) - || aCharThis == '\v') // VT (vertical tab) + aMaxLineWidth = Max (aMaxLineWidth, MaximumSymbolWidth()); // it is not possible to wrap less than symbol width + } + else + { + if (myNewLines.IsEmpty()) // If only one line + aMaxLineWidth = myPen.x(); + else { - continue; // skip unsupported carriage control codes + for (int aLineIt = 0; aLineIt < myNewLines.Size(); aLineIt++) + aMaxLineWidth = Max (aMaxLineWidth, LineWidth (aLineIt)); + aMaxLineWidth = Max (aMaxLineWidth, LineWidth (myNewLines.Size())); // processing the last line also } - else if (aCharThis == '\x0A') // LF (line feed, new line) - { - // calculate max line width - if (myNewLineNb == 0) - { - aMaxLineWidth = myNewLines.Value(0); - } - else - { - aMaxLineWidth = Max (aMaxLineWidth, myNewLines.Value (myNewLineNb) - myNewLines.Value (myNewLineNb - 1)); - } + } - const Standard_Integer aLastRect = aRectIter - 1; // last rect on current line - newLine (aLastRect); + Handle(Font_TextFormatter) aFormatter (this); + for (Font_TextFormatter::Iterator aFormatterIt (aFormatter); + 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 (aCharThis == ' ' - || aCharThis == '\t') + else if (HasWrapping()) // wrap lines longer than maximum width { - myRectWordStart = aRectIter; - continue; - } - - ++aRectIter; - } + Standard_Integer aFirstCornerId = myRectLineStart; - // If only one line - if (aMaxLineWidth < 0.0f) - { - aMaxLineWidth = myPen.x(); - } - else // Consider last line - { - aMaxLineWidth = Max (aMaxLineWidth, myPen.x() - myNewLines.Value (myNewLineNb - 1)); + Font_Rect aBndBox; + BndBox (aRectIter, aBndBox); + const Standard_ShortReal aNextXPos = aBndBox.Right - BottomLeft (aFirstCornerId).x(); + if (aNextXPos > aMaxLineWidth) // wrap the line and do processing of the symbol + { + const Standard_Integer aLastRect = aRectIter - 1; // last rect on current line + newLine (aLastRect, aMaxLineWidth); + } + } } myBndWidth = aMaxLineWidth; // move last line - newLine (myRectsNb - 1); + newLine (getRectsNb() - 1, aMaxLineWidth); // apply vertical alignment style if (myAlignY == Graphic3d_VTA_BOTTOM) @@ -302,6 +293,142 @@ void Font_TextFormatter::Format() if (myAlignY != Graphic3d_VTA_TOP) { - moveY (myCorners, myBndTop, 0, myRectsNb - 1); + moveY (myCorners, myBndTop, 0, getRectsNb() - 1); } } + +// ======================================================================= +// function : BndBox +// purpose : +// ======================================================================= +Standard_Boolean Font_TextFormatter::BndBox (const Standard_Integer theIndex, Font_Rect& theBndBox) const +{ + if (theIndex < 0 || theIndex >= GetCorners().Size()) + return Standard_False; + + const NCollection_Vec2& aLeftCorner = BottomLeft (theIndex); + if (theIndex + 1 < myCorners.Length()) // not the last symbol + { + const NCollection_Vec2& aNextLeftCorner = BottomLeft (theIndex + 1); + theBndBox.Left = aLeftCorner.x(); + theBndBox.Bottom = aLeftCorner.y(); + theBndBox.Top = theBndBox.Bottom + myLineSpacing; + 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; + } + } + } + else // the last symbol + { + theBndBox.Left = aLeftCorner.x(); + theBndBox.Right = aLeftCorner.x() + myLastSymbolWidth; + theBndBox.Bottom = aLeftCorner.y(); + theBndBox.Top = theBndBox.Bottom + myLineSpacing; + } + return Standard_True; +} + + +// ======================================================================= +// function : IsLFSymbol +// purpose : +// ======================================================================= +Standard_Boolean Font_TextFormatter::IsLFSymbol (const Standard_Integer theIndex) const +{ + Font_Rect aBndBox; + if (!BndBox (theIndex, aBndBox)) + return Standard_False; + + return Abs (aBndBox.Right - aBndBox.Left) < Precision::Confusion(); +} + +// ======================================================================= +// function : GetFirstPosition +// purpose : +// ======================================================================= +Standard_ShortReal Font_TextFormatter::GetFirstPosition() const +{ + switch (myAlignX) + { + default: + case Graphic3d_HTA_LEFT: return 0; break; + case Graphic3d_HTA_RIGHT: return myBndWidth; break; + case Graphic3d_HTA_CENTER: return 0.5f * myBndWidth; break; + } +} + +// ======================================================================= +// function : LinePositionIndex +// purpose : +// ======================================================================= +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; +} + +// ======================================================================= +// function : LineIndex +// purpose : +// ======================================================================= +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); +} + +// ======================================================================= +// function : IsCommandSymbol +// purpose : +// ======================================================================= +Standard_Boolean Font_TextFormatter::IsCommandSymbol (const Standard_Utf32Char& theSymbol) +{ + if (theSymbol == '\x0D' // CR (carriage return) + || theSymbol == '\a' // BEL (alarm) + || theSymbol == '\f' // FF (form feed) NP (new page) + || theSymbol == '\b' // BS (backspace) + || theSymbol == '\v') // VT (vertical tab) + return Standard_True; + + return Standard_False; +} + +// ======================================================================= +// function : LineWidth +// purpose : +// ======================================================================= +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; +} diff --git a/src/Font/Font_TextFormatter.hxx b/src/Font/Font_TextFormatter.hxx index 19d68e7e39..4044407868 100755 --- a/src/Font/Font_TextFormatter.hxx +++ b/src/Font/Font_TextFormatter.hxx @@ -25,10 +25,112 @@ class Font_FTFont; +DEFINE_STANDARD_HANDLE(Font_TextFormatter, Standard_Transient) + //! This class intended to prepare formatted text. -class Font_TextFormatter +//! Case of the formatter using: +//! Handle(Font_TextFormatter) aFormatter = new Font_TextFormatter(); +//! aFormatter->Append(text_1, aFont1); +//! aFormatter->Append(text_2, aFont2); +//! aFormatter->Format(); +//! +//! Example of corners indices for the text: +//! "row_1\n" - 0-5 +//! "\n" - 6 +//! "\n" - 7 +//! "row_2\n" - 8-13 +//! Processing of \n symbol: +//! - it is placed on the row where it appears +//! - BndBox for \n has zero width +//! - if the last symbol is \n, use LastBndBox() to get position on the next row +//! +//! Pay attention that fonts should have the same LineSpacing value +//! +class Font_TextFormatter : public Standard_Transient { public: + //! Iteration filter flags. Command symbols are skipped with any filter. + enum IterationFilter + { + IterationFilter_None = 0x0000, //!< no filter + IterationFilter_ExcludeInvisible = 0x0002, //!< exclude ' ', '\t', '\n' + }; + + //! Iterator through light sources. + class Iterator + { + public: + //! Constructor with initialization. + Iterator (const Handle(Font_TextFormatter)& theFormatter, + IterationFilter theFilter = IterationFilter_None) + : myFormatter (theFormatter), myFilter (theFilter), myIter (theFormatter->myString.Iterator()) + { + mySymbolPosition = readNextSymbol (-1, mySymbolChar); + mySymbolNext = readNextSymbol (mySymbolPosition, mySymbolCharNext); + } + + //! Returns TRUE if iterator points to a valid item. + Standard_Boolean More() const { return mySymbolPosition >= 0; } + + //! Returns TRUE if next item exists + Standard_Boolean HasNext() const { return mySymbolNext >= 0; } + + //! Returns current symbol. + const Standard_Utf32Char& Symbol() const { return mySymbolChar; } + + //! Returns the next symbol if exists. + const Standard_Utf32Char& SymbolNext() const { return mySymbolCharNext; } + + //! Returns current symbol position. + const Standard_Integer& SymbolPosition() const { return mySymbolPosition; } + + //! Returns the next symbol position. + const Standard_Integer& SymbolPositionNext() const { return mySymbolNext; } + + //! Moves to the next item. + void Next() + { + mySymbolPosition = mySymbolNext; + mySymbolChar = mySymbolCharNext; + mySymbolNext = readNextSymbol (mySymbolPosition, mySymbolCharNext); + } + + protected: + Standard_Integer readNextSymbol (const Standard_Integer aSymbolStartingFrom, Standard_Utf32Char& theSymbolChar) + { + Standard_Integer aNextSymbol = aSymbolStartingFrom; + for (; *myIter != 0; ++myIter) + { + const Standard_Utf32Char aCharCurr = *myIter; + if (Font_TextFormatter::IsCommandSymbol (aCharCurr)) + { + continue; // skip unsupported carriage control codes + } + aNextSymbol++; + if ((myFilter & IterationFilter_ExcludeInvisible) != 0) + { + if (aCharCurr == '\x0A'|| // LF (line feed, new line) + aCharCurr == ' ' || + aCharCurr == '\t') + continue; + } + ++myIter; + theSymbolChar = aCharCurr; + return aNextSymbol; // found the first next, not command and not filtered symbol + } + return -1; // the next symbol is not found + } + + protected: + Handle(Font_TextFormatter) myFormatter; //!< source class for iterating + IterationFilter myFilter; //!< possibility to filter not-necessary symbols + + NCollection_Utf8Iter myIter; //!< the next symbol iterator value over the text formatter string + Standard_Integer mySymbolPosition; //!< the current position + Standard_Utf32Char mySymbolChar; //!< the current symbol + Standard_Integer mySymbolNext; //!< position of the next symbol in iterator, if zero, the iterator is finished + Standard_Utf32Char mySymbolCharNext; //!< the current symbol + }; //! Default constructor. Standard_EXPORT Font_TextFormatter(); @@ -49,16 +151,39 @@ public: Standard_EXPORT void Format(); //! Returns specific glyph rectangle. - inline const NCollection_Vec2& TopLeft (const Standard_Integer theIndex) const - { - return myCorners.Value (theIndex); - } + inline const NCollection_Vec2& BottomLeft (const Standard_Integer theIndex) const + { return myCorners.Value (theIndex); } + + //! Returns symbol bounding box + //! @param bounding box. + Standard_EXPORT Standard_Boolean BndBox (const Standard_Integer theIndex, Font_Rect& theBndBox) const; + + //! Returns the line height + //! \param theIndex a line index, obtained by LineIndex() + Standard_ShortReal LineHeight (const Standard_Integer theIndex) const + { return theIndex == 0 ? myAscender : myLineSpacing; } + + //!< Returns width of a line + Standard_EXPORT Standard_ShortReal LineWidth (const Standard_Integer theIndex) const; + + //! Returns true if the symbol by the index is '\n'. The width of the symbol is zero. + Standard_EXPORT Standard_Boolean IsLFSymbol (const Standard_Integer theIndex) const; + + //! Returns position of the first symbol in a line using alignment + Standard_EXPORT Standard_ShortReal GetFirstPosition() const; + + //! Returns column index of the corner index in the current line + Standard_EXPORT Standard_Integer LinePositionIndex (const Standard_Integer theIndex) const; + + //! Returns row index of the corner index among text lines + Standard_EXPORT Standard_Integer LineIndex (const Standard_Integer theIndex) const; //! Returns current rendering string. - inline const NCollection_String& String() const - { - return myString; - } + //! Obsolete method, please use Iterator to get the string characters + //inline const NCollection_String& String() const + //{ + // return myString; + //} //! Returns tab size. inline Standard_Integer TabSize() const @@ -66,6 +191,21 @@ public: return myTabSize; } + //!< Returns horizontal alignment style + Graphic3d_HorizontalTextAlignment HorizontalTextAlignment() const { return myAlignX; } + + //!< Returns vertical alignment style + Graphic3d_VerticalTextAlignment VerticalTextAlignment() const { return myAlignY; } + + //!< Sets text wrapping width, zero means that the text is not bounded by width + void SetWrapping (const Standard_ShortReal theWidth) { myWrappingWidth = theWidth; } + + //!< Returns text maximum width, zero means that the text is not bounded by width + Standard_Boolean HasWrapping() const { return myWrappingWidth > 0; } + + //!< Returns text maximum width, zero means that the text is not bounded by width + Standard_ShortReal Wrapping() const { return myWrappingWidth; } + //! @return width of formatted text. inline Standard_ShortReal ResultWidth() const { @@ -78,6 +218,9 @@ public: return myLineSpacing * Standard_ShortReal(myLinesNb); } + //!< @return maximum width of the text symbol + inline Standard_ShortReal MaximumSymbolWidth() const { return myMaxSymbolWidth; } + //! @param bounding box. inline void BndBox (Font_Rect& theBndBox) const { @@ -89,7 +232,7 @@ public: case Graphic3d_HTA_RIGHT: theBndBox.Right = -myBndWidth; break; case Graphic3d_HTA_CENTER: { - theBndBox.Left = -0.5f * myBndWidth; + theBndBox.Left = -0.5f * myBndWidth; theBndBox.Right = 0.5f * myBndWidth; break; } @@ -98,16 +241,33 @@ public: theBndBox.Bottom = theBndBox.Top - myLineSpacing * Standard_ShortReal(myLinesNb); } + //!< Returns internal container of the top left corners of a formatted rectangles. + const NCollection_Vector < NCollection_Vec2 >& GetCorners() const { return myCorners; } + + const NCollection_Vector& GetNewLines() const { return myNewLines; } + + //!< Returns true if the symbol is CR, BEL, FF, NP, BS or VT + Standard_EXPORT static Standard_Boolean IsCommandSymbol (const Standard_Utf32Char& theSymbol); + + DEFINE_STANDARD_RTTIEXT (Font_TextFormatter, Standard_Transient) + protected: //! @name class auxiliary methods //! Move glyphs on the current line to correct position. - Standard_EXPORT void newLine (const Standard_Integer theLastRect); + Standard_EXPORT void newLine (const Standard_Integer theLastRect, + const Standard_ShortReal theMaxLineWidth); + + //!< Returns rectangle number + Standard_Integer getRectsNb() { return myCorners.Length(); } protected: //! @name configuration Graphic3d_HorizontalTextAlignment myAlignX; //!< horizontal alignment style Graphic3d_VerticalTextAlignment myAlignY; //!< vertical alignment style Standard_Integer myTabSize; //!< horizontal tabulation width (number of space symbols) + Standard_ShortReal myWrappingWidth; //!< text is wrapped by the width if it is defined (more 0) + Standard_ShortReal myLastSymbolWidth; //!< width of the last symbol + Standard_ShortReal myMaxSymbolWidth; //!< maximum symbol width of the formatter string protected: //! @name input data @@ -116,18 +276,16 @@ protected: //! @name input data myPen; //!< current pen position NCollection_Vector < NCollection_Vec2 > myCorners; //!< The top left corners of a formatted rectangles. - Standard_Integer myRectsNb; //!< rectangles number NCollection_Vector myNewLines; //!< position at LF Standard_ShortReal myLineSpacing; //!< line spacing (computed as maximum of all fonts involved in text formatting) - Standard_ShortReal myAscender; //!< + Standard_ShortReal myAscender; //!< line spacing for the first line bool myIsFormatted; //!< formatting state protected: //! @name temporary variables for formatting routines Standard_Integer myLinesNb; //!< overall (new)lines number (including splitting by width limit) Standard_Integer myRectLineStart; //!< id of first rectangle on the current line - Standard_Integer myRectWordStart; //!< id of first rectangle in the current word Standard_Integer myNewLineNb; Standard_ShortReal myPenCurrLine; //!< current baseline position @@ -135,6 +293,8 @@ protected: //! @name temporary variables for formatting routines Standard_ShortReal myBndWidth; NCollection_Vec2 myMoveVec; //!< local variable + + friend Iterator; }; #endif // Font_TextFormatter_Header diff --git a/src/Graphic3d/Graphic3d_Group.cxx b/src/Graphic3d/Graphic3d_Group.cxx index e85870d10c..50072210ab 100644 --- a/src/Graphic3d/Graphic3d_Group.cxx +++ b/src/Graphic3d/Graphic3d_Group.cxx @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -424,6 +425,30 @@ void Graphic3d_Group::Text (const Standard_CString /*theText*/, Update(); } +// ======================================================================= +// function : Text +// purpose : +// ======================================================================= +void Graphic3d_Group::Text (const Handle(Font_TextFormatter)& theTextFormatter, + const gp_Ax2& theOrientation, + const Standard_Real theHeight, + const Standard_Real theAngle, + const Graphic3d_TextPath theTp, + const Standard_Boolean theToEvalMinMax, + const Standard_Boolean theHasOwnAnchor) +{ + + Text ("", //theTextFormatter->String().ToCString(), + theOrientation, + theHeight, + theAngle, + theTp, + theTextFormatter->HorizontalTextAlignment(), + theTextFormatter->VerticalTextAlignment(), + theToEvalMinMax, + theHasOwnAnchor); +} + // ======================================================================= // function : Text // purpose : diff --git a/src/Graphic3d/Graphic3d_Group.hxx b/src/Graphic3d/Graphic3d_Group.hxx index 4da4fc13ae..ce2ba63461 100644 --- a/src/Graphic3d/Graphic3d_Group.hxx +++ b/src/Graphic3d/Graphic3d_Group.hxx @@ -35,6 +35,7 @@ #include #include +class Font_TextFormatter; class Graphic3d_Structure; class Graphic3d_ArrayOfPrimitives; @@ -188,6 +189,15 @@ public: const Standard_Boolean theToEvalMinMax = Standard_True, const Standard_Boolean theHasOwnAnchor = Standard_True); + //! Add text element in 3D space. + Standard_EXPORT virtual void Text (const Handle(Font_TextFormatter)& theTextFormatter, + const gp_Ax2& theOrientation, + const Standard_Real theHeight, + const Standard_Real theAngle, + const Graphic3d_TextPath theTp, + const Standard_Boolean theToEvalMinMax, + const Standard_Boolean theHasOwnAnchor = Standard_True); + //! Adds an array of primitives for display Standard_EXPORT virtual void AddPrimitiveArray (const Graphic3d_TypeOfPrimitiveArray theType, const Handle(Graphic3d_IndexBuffer)& theIndices, const Handle(Graphic3d_Buffer)& theAttribs, const Handle(Graphic3d_BoundBuffer)& theBounds, const Standard_Boolean theToEvalMinMax = Standard_True); diff --git a/src/OpenGl/OpenGl_Group.cxx b/src/OpenGl/OpenGl_Group.cxx index 61ae607d21..fa22d87ed7 100644 --- a/src/OpenGl/OpenGl_Group.cxx +++ b/src/OpenGl/OpenGl_Group.cxx @@ -24,6 +24,8 @@ #include #include +#include + #include #include @@ -277,6 +279,38 @@ void OpenGl_Group::Text (const Standard_CString theTextUtf, } +// ======================================================================= +// function : SetFlippingOptions +// purpose : +// ======================================================================= +void OpenGl_Group::Text (const Handle(Font_TextFormatter)& theTextFormatter, + const gp_Ax2& theOrientation, + const Standard_Real theHeight, + const Standard_Real theAngle, + const Graphic3d_TextPath theTp, + const Standard_Boolean theToEvalMinMax, + const Standard_Boolean theHasOwnAnchor) +{ + if (IsDeleted()) + { + return; + } + + OpenGl_TextParam aParams; + OpenGl_Structure* aStruct = GlStruct(); + + aParams.Height = int ((theHeight < 2.0) ? aStruct->GlDriver()->DefaultTextHeight() : theHeight); + + OpenGl_Text* aText = new OpenGl_Text ("", theOrientation, aParams, theHasOwnAnchor != Standard_False); + aText->SetTextFormatter (theTextFormatter); + + AddElement (aText); + + Graphic3d_Group::Text ("", theOrientation, theHeight, theAngle, + theTp, theTextFormatter->HorizontalTextAlignment(), theTextFormatter->VerticalTextAlignment(), + theToEvalMinMax, theHasOwnAnchor); +} + // ======================================================================= // function : SetFlippingOptions // purpose : diff --git a/src/OpenGl/OpenGl_Group.hxx b/src/OpenGl/OpenGl_Group.hxx index 33400dcff0..fb39c439b8 100644 --- a/src/OpenGl/OpenGl_Group.hxx +++ b/src/OpenGl/OpenGl_Group.hxx @@ -23,6 +23,8 @@ #include #include +class Font_TextFormatter; + class OpenGl_Group; class OpenGl_Structure; @@ -92,6 +94,15 @@ public: const Standard_Boolean theToEvalMinMax, const Standard_Boolean theHasOwnAnchor = Standard_True) Standard_OVERRIDE; + //! Add text element in 3D space. + Standard_EXPORT virtual void Text (const Handle(Font_TextFormatter)& theTextFormatter, + const gp_Ax2& theOrientation, + const Standard_Real theHeight, + const Standard_Real theAngle, + const Graphic3d_TextPath theTp, + const Standard_Boolean theToEvalMinMax, + const Standard_Boolean theHasOwnAnchor = Standard_True) Standard_OVERRIDE; + //! Add flipping element Standard_EXPORT virtual void SetFlippingOptions (const Standard_Boolean theIsEnabled, const gp_Ax2& theRefPlane) Standard_OVERRIDE; diff --git a/src/OpenGl/OpenGl_Text.cxx b/src/OpenGl/OpenGl_Text.cxx index 1fc4503786..08caff903f 100644 --- a/src/OpenGl/OpenGl_Text.cxx +++ b/src/OpenGl/OpenGl_Text.cxx @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -672,7 +673,7 @@ void OpenGl_Text::render (const Handle(OpenGl_Context)& theCtx, const OpenGl_Vec4& theColorSubs, unsigned int theResolution) const { - if (myString.IsEmpty()) + if (myString.IsEmpty() && myFormatter.IsNull()) { return; } @@ -698,12 +699,16 @@ void OpenGl_Text::render (const Handle(OpenGl_Context)& theCtx, if (myTextures.IsEmpty()) { - Font_TextFormatter aFormatter; - aFormatter.SetupAlignment (myParams.HAlign, myParams.VAlign); - aFormatter.Reset(); + Handle(Font_TextFormatter) aFormatter = myFormatter; + if (myFormatter.IsNull()) + { + aFormatter = new Font_TextFormatter(); + aFormatter->SetupAlignment (myParams.HAlign, myParams.VAlign); + aFormatter->Reset(); - aFormatter.Append (myString, *myFont->FTFont().operator->()); - aFormatter.Format(); + aFormatter->Append (myString, *myFont->FTFont().operator->()); + aFormatter->Format(); + } OpenGl_TextBuilder aBuilder; aBuilder.Perform (aFormatter, @@ -713,7 +718,7 @@ void OpenGl_Text::render (const Handle(OpenGl_Context)& theCtx, myVertsVbo, myTCrdsVbo); - aFormatter.BndBox (myBndBox); + aFormatter->BndBox (myBndBox); if (!myBndVertsVbo.IsNull()) { myBndVertsVbo->Release (theCtx.get()); diff --git a/src/OpenGl/OpenGl_Text.hxx b/src/OpenGl/OpenGl_Text.hxx index 2de368c7b5..38d0d3de3b 100755 --- a/src/OpenGl/OpenGl_Text.hxx +++ b/src/OpenGl/OpenGl_Text.hxx @@ -19,6 +19,8 @@ #include #include +#include + #include #include @@ -30,6 +32,8 @@ #include +class Font_TextFormatter; + //! Text rendering class OpenGl_Text : public OpenGl_Element { @@ -61,6 +65,9 @@ public: const OpenGl_Vec3& thePoint, const OpenGl_TextParam& theParams); + //! Setup text formatter + Standard_EXPORT void SetTextFormatter (const Handle(Font_TextFormatter)& theFormatter) { myFormatter = theFormatter; } + //! Setup new position Standard_EXPORT void SetPosition (const OpenGl_Vec3& thePoint); @@ -176,6 +183,8 @@ protected: bool myHasPlane; //!< Check if text have orientation in 3D space. bool myHasAnchorPoint; //!< Shows if it has own attach point + Handle(Font_TextFormatter) myFormatter; //!< Text formatter, an alternative to text params + public: DEFINE_STANDARD_ALLOC diff --git a/src/OpenGl/OpenGl_TextBuilder.cxx b/src/OpenGl/OpenGl_TextBuilder.cxx index 9505f7443d..338cec15fe 100644 --- a/src/OpenGl/OpenGl_TextBuilder.cxx +++ b/src/OpenGl/OpenGl_TextBuilder.cxx @@ -17,6 +17,7 @@ #include #include +#include namespace { @@ -44,7 +45,7 @@ OpenGl_TextBuilder::OpenGl_TextBuilder() // function : createGlyphs // purpose : // ======================================================================= -void OpenGl_TextBuilder::createGlyphs (const Font_TextFormatter& theFormatter, +void OpenGl_TextBuilder::createGlyphs (const Handle(Font_TextFormatter)& theFormatter, const Handle(OpenGl_Context)& theCtx, OpenGl_Font& theFont, NCollection_Vector& theTextures, @@ -58,51 +59,16 @@ void OpenGl_TextBuilder::createGlyphs (const Font_TextFormatter& theTCrdsPerTexture.Clear(); OpenGl_Font::Tile aTile = {Font_Rect(), Font_Rect(), 0u}; - OpenGl_Vec2 aPen (0.0f, 0.0f); - Standard_Integer aRectsNb = 0; - Standard_Integer aSymbolsCounter = 0; - - for (NCollection_Utf8Iter anIter = theFormatter.String().Iterator(); *anIter != 0;) + for (Font_TextFormatter::Iterator aFormatterIt (theFormatter, Font_TextFormatter::IterationFilter_ExcludeInvisible); + aFormatterIt .More(); aFormatterIt .Next()) { - const Standard_Utf32Char aCharThis = *anIter; - const Standard_Utf32Char aCharNext = *++anIter; - - if (aCharThis == '\x0D' // CR (carriage return) - || aCharThis == '\a' // BEL (alarm) - || aCharThis == '\f' // FF (form feed) NP (new page) - || aCharThis == '\b' // BS (backspace) - || aCharThis == '\v') // VT (vertical tab) - { - continue; // skip unsupported carriage control codes - } - else if (aCharThis == '\x0A') // LF (line feed, new line) - { - aSymbolsCounter = 0; - continue; // will be processed on second pass - } - else if (aCharThis == ' ') - { - ++aSymbolsCounter; - aPen.x() += theFont.FTFont()->AdvanceX (' ', aCharNext); - continue; - } - else if (aCharThis == '\t') - { - const Standard_Integer aSpacesNum = (theFormatter.TabSize() - (aSymbolsCounter - 1) % theFormatter.TabSize()); - aPen.x() += theFont.FTFont()->AdvanceX (' ', aCharNext) * Standard_ShortReal(aSpacesNum); - aSymbolsCounter += aSpacesNum; - continue; - } + theFont.RenderGlyph (theCtx, aFormatterIt.Symbol(), aTile); - ++aSymbolsCounter; - - theFont.RenderGlyph (theCtx, aCharThis, aTile); - - const OpenGl_Vec2& aTopLeft = theFormatter.TopLeft (aRectsNb); - aTile.px.Right += aTopLeft.x(); - aTile.px.Left += aTopLeft.x(); - aTile.px.Bottom += aTopLeft.y(); - aTile.px.Top += aTopLeft.y(); + const OpenGl_Vec2& aBottomLeft = theFormatter->BottomLeft (aFormatterIt.SymbolPosition()); + aTile.px.Right += aBottomLeft.x(); + aTile.px.Left += aBottomLeft.x(); + aTile.px.Bottom += aBottomLeft.y(); + aTile.px.Top += aBottomLeft.y(); const Font_Rect& aRectUV = aTile.uv; const GLuint aTexture = aTile.texture; @@ -139,8 +105,6 @@ void OpenGl_TextBuilder::createGlyphs (const Font_TextFormatter& aTCrds.Append (aRectUV.BottomRight (aVec)); aTCrds.Append (aRectUV.TopRight (aVec)); aTCrds.Append (aRectUV.BottomLeft (aVec)); - - ++aRectsNb; } } @@ -148,7 +112,7 @@ void OpenGl_TextBuilder::createGlyphs (const Font_TextFormatter& // function : CreateTextures // purpose : // ======================================================================= -void OpenGl_TextBuilder::Perform (const Font_TextFormatter& theFormatter, +void OpenGl_TextBuilder::Perform (const Handle(Font_TextFormatter)& theFormatter, const Handle(OpenGl_Context)& theCtx, OpenGl_Font& theFont, NCollection_Vector& theTextures, diff --git a/src/OpenGl/OpenGl_TextBuilder.hxx b/src/OpenGl/OpenGl_TextBuilder.hxx index 6ed37b819e..2fcd775e33 100644 --- a/src/OpenGl/OpenGl_TextBuilder.hxx +++ b/src/OpenGl/OpenGl_TextBuilder.hxx @@ -16,8 +16,6 @@ #ifndef OpenGl_TextBuilder_Header #define OpenGl_TextBuilder_Header -#include - #include #include #include @@ -27,6 +25,7 @@ #include #include +class Font_TextFormatter; //! This class generates primitive array required for rendering textured text using OpenGl_Font instance. class OpenGl_TextBuilder @@ -37,7 +36,7 @@ public: Standard_EXPORT OpenGl_TextBuilder(); //! Creates texture quads for the given text. - Standard_EXPORT void Perform (const Font_TextFormatter& theFormatter, + Standard_EXPORT void Perform (const Handle(Font_TextFormatter)& theFormatter, const Handle(OpenGl_Context)& theContext, OpenGl_Font& theFont, NCollection_Vector& theTextures, @@ -46,7 +45,7 @@ public: protected: //! @name class auxillary methods - Standard_EXPORT void createGlyphs (const Font_TextFormatter& theFormatter, + Standard_EXPORT void createGlyphs (const Handle(Font_TextFormatter)& theFormatter, const Handle(OpenGl_Context)& theCtx, OpenGl_Font& theFont, NCollection_Vector& theTextures,