// 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. #ifndef Font_TextFormatter_Header #define Font_TextFormatter_Header #include #include #include #include #include #include class Font_FTFont; DEFINE_STANDARD_HANDLE(Font_TextFormatter, Standard_Transient) //! This class is intended to prepare formatted text by using:
//! - font to string combination,
//! - alignment,
//! - wrapping.
//! //! After text formatting, each symbol of formatted text is placed in some position. //! Further work with the formatter is using an iterator. //! The iterator gives an access to each symbol inside the initial row. //! Also it's possible to get only significant/writable symbols of the text.
//! Formatter gives an access to geometrical position of a symbol by the symbol index in the text.
//! Example of correspondence of some text symbol to an index in "row_1\n\nrow_2\n":
//! "row_1\n" - 0-5 indices;
//! "\n" - 6 index;
//! "\n" - 7 index;
//! "row_2\n" - 8-13 indices.
//! Pay attention that fonts should have the same LineSpacing value for correct formatting.
//! Example of the formatter using: //! @code //! Handle(Font_TextFormatter) aFormatter = new Font_TextFormatter(); //! aFormatter->Append(text_1, aFont1); //! aFormatter->Append(text_2, aFont2); //! // setting of additional properties such as wrapping or alignment //! aFormatter->Format(); //! @endcode 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 formatted symbols. //! It's possible to filter returned symbols to have only significant ones. class Iterator { public: //! Constructor with initialization. Iterator (const Font_TextFormatter& theFormatter, IterationFilter theFilter = IterationFilter_None) : myFilter (theFilter), myIter (theFormatter.myString.Iterator()), mySymbolChar (0), mySymbolCharNext (0) { 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. Standard_Utf32Char Symbol() const { return mySymbolChar; } //! Returns the next symbol if exists. Standard_Utf32Char SymbolNext() const { return mySymbolCharNext; } //! Returns current symbol position. Standard_Integer SymbolPosition() const { return mySymbolPosition; } //! Returns the next symbol position. Standard_Integer SymbolPositionNext() const { return mySymbolNext; } //! Moves to the next item. void Next() { mySymbolPosition = mySymbolNext; mySymbolChar = mySymbolCharNext; mySymbolNext = readNextSymbol (mySymbolPosition, mySymbolCharNext); } protected: //! Finds index of the next symbol Standard_Integer readNextSymbol (const Standard_Integer theSymbolStartingFrom, Standard_Utf32Char& theSymbolChar) { Standard_Integer aNextSymbol = theSymbolStartingFrom; 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: 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(); //! Setup alignment style. Standard_EXPORT void SetupAlignment (const Graphic3d_HorizontalTextAlignment theAlignX, const Graphic3d_VerticalTextAlignment theAlignY); //! Reset current progress. Standard_EXPORT void Reset(); //! Render specified text to inner buffer. Standard_EXPORT void Append (const NCollection_String& theString, Font_FTFont& theFont); //! Perform formatting on the buffered text. //! Should not be called more than once after initialization! Standard_EXPORT void Format(); Standard_DEPRECATED("BottomLeft should be used instead") const NCollection_Vec2& TopLeft (const Standard_Integer theIndex) const { return BottomLeft (theIndex); } //! Returns specific glyph rectangle. const NCollection_Vec2& BottomLeft (const Standard_Integer theIndex) const { return myCorners.Value (theIndex); } //! Returns current rendering string. inline const NCollection_String& String() const { return myString; } //! Returns symbol bounding box //! @param bounding box. Standard_EXPORT Standard_Boolean GlyphBoundingBox (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 FirstPosition() 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 tab size. inline Standard_Integer TabSize() const { 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; } //! returns TRUE when trying not to break words when wrapping text Standard_Boolean WordWrapping () const { return myIsWordWrapping; } //! returns TRUE when trying not to break words when wrapping text void SetWordWrapping (const Standard_Boolean theIsWordWrapping) { myIsWordWrapping = theIsWordWrapping; } //! @return width of formatted text. inline Standard_ShortReal ResultWidth() const { return myBndWidth; } //! @return height of formatted text. inline Standard_ShortReal ResultHeight() const { return myLineSpacing * Standard_ShortReal(myLinesNb); } //! @return maximum width of the text symbol Standard_ShortReal MaximumSymbolWidth() const { return myMaxSymbolWidth; } //! @param bounding box. inline void BndBox (Font_Rect& theBndBox) const { theBndBox.Left = 0.0f; switch (myAlignX) { default: case Graphic3d_HTA_LEFT: theBndBox.Right = myBndWidth; break; case Graphic3d_HTA_RIGHT: theBndBox.Right = -myBndWidth; break; case Graphic3d_HTA_CENTER: { theBndBox.Left = -0.5f * myBndWidth; theBndBox.Right = 0.5f * myBndWidth; break; } } theBndBox.Top = myBndTop; 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 >& Corners() const { return myCorners; } //! Returns container of each line position at LF in formatted text const NCollection_Vector& NewLines() const { return myNewLines; } //! Returns true if the symbol is CR, BEL, FF, NP, BS or VT static inline Standard_Boolean 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; } //! Returns true if the symbol separates words when wrapping is enabled static Standard_Boolean IsSeparatorSymbol (const Standard_Utf32Char& theSymbol) { return theSymbol == '\x0A' // new line || theSymbol == ' ' // space || theSymbol == '\x09'; // tab } 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, const Standard_ShortReal theMaxLineWidth); 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 defined (more 0) Standard_Boolean myIsWordWrapping; //!< if TRUE try not to break words when wrapping text (true by default) Standard_ShortReal myLastSymbolWidth; //!< width of the last symbol Standard_ShortReal myMaxSymbolWidth; //!< maximum symbol width of the formatter string protected: //! @name input data NCollection_String myString; //!< currently rendered text NCollection_Vec2 myPen; //!< current pen position NCollection_Vector < NCollection_Vec2 > myCorners; //!< The bottom left corners of a formatted rectangles. NCollection_Vector myNewLines; //!< position at LF Standard_ShortReal myLineSpacing; //!< line spacing (computed as maximum of all fonts involved in text formatting) 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 myNewLineNb; Standard_ShortReal myPenCurrLine; //!< current baseline position Standard_ShortReal myBndTop; Standard_ShortReal myBndWidth; NCollection_Vec2 myMoveVec; //!< local variable }; #endif // Font_TextFormatter_Header