0030537: Visualization - wrapping text in font text formatter
[occt.git] / src / Font / Font_TextFormatter.hxx
index 19d68e7..1d917f2 100755 (executable)
 
 class Font_FTFont;
 
-//! This class intended to prepare formatted text.
-class Font_TextFormatter
+DEFINE_STANDARD_HANDLE(Font_TextFormatter, Standard_Transient)
+
+//! This class is intended to prepare formatted text by using:<br>
+//! - font to string combination,<br>
+//! - alignment,<br>
+//! - wrapping.<br>
+//!
+//! 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.<br>
+//! Formatter gives an access to geometrical position of a symbol by the symbol index in the text.<br>
+//! Example of correspondence of some text symbol to an index in "row_1\n\nrow_2\n":<br>
+//! "row_1\n"  - 0-5 indices;<br>
+//! "\n"       - 6 index;<br>
+//! "\n"       - 7 index;<br>
+//! "row_2\n"  - 8-13 indices.<br>
+//! Pay attention that fonts should have the same LineSpacing value for correct formatting.<br>
+//! 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();
@@ -48,24 +158,68 @@ public:
   //! Should not be called more than once after initialization!
   Standard_EXPORT void Format();
 
-  //! Returns specific glyph rectangle.
-  inline const NCollection_Vec2<Standard_ShortReal>& TopLeft (const Standard_Integer theIndex) const
+  Standard_DEPRECATED("BottomLeft should be used instead")
+  const NCollection_Vec2<Standard_ShortReal>& TopLeft (const Standard_Integer theIndex) const
   {
-    return myCorners.Value (theIndex);
+    return BottomLeft (theIndex);
   }
 
+  //! Returns specific glyph rectangle.
+  const NCollection_Vec2<Standard_ShortReal>& 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; }
+
   //! @return width of formatted text.
   inline Standard_ShortReal ResultWidth() const
   {
@@ -78,6 +232,9 @@ public:
     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
   {
@@ -98,16 +255,41 @@ 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<Standard_ShortReal> >& Corners() const { return myCorners; }
+
+  //! Returns container of each line position at LF in formatted text
+  const NCollection_Vector<Standard_ShortReal>& 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;
+  }
+
+  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);
 
 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_ShortReal                myLastSymbolWidth; //!< width of the last symbol
+  Standard_ShortReal                myMaxSymbolWidth; //!< maximum symbol width of the formatter string
 
 protected: //! @name input data
 
@@ -115,19 +297,17 @@ protected: //! @name input data
   NCollection_Vec2<Standard_ShortReal>
                      myPen;           //!< current pen position
   NCollection_Vector < NCollection_Vec2<Standard_ShortReal> >
-                     myCorners;       //!< The top left corners of a formatted rectangles.
-  Standard_Integer   myRectsNb;       //!< rectangles number
+                     myCorners;       //!< The bottom left corners of a formatted rectangles.
   NCollection_Vector<Standard_ShortReal>
                      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