Text vertical alignment added.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / layouts / layout-engine.cpp
index 86ddbb4..fa1e919 100644 (file)
@@ -45,15 +45,18 @@ struct LineLayout
   Length         numberOfCharacters; ///< The number of characters which fit in one line.
   Length         numberOfGlyphs;     ///< The number of glyph which fit in one line.
   float          length;             ///< The length of the glyphs which fit in one line.
+  float          widthAdvanceDiff;   ///< The difference between the width and the advance of the last glyph.
   float          wsLengthEndOfLine;  ///< The length of the white spaces at the end of the line.
-  float          height;             ///< The maximum height of all fonts in the line.
   float          ascender;           ///< The maximum ascender of all fonts in the line.
+  float          descender;          ///< The maximum descender of all fonts in the line.
 };
 
 struct LayoutEngine::Impl
 {
   Impl()
-  : mLayout( LayoutEngine::SINGLE_LINE_BOX )
+  : mLayout( LayoutEngine::SINGLE_LINE_BOX ),
+    mHorizontalAlignment( LayoutEngine::HORIZONTAL_ALIGN_BEGIN ),
+    mVerticalAlignment( LayoutEngine::VERTICAL_ALIGN_TOP )
   {
     mFontClient = TextAbstraction::FontClient::Get();
   }
@@ -69,8 +72,8 @@ struct LayoutEngine::Impl
     lineLayout.numberOfGlyphs = 0u;
     lineLayout.length = 0.f;
     lineLayout.wsLengthEndOfLine = 0.f;
-    lineLayout.height = 0.f;
     lineLayout.ascender = 0.f;
+    lineLayout.descender = 0.f;
 
     // Get the last glyph index.
     const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
@@ -83,6 +86,10 @@ struct LayoutEngine::Impl
       // Get the glyph info.
       const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
 
+      // Check whether is a white space.
+      const Character character = *( parameters.textBuffer + lineLayout.numberOfCharacters );
+      const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
+
       // Get the character indices for the current glyph. The last character index is needed
       // because there are glyphs formed by more than one character but their break info is
       // given only for the last character.
@@ -95,25 +102,39 @@ struct LayoutEngine::Impl
       lineLayout.numberOfGlyphs++;
 
       // Increase the accumulated length.
-      lineLayout.length += ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
+      const float glyphLength = ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
+
+      if( isWhiteSpace )
+      {
+        // Add the length to the length of white spaces at the end of the line.
+        lineLayout.wsLengthEndOfLine += glyphLength;
+      }
+      else
+      {
+        // Add as well any previous white space length.
+        lineLayout.length += lineLayout.wsLengthEndOfLine + glyphLength;
+
+        // Clear the white space length at the end of the line.
+        lineLayout.wsLengthEndOfLine = 0.f;
+      }
 
       if( lastFontId != glyphInfo.fontId )
       {
         Text::FontMetrics fontMetrics;
         mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
 
-        // Sets the maximum height.
-        if( fontMetrics.height > lineLayout.height )
-        {
-          lineLayout.height = fontMetrics.height;
-        }
-
         // Sets the maximum ascender.
         if( fontMetrics.ascender > lineLayout.ascender )
         {
           lineLayout.ascender = fontMetrics.ascender;
         }
 
+        // Sets the maximum descender.
+        if( fontMetrics.descender > lineLayout.descender )
+        {
+          lineLayout.descender = fontMetrics.descender;
+        }
+
         lastFontId = glyphInfo.fontId;
       }
     }
@@ -129,21 +150,20 @@ struct LayoutEngine::Impl
     lineLayout.numberOfCharacters = 0u;
     lineLayout.numberOfGlyphs = 0u;
     lineLayout.length = 0.f;
+    lineLayout.widthAdvanceDiff = 0.f;
     lineLayout.wsLengthEndOfLine = 0.f;
-    lineLayout.height = 0.f;
     lineLayout.ascender = 0.f;
+    lineLayout.descender = 0.f;
 
     // Stores temporary line layout which has not been added to the final line layout.
     LineLayout tmpLineLayout;
     tmpLineLayout.numberOfCharacters = 0u;
     tmpLineLayout.numberOfGlyphs = 0u;
     tmpLineLayout.length = 0.f;
+    tmpLineLayout.widthAdvanceDiff = 0.f;
     tmpLineLayout.wsLengthEndOfLine = 0.f;
-    tmpLineLayout.height = 0.f;
     tmpLineLayout.ascender = 0.f;
-
-    // Get the last glyph index.
-    const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
+    tmpLineLayout.descender = 0.f;
 
     FontId lastFontId = 0u;
     for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
@@ -172,11 +192,29 @@ struct LayoutEngine::Impl
       // Increase the number of glyphs.
       tmpLineLayout.numberOfGlyphs++;
 
+      // Check whether is a white space.
+      const Character character = *( parameters.textBuffer + characterFirstIndex );
+      const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
+
       // Increase the accumulated length.
-      tmpLineLayout.length += ( glyphIndex == lastGlyphIndex ) ? glyphInfo.width : glyphInfo.advance;
+      if( isWhiteSpace )
+      {
+        // Add the length to the length of white spaces at the end of the line.
+        tmpLineLayout.wsLengthEndOfLine += glyphInfo.advance; // I use the advance as the width is always zero for the white spaces.
+        tmpLineLayout.widthAdvanceDiff = 0.f;
+      }
+      else
+      {
+        // Add as well any previous white space length.
+        tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphInfo.advance;
+        tmpLineLayout.widthAdvanceDiff = glyphInfo.width - glyphInfo.advance;
+
+        // Clear the white space length at the end of the line.
+        tmpLineLayout.wsLengthEndOfLine = 0.f;
+      }
 
       // Check if the accumulated length fits in the width of the box.
-      if( lineLayout.length + tmpLineLayout.length > parameters.boundingBox.width )
+      if( lineLayout.length + tmpLineLayout.length + tmpLineLayout.widthAdvanceDiff + ( ( 0.f < tmpLineLayout.length ) ? lineLayout.wsLengthEndOfLine : 0.f ) > parameters.boundingBox.width )
       {
         // Current word does not fit in the box's width.
         return;
@@ -184,31 +222,40 @@ struct LayoutEngine::Impl
 
       if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
       {
-        if( glyphIndex == lastGlyphIndex )
+        // Must break the line. Update the line layout and return.
+        lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
+        lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
+        lineLayout.length += tmpLineLayout.length;
+        lineLayout.widthAdvanceDiff = tmpLineLayout.widthAdvanceDiff;
+
+        if( 0.f < tmpLineLayout.length )
         {
-          // Must break the line. Update the line layout and return.
-          lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
-          lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
-          lineLayout.length += tmpLineLayout.length;
+          lineLayout.length += lineLayout.wsLengthEndOfLine;
+
           lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
+        }
+        else
+        {
+          lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
+        }
 
-          if( tmpLineLayout.height > lineLayout.height )
-          {
-            lineLayout.height = tmpLineLayout.height;
-          }
+        if( tmpLineLayout.ascender > lineLayout.ascender )
+        {
+          lineLayout.ascender = tmpLineLayout.ascender;
+        }
 
-          if( tmpLineLayout.ascender > lineLayout.ascender )
-          {
-            lineLayout.ascender = tmpLineLayout.ascender;
-          }
+        if( tmpLineLayout.descender > lineLayout.descender )
+        {
+          lineLayout.descender = tmpLineLayout.descender;
         }
 
         tmpLineLayout.numberOfCharacters = 0u;
         tmpLineLayout.numberOfGlyphs = 0u;
         tmpLineLayout.length = 0u;
+        tmpLineLayout.widthAdvanceDiff = 0u;
         tmpLineLayout.wsLengthEndOfLine = 0u;
-        tmpLineLayout.height = 0.f;
         tmpLineLayout.ascender = 0.f;
+        tmpLineLayout.descender = 0.f;
         return;
       }
 
@@ -219,11 +266,17 @@ struct LayoutEngine::Impl
         lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
         lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
         lineLayout.length += tmpLineLayout.length;
-        lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
+        lineLayout.widthAdvanceDiff = tmpLineLayout.widthAdvanceDiff;
+
+        if( 0.f < tmpLineLayout.length )
+        {
+          lineLayout.length += lineLayout.wsLengthEndOfLine;
 
-        if( tmpLineLayout.height > lineLayout.height )
+          lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
+        }
+        else
         {
-          lineLayout.height = tmpLineLayout.height;
+          lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
         }
 
         if( tmpLineLayout.ascender > lineLayout.ascender )
@@ -231,12 +284,18 @@ struct LayoutEngine::Impl
           lineLayout.ascender = tmpLineLayout.ascender;
         }
 
+        if( tmpLineLayout.descender > lineLayout.descender )
+        {
+          lineLayout.descender = tmpLineLayout.descender;
+        }
+
         tmpLineLayout.numberOfCharacters = 0u;
         tmpLineLayout.numberOfGlyphs = 0u;
         tmpLineLayout.length = 0u;
+        tmpLineLayout.widthAdvanceDiff = 0u;
         tmpLineLayout.wsLengthEndOfLine = 0u;
-        tmpLineLayout.height = 0.f;
         tmpLineLayout.ascender = 0.f;
+        tmpLineLayout.descender = 0.f;
       }
 
       if( lastFontId != glyphInfo.fontId )
@@ -244,18 +303,18 @@ struct LayoutEngine::Impl
         Text::FontMetrics fontMetrics;
         mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
 
-        // Sets the maximum height.
-        if( fontMetrics.height > tmpLineLayout.height )
-        {
-          tmpLineLayout.height = fontMetrics.height;
-        }
-
         // Sets the maximum ascender.
         if( fontMetrics.ascender > tmpLineLayout.ascender )
         {
           tmpLineLayout.ascender = fontMetrics.ascender;
         }
 
+        // Sets the maximum descender.
+        if( -fontMetrics.descender > tmpLineLayout.descender )
+        {
+          tmpLineLayout.descender = -fontMetrics.descender;
+        }
+
         lastFontId = glyphInfo.fontId;
       }
     }
@@ -297,14 +356,16 @@ struct LayoutEngine::Impl
   void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
                                  Vector<Vector2>& glyphPositions )
   {
-    for( Length lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
+    // Traverses the paragraphs with right to left characters.
+    for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
     {
-      const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer +lineIndex  );
+      const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
 
       float penX = 0.f;
 
       Vector2* glyphPositionsBuffer = glyphPositions.Begin();
 
+      // Traverses the characters of the right to left paragraph.
       for( CharacterIndex characterLogicalIndex = 0u;
            characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
            ++characterLogicalIndex )
@@ -318,19 +379,81 @@ struct LayoutEngine::Impl
         for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
         {
           // Convert the character in the visual order into the glyph in the visual order.
-          GlyphIndex glyphIndex = 1u + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex + index ) - numberOfGlyphs;
+          const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
+
+          DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
 
           const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
           Vector2& position = *( glyphPositionsBuffer + glyphIndex );
 
           position.x = penX + glyph.xBearing;
-
           penX += glyph.advance;
         }
       }
     }
   }
 
+  void Align( const LayoutParameters& layoutParameters,
+              const Size& layoutSize,
+              const Vector<LineRun>& lines,
+              Vector<Vector2>& glyphPositions )
+  {
+    Vector2* glyphPositionsBuffer = glyphPositions.Begin();
+
+    // Traverse all lines and align the glyphs.
+    // LayoutParameters contains bidirectional info for those lines with
+    // right to left text, this info includes the paragraph's direction.
+
+    LineIndex bidiLineIndex = 0u;
+    for( Vector<LineRun>::ConstIterator it = lines.Begin(), endIt = lines.End();
+         it != endIt;
+         ++it )
+    {
+      const LineRun& line = *it;
+
+      // 1) Get the paragrap's direction.
+      bool paragraphDirection = false;
+
+      // Check if there is any right to left line.
+      if( ( NULL != layoutParameters.lineBidirectionalInfoRunsBuffer ) &&
+          ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
+      {
+        const BidirectionalLineInfoRun* bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
+
+        // Get the right to left line that match with current line.
+        while( ( line.characterRun.characterIndex > bidiLine->characterRun.characterIndex ) &&
+               ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
+        {
+          ++bidiLineIndex;
+          bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
+        }
+
+        if( line.characterRun.characterIndex == bidiLine->characterRun.characterIndex )
+        {
+          paragraphDirection = bidiLine->direction;
+        }
+      }
+
+      // 2) Calculate the alignment offset accordingly with the align option,
+      //    the box width, line length, and the paragraphs direction.
+      float alignOffset = CalculateHorizontalAlignment( layoutSize.width,
+                                                        line.lineSize.width,
+                                                        line.extraLength,
+                                                        paragraphDirection );
+
+      // 3) Traverse all glyphs and update the 'x' position.
+      for( GlyphIndex index = line.glyphIndex,
+             endIndex = line.glyphIndex + line.numberOfGlyphs;
+           index < endIndex;
+           ++index )
+      {
+        Vector2& position = *( glyphPositionsBuffer + index );
+
+        position.x += alignOffset;
+      }
+    }
+  }
+
   bool SingleLineLayout( const LayoutParameters& layoutParameters,
                          Vector<Vector2>& glyphPositions,
                          Vector<LineRun>& lines,
@@ -350,16 +473,17 @@ struct LayoutEngine::Impl
     lineRun.characterRun.characterIndex = 0u;
     lineRun.characterRun.numberOfCharacters = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
     lineRun.lineSize.width = layout.length;
-    lineRun.lineSize.height = layout.height;
+    lineRun.lineSize.height = layout.ascender + layout.descender;
+    lineRun.extraLength = layout.wsLengthEndOfLine;
 
     lines.PushBack( lineRun );
 
     // Update the actual size.
     actualSize.width = layout.length;
-    actualSize.height = layout.height;
+    actualSize.height = lineRun.lineSize.height;
 
     float penX = 0.f;
-    float penY = layout.height;
+    float penY = layout.ascender;
 
     Vector2* glyphPositionsBuffer = glyphPositions.Begin();
     for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
@@ -406,22 +530,23 @@ struct LayoutEngine::Impl
       lineRun.numberOfGlyphs = layout.numberOfGlyphs;
       lineRun.characterRun.characterIndex = *( layoutParameters.glyphsToCharactersBuffer + index );
       lineRun.characterRun.numberOfCharacters = ( *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex ) ) - lineRun.characterRun.characterIndex;
-      lineRun.lineSize.width = layout.length;
-      lineRun.lineSize.height = layout.height;
+      lineRun.lineSize.width = layout.length + ( ( layout.widthAdvanceDiff > 0.f ) ? layout.widthAdvanceDiff : 0.f );
+      lineRun.lineSize.height = layout.ascender + layout.descender;
+      lineRun.extraLength = layout.wsLengthEndOfLine;
 
       lines.PushBack( lineRun );
 
       // Update the actual size.
-      if( layout.length > actualSize.width )
+      if( layout.length + layout.widthAdvanceDiff > actualSize.width )
       {
         actualSize.width = layout.length;
       }
 
-      actualSize.height += layout.height;
+      actualSize.height += lineRun.lineSize.height;
 
       // Traverse the glyphs and set the positions.
 
-      penY += layout.height;
+      penY += layout.ascender;
 
       Vector2* glyphPositionsBuffer = glyphPositions.Begin();
       for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
@@ -435,6 +560,8 @@ struct LayoutEngine::Impl
         penX += glyph.advance;
       }
 
+      penY += layout.descender;
+
       // Increase the glyph index.
       index += layout.numberOfGlyphs;
     }
@@ -442,7 +569,59 @@ struct LayoutEngine::Impl
     return true;
   }
 
+  float CalculateHorizontalAlignment( float boxWidth,
+                                      float lineLength,
+                                      float extraLength,
+                                      bool paragraphDirection )
+  {
+    float offset = 0.f;
+
+    HorizontalAlignment alignment = mHorizontalAlignment;
+    if( paragraphDirection &&
+        ( HORIZONTAL_ALIGN_CENTER != alignment ) )
+    {
+      if( HORIZONTAL_ALIGN_BEGIN == alignment )
+      {
+        alignment = HORIZONTAL_ALIGN_END;
+      }
+      else
+      {
+        alignment = HORIZONTAL_ALIGN_BEGIN;
+      }
+    }
+
+    switch( alignment )
+    {
+      case HORIZONTAL_ALIGN_BEGIN:
+      {
+        offset = 0.f;
+        break;
+      }
+      case HORIZONTAL_ALIGN_CENTER:
+      {
+        offset = 0.5f * ( boxWidth - lineLength );
+        const int intOffset = static_cast<int>( offset ); // try to avoid pixel alignment.
+        offset = static_cast<float>( intOffset );
+        break;
+      }
+      case HORIZONTAL_ALIGN_END:
+      {
+        offset = boxWidth - lineLength;
+        break;
+      }
+    }
+
+    if( paragraphDirection )
+    {
+      offset -= extraLength;
+    }
+
+    return offset;
+  }
+
   LayoutEngine::Layout mLayout;
+  LayoutEngine::HorizontalAlignment mHorizontalAlignment;
+  LayoutEngine::VerticalAlignment mVerticalAlignment;
 
   TextAbstraction::FontClient mFontClient;
 };
@@ -468,6 +647,26 @@ unsigned int LayoutEngine::GetLayout() const
   return mImpl->mLayout;
 }
 
+void LayoutEngine::SetHorizontalAlignment( HorizontalAlignment alignment )
+{
+  mImpl->mHorizontalAlignment = alignment;
+}
+
+LayoutEngine::HorizontalAlignment LayoutEngine::GetHorizontalAlignment() const
+{
+  return mImpl->mHorizontalAlignment;
+}
+
+void LayoutEngine::SetVerticalAlignment( VerticalAlignment alignment )
+{
+  mImpl->mVerticalAlignment = alignment;
+}
+
+LayoutEngine::VerticalAlignment LayoutEngine::GetVerticalAlignment() const
+{
+  return mImpl->mVerticalAlignment;
+}
+
 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
                                Vector<Vector2>& glyphPositions,
                                Vector<LineRun>& lines,
@@ -486,6 +685,17 @@ void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParam
                                    glyphPositions );
 }
 
+void LayoutEngine::Align( const LayoutParameters& layoutParameters,
+                          const Size& layoutSize,
+                          const Vector<LineRun>& lines,
+                          Vector<Vector2>& glyphPositions )
+{
+  mImpl->Align( layoutParameters,
+                layoutSize,
+                lines,
+                glyphPositions );
+}
+
 } // namespace Text
 
 } // namespace Toolkit