Text vertical alignment added.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / layouts / layout-engine.cpp
index ac2c53f..fa1e919 100644 (file)
@@ -23,8 +23,8 @@
 #include <dali/public-api/text-abstraction/font-client.h>
 
 // INTERNAL INCLUDES
-#include <dali-toolkit/internal/text/logical-model.h>
-#include <dali-toolkit/internal/text/visual-model.h>
+#include <dali-toolkit/internal/text/layouts/layout-parameters.h>
+#include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
 
 namespace Dali
 {
@@ -35,177 +35,593 @@ namespace Toolkit
 namespace Text
 {
 
+/**
+ * @brief Stores temporary layout info of the line.
+ */
+struct LineLayout
+{
+  GlyphIndex     glyphIndex;         ///< Index of the first glyph to be laid-out.
+  CharacterIndex characterIndex;     ///< Index of the first character to be laid-out.
+  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          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();
   }
 
-  void UpdateVisualModel( const Vector2& boundingBox,
-                          const Vector<GlyphInfo>& glyphs,
-                          const Vector<CharacterIndex>& characterIndices,
-                          const Vector<Length>& charactersPerGlyph,
-                          VisualModel& visualModel )
+  /**
+   * Retrieves the line layout for a given box width.
+   */
+  void GetLineLayoutForBox( const LayoutParameters& parameters,
+                            LineLayout& lineLayout )
   {
-    // TODO Switch between different layouts
+    // Initializes the line layout.
+    lineLayout.numberOfCharacters = 0u;
+    lineLayout.numberOfGlyphs = 0u;
+    lineLayout.length = 0.f;
+    lineLayout.wsLengthEndOfLine = 0.f;
+    lineLayout.ascender = 0.f;
+    lineLayout.descender = 0.f;
+
+    // Get the last glyph index.
+    const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
+
+    FontId lastFontId = 0u;
+    for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
+         glyphIndex < parameters.totalNumberOfGlyphs;
+         ++glyphIndex )
+    {
+      // Get the glyph info.
+      const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
 
-    visualModel.SetGlyphs( &glyphs[0],
-                           &characterIndices[0],
-                           &charactersPerGlyph[0],
-                           glyphs.Count() );
+      // 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.
+      const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
+
+      // Increase the number of characters.
+      lineLayout.numberOfCharacters += charactersPerGlyph;
+
+      // Increase the number of glyphs.
+      lineLayout.numberOfGlyphs++;
+
+      // Increase the accumulated length.
+      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 ascender.
+        if( fontMetrics.ascender > lineLayout.ascender )
+        {
+          lineLayout.ascender = fontMetrics.ascender;
+        }
+
+        // Sets the maximum descender.
+        if( fontMetrics.descender > lineLayout.descender )
+        {
+          lineLayout.descender = fontMetrics.descender;
+        }
 
-    UpdateGlyphPositions( boundingBox, visualModel );
+        lastFontId = glyphInfo.fontId;
+      }
+    }
   }
 
-  void UpdateGlyphPositions( const Vector2& boundingBox, VisualModel& visualModel )
+  /**
+   * Retrieves the line layout for a given box width.
+   */
+  void GetMultiLineLayoutForBox( const LayoutParameters& parameters,
+                                 LineLayout& lineLayout )
   {
-    if( LayoutEngine::SINGLE_LINE_BOX == mLayout )
+    // Initializes the line layout.
+    lineLayout.numberOfCharacters = 0u;
+    lineLayout.numberOfGlyphs = 0u;
+    lineLayout.length = 0.f;
+    lineLayout.widthAdvanceDiff = 0.f;
+    lineLayout.wsLengthEndOfLine = 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.ascender = 0.f;
+    tmpLineLayout.descender = 0.f;
+
+    FontId lastFontId = 0u;
+    for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
+         glyphIndex < parameters.totalNumberOfGlyphs;
+         ++glyphIndex )
     {
-      SingleLineLayout( boundingBox, visualModel );
-    }
-    else
-    {
-      MultiLineLayout( boundingBox, visualModel );
+      // Get the glyph info.
+      const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
+
+      // 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.
+      const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
+      const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
+      const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
+
+      // Get the line break info for the current character.
+      const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
+
+      // Get the word break info for the current character.
+      const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
+
+      // Increase the number of characters.
+      tmpLineLayout.numberOfCharacters += charactersPerGlyph;
+
+      // 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.
+      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 + tmpLineLayout.widthAdvanceDiff + ( ( 0.f < tmpLineLayout.length ) ? lineLayout.wsLengthEndOfLine : 0.f ) > parameters.boundingBox.width )
+      {
+        // Current word does not fit in the box's width.
+        return;
+      }
+
+      if( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo )
+      {
+        // 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 )
+        {
+          lineLayout.length += lineLayout.wsLengthEndOfLine;
+
+          lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
+        }
+        else
+        {
+          lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
+        }
+
+        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.ascender = 0.f;
+        tmpLineLayout.descender = 0.f;
+        return;
+      }
+
+      if( TextAbstraction::WORD_BREAK == wordBreakInfo )
+      {
+        // Current glyph is the last one of the current word.
+        // Add the temporal layout to the current one.
+        lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
+        lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
+        lineLayout.length += tmpLineLayout.length;
+        lineLayout.widthAdvanceDiff = tmpLineLayout.widthAdvanceDiff;
+
+        if( 0.f < tmpLineLayout.length )
+        {
+          lineLayout.length += lineLayout.wsLengthEndOfLine;
+
+          lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
+        }
+        else
+        {
+          lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
+        }
+
+        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.ascender = 0.f;
+        tmpLineLayout.descender = 0.f;
+      }
+
+      if( lastFontId != glyphInfo.fontId )
+      {
+        Text::FontMetrics fontMetrics;
+        mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
+
+        // 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;
+      }
     }
   }
 
-  // TODO - Rewrite this to handle bidi
-  void SingleLineLayout( const Vector2& boundingBox, VisualModel& visualModel )
+  bool LayoutText( const LayoutParameters& layoutParameters,
+                   Vector<Vector2>& glyphPositions,
+                   Vector<LineRun>& lines,
+                   Size& actualSize )
   {
-    Length glyphCount = visualModel.GetNumberOfGlyphs();
+    // TODO Switch between different layouts
+    bool update = false;
 
-    std::vector<Vector2> glyphPositions;
-    glyphPositions.reserve( glyphCount );
+    switch( mLayout )
+    {
+      case LayoutEngine::SINGLE_LINE_BOX:
+      {
+        update = SingleLineLayout( layoutParameters,
+                                   glyphPositions,
+                                   lines,
+                                   actualSize );
+        break;
+      }
+      case LayoutEngine::MULTI_LINE_BOX:
+      {
+        update = MultiLineLayout( layoutParameters,
+                                  glyphPositions,
+                                  lines,
+                                  actualSize );
+        break;
+      }
+      default:
+        break;
+    }
 
-    if( glyphCount > 0 )
+    return update;
+  }
+
+  void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
+                                 Vector<Vector2>& glyphPositions )
+  {
+    // Traverses the paragraphs with right to left characters.
+    for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
     {
-      // FIXME Single font assumption
-      Text::FontMetrics fontMetrics;
-      GlyphInfo firstGlyph;
-      visualModel.GetGlyphs( &firstGlyph, 0, 1 );
-      mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
+      const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
 
-      float penX( 0 );
-      float penY( fontMetrics.ascender ); // Move to baseline
+      float penX = 0.f;
 
-      for( unsigned int i=0; i<glyphCount; ++i )
+      Vector2* glyphPositionsBuffer = glyphPositions.Begin();
+
+      // Traverses the characters of the right to left paragraph.
+      for( CharacterIndex characterLogicalIndex = 0u;
+           characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
+           ++characterLogicalIndex )
       {
-        GlyphInfo glyph;
-        visualModel.GetGlyphs( &glyph, i, 1 );
+        // Convert the character in the logical order into the character in the visual order.
+        const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
 
-        glyphPositions.push_back( Vector2( penX + glyph.xBearing,
-                                           penY - glyph.yBearing ) );
+        // Get the number of glyphs of the character.
+        const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
 
-        penX += glyph.advance;
-      }
+        for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
+        {
+          // Convert the character in the visual order into the glyph in the visual order.
+          const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
+
+          DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
 
-      visualModel.SetGlyphPositions( &glyphPositions[0], glyphCount );
+          const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
+          Vector2& position = *( glyphPositionsBuffer + glyphIndex );
 
-      visualModel.SetActualSize( Vector2(penX, fontMetrics.height) );
+          position.x = penX + glyph.xBearing;
+          penX += glyph.advance;
+        }
+      }
     }
   }
 
-  // TODO - Rewrite this to handle bidi
-  void MultiLineLayout( const Vector2& boundingBox, VisualModel& visualModel )
+  void Align( const LayoutParameters& layoutParameters,
+              const Size& layoutSize,
+              const Vector<LineRun>& lines,
+              Vector<Vector2>& glyphPositions )
   {
-    Length glyphCount = visualModel.GetNumberOfGlyphs();
+    Vector2* glyphPositionsBuffer = glyphPositions.Begin();
 
-    std::vector<Vector2> glyphPositions;
-    glyphPositions.reserve( glyphCount );
+    // 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.
 
-    if( glyphCount > 0 )
+    LineIndex bidiLineIndex = 0u;
+    for( Vector<LineRun>::ConstIterator it = lines.Begin(), endIt = lines.End();
+         it != endIt;
+         ++it )
     {
-      Size actualSize;
-
-      // FIXME Single font assumption
-      Text::FontMetrics fontMetrics;
-      GlyphInfo firstGlyph;
-      visualModel.GetGlyphs( &firstGlyph, 0, 1 );
-      mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
+      const LineRun& line = *it;
 
-      float penX( 0 );
-      float penY( fontMetrics.ascender ); // Move to baseline
+      // 1) Get the paragrap's direction.
+      bool paragraphDirection = false;
 
-      unsigned int i=0;
-      while( i < glyphCount )
+      // Check if there is any right to left line.
+      if( ( NULL != layoutParameters.lineBidirectionalInfoRunsBuffer ) &&
+          ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
       {
-        // Skip initial whitespace
-        for( ; i<glyphCount; ++i )
+        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 ) )
         {
-          GlyphInfo glyph;
-          visualModel.GetGlyphs( &glyph, i, 1 );
-
-          if( glyph.width  > 0 &&
-              glyph.height > 0 )
-          {
-            break;
-          }
-          else
-          {
-            glyphPositions.push_back( Vector2( penX + glyph.xBearing,
-                                               penY - glyph.yBearing ) );
-          }
+          ++bidiLineIndex;
+          bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
         }
 
-        // Find last glyph for the next line
-        unsigned int endIndex = i;
-        float endPenX = penX;
-        unsigned int j=i;
-        for( ; j<glyphCount; ++j )
+        if( line.characterRun.characterIndex == bidiLine->characterRun.characterIndex )
         {
-          GlyphInfo glyph;
-          visualModel.GetGlyphs( &glyph, j, 1 );
-
-          endPenX += glyph.advance;
-
-          if( glyph.width  <= 0 ||
-              glyph.height <= 0 )
-          {
-            // Potential line end found
-            endIndex = j;
-          }
-          else if( endPenX > boundingBox.width )
-          {
-            break;
-          }
+          paragraphDirection = bidiLine->direction;
         }
+      }
 
-        actualSize.width = ( actualSize.width < endPenX ) ? endPenX : actualSize.width;
+      // 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 );
 
-        // If end of text or no whitespace found
-        if( glyphCount == j ||
-            endIndex == i )
-        {
-          endIndex = j;
-        }
+        position.x += alignOffset;
+      }
+    }
+  }
 
-        for( ; i<endIndex; ++i )
-        {
-          GlyphInfo glyph;
-          visualModel.GetGlyphs( &glyph, i, 1 );
+  bool SingleLineLayout( const LayoutParameters& layoutParameters,
+                         Vector<Vector2>& glyphPositions,
+                         Vector<LineRun>& lines,
+                         Size& actualSize )
+  {
+    LineLayout layout;
+    layout.glyphIndex = 0u;
+    GetLineLayoutForBox( layoutParameters,
+                         layout );
+
+    // Create a line run and add it to the lines.
+    const GlyphIndex lastGlyphIndex = layoutParameters.totalNumberOfGlyphs - 1u;
+
+    LineRun lineRun;
+    lineRun.glyphIndex = 0u;
+    lineRun.numberOfGlyphs = layoutParameters.totalNumberOfGlyphs;
+    lineRun.characterRun.characterIndex = 0u;
+    lineRun.characterRun.numberOfCharacters = *( layoutParameters.glyphsToCharactersBuffer + lastGlyphIndex ) + *( layoutParameters.charactersPerGlyphBuffer + lastGlyphIndex );
+    lineRun.lineSize.width = layout.length;
+    lineRun.lineSize.height = layout.ascender + layout.descender;
+    lineRun.extraLength = layout.wsLengthEndOfLine;
+
+    lines.PushBack( lineRun );
+
+    // Update the actual size.
+    actualSize.width = layout.length;
+    actualSize.height = lineRun.lineSize.height;
+
+    float penX = 0.f;
+    float penY = layout.ascender;
+
+    Vector2* glyphPositionsBuffer = glyphPositions.Begin();
+    for( GlyphIndex glyphIndex = 0u; glyphIndex < layout.numberOfGlyphs; ++glyphIndex )
+    {
+      const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
+      Vector2& position = *( glyphPositionsBuffer + glyphIndex );
 
-          glyphPositions.push_back( Vector2( penX + glyph.xBearing,
-                                             penY - glyph.yBearing ) );
+      position.x = penX + glyph.xBearing;
+      position.y = penY - glyph.yBearing;
 
-          penX += glyph.advance;
-        }
+      penX += glyph.advance;
+    }
+
+    return true;
+  }
+
+  bool MultiLineLayout( const LayoutParameters& layoutParameters,
+                        Vector<Vector2>& glyphPositions,
+                        Vector<LineRun>& lines,
+                        Size& actualSize )
+  {
+    float penY = 0.f;
+    for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
+    {
+      float penX = 0.f;
+
+      // Get the layout for the line.
+      LineLayout layout;
+      layout.glyphIndex = index;
+      GetMultiLineLayoutForBox( layoutParameters,
+                                layout );
+
+      if( 0u == layout.numberOfGlyphs )
+      {
+        // The width is too small and no characters are laid-out.
+        return false;
+      }
+
+      // Create a line run and add it to the lines.
+      const GlyphIndex lastGlyphIndex = index + layout.numberOfGlyphs - 1u;
+
+      LineRun lineRun;
+      lineRun.glyphIndex = index;
+      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 + ( ( 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 + layout.widthAdvanceDiff > actualSize.width )
+      {
+        actualSize.width = layout.length;
+      }
+
+      actualSize.height += lineRun.lineSize.height;
+
+      // Traverse the glyphs and set the positions.
+
+      penY += layout.ascender;
+
+      Vector2* glyphPositionsBuffer = glyphPositions.Begin();
+      for( GlyphIndex i = index; i < index + layout.numberOfGlyphs; ++i )
+      {
+        const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + i );
+        Vector2& position = *( glyphPositionsBuffer + i );
 
-        // Go to next line
-        penX = 0;
-        penY += fontMetrics.height;
+        position.x = penX + glyph.xBearing;
+        position.y = penY - glyph.yBearing;
 
-        actualSize.height += fontMetrics.height;
+        penX += glyph.advance;
       }
 
-      visualModel.SetGlyphPositions( &glyphPositions[0], glyphCount );
+      penY += layout.descender;
 
-      visualModel.SetActualSize( actualSize );
+      // Increase the glyph index.
+      index += layout.numberOfGlyphs;
     }
+
+    return true;
   }
 
-  unsigned int mLayout;
+  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;
 };
@@ -231,17 +647,53 @@ unsigned int LayoutEngine::GetLayout() const
   return mImpl->mLayout;
 }
 
-void LayoutEngine::UpdateVisualModel( const Vector2& boundingBox,
-                                      const Vector<GlyphInfo>& glyphs,
-                                      const Vector<CharacterIndex>& characterIndices,
-                                      const Vector<Length>& charactersPerGlyph,
-                                      VisualModel& visualModel )
-{
-  mImpl->UpdateVisualModel( boundingBox,
-                            glyphs,
-                            characterIndices,
-                            charactersPerGlyph,
-                            visualModel );
+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,
+                               Size& actualSize )
+{
+  return mImpl->LayoutText( layoutParameters,
+                            glyphPositions,
+                            lines,
+                            actualSize );
+}
+
+void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
+                                             Vector<Vector2>& glyphPositions )
+{
+  mImpl->ReLayoutRightToLeftLines( layoutParameters,
+                                   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